diff --git a/TMessagesProj/build.gradle b/TMessagesProj/build.gradle
index 22d52f6fc..869bae73d 100644
--- a/TMessagesProj/build.gradle
+++ b/TMessagesProj/build.gradle
@@ -3,7 +3,7 @@ buildscript {
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:0.7.+'
+ classpath 'com.android.tools.build:gradle:0.8.+'
}
}
apply plugin: 'android'
@@ -31,7 +31,7 @@ tasks.withType(JavaCompile) {
}
dependencies {
- compile 'com.google.android.gms:play-services:4.0.+'
+ compile 'com.google.android.gms:play-services:4.1.+'
compile 'net.hockeyapp.android:HockeySDK:3.0.1'
compile 'com.android.support:support-v4:19.0.+'
compile 'com.android.support:appcompat-v7:19.0.+'
@@ -40,7 +40,7 @@ dependencies {
android {
compileSdkVersion 19
- buildToolsVersion '19.0.0'
+ buildToolsVersion '19.0.1'
signingConfigs {
debug {
diff --git a/TMessagesProj/src/main/AndroidManifest.xml b/TMessagesProj/src/main/AndroidManifest.xml
index 3b163ccc0..12755bb70 100644
--- a/TMessagesProj/src/main/AndroidManifest.xml
+++ b/TMessagesProj/src/main/AndroidManifest.xml
@@ -1,18 +1,18 @@
-
+ android:versionCode="148"
+ android:versionName="1.3.19">
+
-
+ android:smallScreens="true"
+ android:normalScreens="true"
+ android:largeScreens="true"
+ android:resizeable="true"
+ android:xlargeScreens="true"/>
+
-
+
@@ -23,7 +23,7 @@
-
+
@@ -49,10 +49,10 @@
-
+
-
+
-
+
-
+
+
+ android:name="org.telegram.ui.LaunchActivity"
+ android:windowSoftInputMode="adjustResize"
+ android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
@@ -124,48 +125,47 @@
android:theme="@style/Theme.TMessages.Gallery"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
-
-
+
+
-
+
-
+
+ android:name="org.telegram.messenger.GcmBroadcastReceiver"
+ android:permission="com.google.android.c2dm.permission.SEND" >
-
+
+ android:resource="@xml/auth"/>
-
+
+ android:resource="@xml/sync_contacts" />
+ android:resource="@xml/contacts" />
-
+
-
-
+
diff --git a/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java b/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java
index 6f92a935b..8d4ced3b0 100644
--- a/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java
+++ b/TMessagesProj/src/main/java/org/telegram/TL/TLObject.java
@@ -32,7 +32,7 @@ public class TLObject {
}
public int layer () {
- return 8;
+ return 11;
}
public void parseVector(TLRPC.Vector vector, SerializedData data) {
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java
index c0c176695..d9007a68f 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/ConnectionsManager.java
@@ -15,6 +15,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
import android.util.Base64;
+import android.util.Log;
import org.telegram.TL.TLClassStore;
import org.telegram.TL.TLObject;
@@ -33,7 +34,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.TcpConnectionDelegate {
- public static boolean DEBUG_VERSION = false;
+ public static boolean DEBUG_VERSION = true;
public static int APP_ID = 2458;
public static String APP_HASH = "5bce48dc7d331e62c955669eb7233217";
public static String HOCKEY_APP_HASH = "your-hockeyapp-api-key-here";
@@ -100,35 +101,55 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
public void run() {
long currentTime = System.currentTimeMillis();
if (ApplicationLoader.lastPauseTime != 0 && ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout) {
- if (!paused) {
- FileLog.e("tmessages", "pausing network and timers by sleep time = " + nextSleepTimeout);
- for (Datacenter datacenter : datacenters.values()) {
- if (datacenter.connection != null) {
- datacenter.connection.suspendConnection(true);
- }
- if (datacenter.uploadConnection != null) {
- datacenter.uploadConnection.suspendConnection(true);
- }
- if (datacenter.downloadConnection != null) {
- datacenter.downloadConnection.suspendConnection(true);
+ boolean dontSleep = false;
+ for (RPCRequest request : runningRequests) {
+ if (request.retryCount < 10 && (request.runningStartTime + 60 > (int)(currentTime / 1000)) && ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0)) {
+ dontSleep = true;
+ break;
+ }
+ }
+ if (!dontSleep) {
+ for (RPCRequest request : requestQueue) {
+ if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) {
+ dontSleep = true;
+ break;
}
}
}
- try {
- paused = true;
- if (ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout - nextWakeUpTimeout) {
- ApplicationLoader.lastPauseTime = currentTime;
- nextSleepTimeout = 30000;
- FileLog.e("tmessages", "wakeup network in background by wakeup time = " + nextWakeUpTimeout);
- if (nextWakeUpTimeout < 30 * 60 * 1000) {
- nextWakeUpTimeout *= 2;
+ if (!dontSleep) {
+ if (!paused) {
+ FileLog.e("tmessages", "pausing network and timers by sleep time = " + nextSleepTimeout);
+ for (Datacenter datacenter : datacenters.values()) {
+ if (datacenter.connection != null) {
+ datacenter.connection.suspendConnection(true);
+ }
+ if (datacenter.uploadConnection != null) {
+ datacenter.uploadConnection.suspendConnection(true);
+ }
+ if (datacenter.downloadConnection != null) {
+ datacenter.downloadConnection.suspendConnection(true);
+ }
}
- } else {
- Thread.sleep(500);
- return;
}
- } catch (Exception e) {
- FileLog.e("tmessages", e);
+ try {
+ paused = true;
+ if (ApplicationLoader.lastPauseTime < currentTime - nextSleepTimeout - nextWakeUpTimeout) {
+ ApplicationLoader.lastPauseTime = currentTime;
+ nextSleepTimeout = 30000;
+ FileLog.e("tmessages", "wakeup network in background by wakeup time = " + nextWakeUpTimeout);
+ if (nextWakeUpTimeout < 30 * 60 * 1000) {
+ nextWakeUpTimeout *= 2;
+ }
+ } else {
+ Thread.sleep(500);
+ return;
+ }
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ } else {
+ ApplicationLoader.lastPauseTime += 30 * 1000;
+ FileLog.e("tmessages", "don't sleep 30 seconds because of upload or download request");
}
}
if (paused) {
@@ -240,8 +261,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
if (datacenterSetId == useDifferentBackend) {
currentDatacenterId = preferences.getInt("currentDatacenterId", 0);
timeDifference = preferences.getInt("timeDifference", 0);
- lastDcUpdateTime = preferences.getInt("lastDcUpdateTime", 0); //TODO uncomment
- //lastDcUpdateTime = 0;
+ lastDcUpdateTime = preferences.getInt("lastDcUpdateTime", 0);
try {
sessionsToDestroy.clear();
String sessionsString = preferences.getString("sessionsToDestroy", null);
@@ -635,7 +655,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
TLObject wrapInLayer(TLObject object, int datacenterId, RPCRequest request) {
if (object.layer() > 0) {
Datacenter datacenter = datacenterWithId(datacenterId);
- if (datacenter.lastInitVersion != currentAppVersion) {
+ if (datacenter == null || datacenter.lastInitVersion != currentAppVersion) {
request.initRequest = true;
TLRPC.initConnection invoke = new TLRPC.initConnection();
invoke.query = object;
@@ -704,6 +724,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
requestQueue.add(request);
+ if (paused && ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0 || (request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0)) {
+ ApplicationLoader.lastPauseTime = System.currentTimeMillis();
+ nextSleepTimeout = 30000;
+ FileLog.e("tmessages", "wakeup by download or upload request");
+ }
+
processRequestQueue(0, 0);
}
});
@@ -941,6 +967,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
Integer tokenIt = activeTransportTokens.get(requestDatacenter.datacenterId);
int datacenterTransportToken = tokenIt != null ? tokenIt : 0;
+ Integer uploadTokenIt = activeUploadTransportTokens.get(requestDatacenter.datacenterId);
+ int datacenterUploadTransportToken = uploadTokenIt != null ? uploadTokenIt : 0;
+
+ Integer downloadTokenIt = activeDownloadTransportTokens.get(requestDatacenter.datacenterId);
+ int datacenterDownloadTransportToken = downloadTokenIt != null ? downloadTokenIt : 0;
+
double maxTimeout = 8.0;
if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) {
@@ -952,12 +984,18 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
FileLog.d("tmessages", "Don't have any network connection, skipping download request");
continue;
}
+ if (datacenterDownloadTransportToken == 0) {
+ continue;
+ }
maxTimeout = 40.0;
} else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) {
if (!haveNetwork) {
FileLog.d("tmessages", "Don't have any network connection, skipping upload request");
continue;
}
+ if (datacenterUploadTransportToken == 0) {
+ continue;
+ }
maxTimeout = 30.0;
}
@@ -988,20 +1026,19 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
FileLog.d("tmessages", "Request token is valid, not retrying " + request.rawRequest);
continue;
} else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
- int downloadToken = requestDatacenter.downloadConnection.channelToken;
- if (downloadToken != 0 && request.transportChannelToken == downloadToken) {
+ if (datacenterDownloadTransportToken != 0 && request.transportChannelToken == datacenterDownloadTransportToken) {
FileLog.d("tmessages", "Request download token is valid, not retrying " + request.rawRequest);
continue;
}
} else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) {
- int uploadToken = requestDatacenter.uploadConnection.channelToken;
- if (uploadToken != 0 && request.transportChannelToken == uploadToken) {
+ if (datacenterUploadTransportToken != 0 && request.transportChannelToken == datacenterUploadTransportToken) {
FileLog.d("tmessages", "Request upload token is valid, not retrying " + request.rawRequest);
continue;
}
}
}
+ request.retryCount++;
NetworkMessage networkMessage = new NetworkMessage();
networkMessage.protoMessage = new TLRPC.TL_protoMessage();
@@ -1022,10 +1059,12 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
request.transportChannelToken = datacenterTransportToken;
addMessageToDatacenter(genericMessagesToDatacenters, requestDatacenter.datacenterId, networkMessage);
} else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
+ request.transportChannelToken = datacenterDownloadTransportToken;
ArrayList arr = new ArrayList();
arr.add(networkMessage);
proceedToSendingMessages(arr, sessionId, requestDatacenter.downloadConnection, false, false);
} else if ((request.flags & RPCRequest.RPCRequestClassUploadMedia) != 0) {
+ request.transportChannelToken = datacenterUploadTransportToken;
ArrayList arr = new ArrayList();
arr.add(networkMessage);
proceedToSendingMessages(arr, sessionId, requestDatacenter.uploadConnection, false, false);
@@ -1147,6 +1186,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
continue;
}
+ Integer uploadTokenIt = activeUploadTransportTokens.get(requestDatacenter.datacenterId);
+ request.transportChannelToken = uploadTokenIt != null ? uploadTokenIt : 0;
+
uploadRunningRequestCount++;
} else if ((request.flags & RPCRequest.RPCRequestClassDownloadMedia) != 0) {
if (!haveNetwork) {
@@ -1158,6 +1200,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
continue;
}
+ Integer downloadTokenIt = activeDownloadTransportTokens.get(requestDatacenter.datacenterId);
+ request.transportChannelToken = downloadTokenIt != null ? downloadTokenIt : 0;
+
downloadRunningRequestCount++;
}
}
@@ -1166,8 +1211,9 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
SerializedData os = new SerializedData();
request.rpcRequest.serializeToStream(os);
+ int requestLength = os.length();
- if (os.length() != 0) {
+ if (requestLength != 0) {
long sessionId = 0;
if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) {
sessionId = requestDatacenter.authSessionId;
@@ -1177,18 +1223,31 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
sessionId = requestDatacenter.authUploadSessionId;
}
+ if ((request.flags & RPCRequest.RPCRequestClassCanCompress) != 0) {
+ try {
+ byte[] data = Utilities.compress(os.toByteArray());
+ if (data.length < requestLength) {
+ TLRPC.TL_gzip_packed packed = new TLRPC.TL_gzip_packed();
+ packed.packed_data = data;
+ request.rpcRequest = packed;
+ }
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ }
+
NetworkMessage networkMessage = new NetworkMessage();
networkMessage.protoMessage = new TLRPC.TL_protoMessage();
networkMessage.protoMessage.msg_id = messageId;
networkMessage.protoMessage.seqno = generateMessageSeqNo(sessionId, true);
- networkMessage.protoMessage.bytes = os.length();
+ networkMessage.protoMessage.bytes = requestLength;
networkMessage.protoMessage.body = request.rpcRequest;
networkMessage.rawRequest = request.rawRequest;
networkMessage.requestId = request.token;
request.runningMessageId = messageId;
request.runningMessageSeqNo = networkMessage.protoMessage.seqno;
- request.serializedLength = os.length();
+ request.serializedLength = requestLength;
request.runningStartTime = (int)(System.currentTimeMillis() / 1000);
if (request.requiresCompletion) {
runningRequests.add(request);
@@ -1454,8 +1513,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
byte[] transportData = createConnectionData(currentMessages, sessionId, quickAckId, connection);
if (transportData != null) {
- if (reportAck && quickAckId.size() != 0)
- {
+ if (reportAck && quickAckId.size() != 0) {
ArrayList requestIds = new ArrayList();
for (NetworkMessage message : messagesToSend) {
@@ -1933,13 +1991,15 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
isError = true;
if (datacenter.datacenterId == currentDatacenterId || datacenter.datacenterId == movingToDatacenterId) {
if ((request.flags & RPCRequest.RPCRequestClassGeneric) != 0) {
- Utilities.RunOnUIThread(new Runnable() {
- @Override
- public void run() {
- NotificationCenter.Instance.postNotificationName(1234);
- UserConfig.clearConfig();
- }
- });
+ if (UserConfig.clientActivated) {
+ UserConfig.clearConfig();
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ NotificationCenter.Instance.postNotificationName(1234);
+ }
+ });
+ }
}
} else {
datacenter.authorized = false;
@@ -2078,7 +2138,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
TLObject result = Utilities.decompress(packet.packed_data, getRequestWithMessageId(messageId));
processMessage(result, messageId, messageSeqNo, messageSalt, connection, sessionId, innerMsgId, containerMessageId);
} else if (message instanceof TLRPC.Updates) {
- MessagesController.Instance.processUpdates((TLRPC.Updates)message);
+ MessagesController.Instance.processUpdates((TLRPC.Updates)message, false);
} else {
FileLog.e("tmessages", "***** Error: unknown message class " + message);
}
@@ -2275,6 +2335,20 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
} else {
connectionState = 1;
}
+ if (DEBUG_VERSION) {
+ try {
+ ConnectivityManager cm = (ConnectivityManager)ApplicationLoader.applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo[] networkInfos = cm.getAllNetworkInfo();
+ for (NetworkInfo info : networkInfos) {
+ FileLog.e("tmessages", "Network: " + info.getTypeName() + " status: " + info.getState() + " info: " + info.getExtraInfo() + " object: " + info.getDetailedState() + " other: " + info);
+ }
+ if (networkInfos.length == 0) {
+ FileLog.e("tmessages", "no network available");
+ }
+ } catch (Exception e) {
+ FileLog.e("tmessages", "NETWORK STATE GET ERROR");
+ }
+ }
final int stateCopy = connectionState;
Utilities.RunOnUIThread(new Runnable() {
@Override
@@ -2477,7 +2551,7 @@ public class ConnectionsManager implements Action.ActionDelegate, TcpConnection.
public void run() {
moveToDatacenter(datacenterId);
}
- }, 1000);
+ }, 1000, false);
}
}
}, null, true, RPCRequest.RPCRequestClassGeneric, currentDatacenterId);
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java
new file mode 100644
index 000000000..cd6a66eca
--- /dev/null
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/ContactsController.java
@@ -0,0 +1,1310 @@
+/*
+ * This is the source code of Telegram for Android v. 1.3.x.
+ * It is licensed under GNU GPL v. 2 or later.
+ * You should have received a copy of the license in this archive (see LICENSE).
+ *
+ * Copyright Nikolai Kudashov, 2013.
+ */
+
+package org.telegram.messenger;
+
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract;
+import android.util.SparseArray;
+
+import org.telegram.PhoneFormat.PhoneFormat;
+import org.telegram.TL.TLObject;
+import org.telegram.TL.TLRPC;
+import org.telegram.ui.ApplicationLoader;
+
+import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class ContactsController {
+ public static ContactsController Instance = new ContactsController();
+ private Account currentAccount;
+ public boolean loadingContacts = false;
+ private boolean ignoreChanges = false;
+ private boolean contactsSyncInProgress = false;
+ private final Integer observerLock = 1;
+ private HashMap delayedDontactsToDelete = null;
+ public boolean contactsLoaded = false;
+ private boolean contactsBookLoaded = false;
+ private ArrayList delayedContactsUpdate = new ArrayList();
+
+ public static class Contact {
+ public int id;
+ public ArrayList phones = new ArrayList();
+ public ArrayList phoneTypes = new ArrayList();
+ public ArrayList shortPhones = new ArrayList();
+ public ArrayList phoneDeleted = new ArrayList();
+ public String first_name;
+ public String last_name;
+ }
+
+ private String[] projectioPhones = {
+ ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ ContactsContract.CommonDataKinds.Phone.TYPE,
+ ContactsContract.CommonDataKinds.Phone.LABEL
+ };
+ private String[] projectionNames = {
+ ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID,
+ ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
+ ContactsContract.Data.DISPLAY_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME
+ };
+
+ public HashMap contactsBook = new HashMap();
+ public HashMap contactsBookSPhones = new HashMap();
+ public HashMap> contactsSectionsDict = new HashMap>();
+ public ArrayList sortedContactsSectionsArray = new ArrayList();
+
+ public ArrayList contacts = new ArrayList();
+ public SparseArray contactsDict = new SparseArray();
+ public HashMap> usersSectionsDict = new HashMap>();
+ public ArrayList sortedUsersSectionsArray = new ArrayList();
+
+ private class MyContentObserver extends ContentObserver {
+
+ public MyContentObserver() {
+ super(null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+ synchronized (observerLock) {
+ if (ignoreChanges) {
+ FileLog.e("tmessages", "contacts changed - ignore");
+ return;
+ }
+ }
+
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ MessagesController.Instance.scheduleContactsReload = System.currentTimeMillis() + 2000;
+ FileLog.e("tmessages", "contacts changed schedule - apply in " + MessagesController.Instance.scheduleContactsReload);
+ }
+ });
+ }
+
+ @Override
+ public boolean deliverSelfNotifications() {
+ return false;
+ }
+ }
+
+ public ContactsController() {
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ ApplicationLoader.applicationContext.getContentResolver().registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, new MyContentObserver());
+ }
+ });
+ }
+
+ public void cleanup() {
+ contactsBook.clear();
+ contactsBookSPhones.clear();
+ contactsSectionsDict.clear();
+ sortedContactsSectionsArray.clear();
+ contacts.clear();
+ contactsDict.clear();
+ usersSectionsDict.clear();
+ sortedUsersSectionsArray.clear();
+ delayedContactsUpdate.clear();
+
+ delayedDontactsToDelete = null;
+ loadingContacts = false;
+ contactsSyncInProgress = false;
+ contactsLoaded = false;
+ contactsBookLoaded = false;
+ }
+
+ public void checkAppAccount() {
+ AccountManager am = AccountManager.get(ApplicationLoader.applicationContext);
+ Account[] accounts = am.getAccountsByType("org.telegram.messenger.account");
+ boolean recreateAccount = false;
+ if (UserConfig.currentUser != null) {
+ if (accounts.length == 1) {
+ Account acc = accounts[0];
+ if (!acc.name.equals(UserConfig.currentUser.phone)) {
+ recreateAccount = true;
+ } else {
+ currentAccount = acc;
+ }
+ } else {
+ recreateAccount = true;
+ }
+ readContacts();
+ } else {
+ if (accounts.length > 0) {
+ recreateAccount = true;
+ }
+ }
+ if (recreateAccount) {
+ for (Account c : accounts) {
+ am.removeAccount(c, null, null);
+ }
+ if (UserConfig.currentUser != null) {
+ currentAccount = new Account(UserConfig.currentUser.phone, "org.telegram.messenger.account");
+ am.addAccountExplicitly(currentAccount, "", null);
+ }
+ }
+ }
+
+ public void readContacts() {
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ if (contactsBook.size() != 0 || contactsSyncInProgress) {
+ return;
+ }
+ contactsSyncInProgress = true;
+ MessagesStorage.Instance.getCachedPhoneBook();
+ }
+ });
+ }
+
+ private HashMap readContactsFromPhoneBook() {
+ HashMap contactsMap = new HashMap();
+ ContentResolver cr = ApplicationLoader.applicationContext.getContentResolver();
+ String ids = "";
+ Cursor pCur = cr.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, projectioPhones, null, null, null);
+ if (pCur != null) {
+ if (pCur.getCount() > 0) {
+ while (pCur.moveToNext()) {
+ String number = pCur.getString(1);
+ if (number == null || number.length() == 0) {
+ continue;
+ }
+ number = PhoneFormat.stripExceptNumbers(number);
+ if (number.length() == 0) {
+ continue;
+ }
+ Integer id = pCur.getInt(0);
+ if (ids.length() != 0) {
+ ids += ",";
+ }
+ ids += id;
+
+ int type = pCur.getInt(2);
+ Contact contact = contactsMap.get(id);
+ if (contact == null) {
+ contact = new Contact();
+ contact.first_name = "";
+ contact.last_name = "";
+ contact.id = id;
+ contactsMap.put(id, contact);
+ }
+
+ boolean addNumber = true;
+ if (number.length() > 8) {
+ String shortNumber = number.substring(number.length() - 8);
+ if (contact.shortPhones.contains(shortNumber)) {
+ addNumber = false;
+ } else {
+ contact.shortPhones.add(shortNumber);
+ }
+ } else {
+ if (contact.shortPhones.contains(number)) {
+ addNumber = false;
+ } else {
+ contact.shortPhones.add(number);
+ }
+ }
+ if (addNumber) {
+ contact.phones.add(number);
+ contact.phoneDeleted.add(0);
+ }
+
+ if (type == ContactsContract.CommonDataKinds.Phone.TYPE_CUSTOM) {
+ contact.phoneTypes.add(pCur.getString(3));
+ } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_HOME) {
+ contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneHome));
+ } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE) {
+ contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMobile));
+ } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_WORK) {
+ contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneWork));
+ } else if (type == ContactsContract.CommonDataKinds.Phone.TYPE_MAIN) {
+ contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneMain));
+ } else {
+ contact.phoneTypes.add(ApplicationLoader.applicationContext.getString(R.string.PhoneOther));
+ }
+ }
+ }
+ pCur.close();
+ }
+
+ pCur = cr.query(ContactsContract.Data.CONTENT_URI, projectionNames, ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " IN (" + ids + ") AND " + ContactsContract.Data.MIMETYPE + " = '" + ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE + "'", null, null);
+ if (pCur != null && pCur.getCount() > 0) {
+ while (pCur.moveToNext()) {
+ int id = pCur.getInt(0);
+ String fname = pCur.getString(1);
+ String sname = pCur.getString(2);
+ String sname2 = pCur.getString(3);
+ String mname = pCur.getString(4);
+ Contact contact = contactsMap.get(id);
+ if (contact != null) {
+ contact.first_name = fname;
+ contact.last_name = sname;
+ if (contact.first_name == null) {
+ contact.first_name = "";
+ }
+ if (mname != null && mname.length() != 0) {
+ if (contact.first_name.length() != 0) {
+ contact.first_name += " " + mname;
+ } else {
+ contact.first_name = mname;
+ }
+ }
+ if (contact.last_name == null) {
+ contact.last_name = "";
+ }
+ if (contact.last_name.length() == 0 && contact.first_name.length() == 0 && sname2 != null && sname2.length() != 0) {
+ contact.first_name = sname2;
+ }
+ }
+ }
+ pCur.close();
+ }
+ return contactsMap;
+ }
+
+ public HashMap getContactsCopy(HashMap original) {
+ HashMap ret = new HashMap();
+ for (HashMap.Entry entry : original.entrySet()) {
+ Contact copyContact = new Contact();
+ Contact originalContact = entry.getValue();
+ copyContact.phoneDeleted.addAll(originalContact.phoneDeleted);
+ copyContact.phones.addAll(originalContact.phones);
+ copyContact.phoneTypes.addAll(originalContact.phoneTypes);
+ copyContact.shortPhones.addAll(originalContact.shortPhones);
+ copyContact.first_name = originalContact.first_name;
+ copyContact.last_name = originalContact.last_name;
+ copyContact.id = originalContact.id;
+ ret.put(copyContact.id, copyContact);
+ }
+ return ret;
+ }
+
+ public void performSyncPhoneBook(final HashMap contactHashMap, final boolean request, final boolean first) {
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ FileLog.e("tmessages", "start read contacts from phone");
+ final HashMap contactsMap = readContactsFromPhoneBook();
+ final HashMap contactsBookShort = new HashMap();
+ int oldCount = contactHashMap.size();
+
+ ArrayList toImport = new ArrayList();
+ if (!contactHashMap.isEmpty()) {
+ HashMap contactsMapCopy = new HashMap(contactsMap);
+ for (HashMap.Entry pair : contactsMap.entrySet()) {
+ Integer id = pair.getKey();
+ Contact value = pair.getValue();
+ Contact existing = contactHashMap.get(id);
+
+ if (existing == null || existing != null && (!existing.first_name.equals(value.first_name) || !existing.last_name.equals(value.last_name))) {
+ for (int a = 0; a < value.phones.size(); a++) {
+ String sphone = value.shortPhones.get(a);
+ contactsBookShort.put(sphone, value);
+ if (existing != null) {
+ int index = existing.shortPhones.indexOf(sphone);
+ if (index != -1) {
+ Integer deleted = existing.phoneDeleted.get(index);
+ value.phoneDeleted.set(a, deleted);
+ if (deleted == 1) {
+ continue;
+ }
+ }
+ }
+ if (request) {
+ TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
+ imp.client_id = id;
+ imp.first_name = value.first_name;
+ imp.last_name = value.last_name;
+ imp.phone = value.phones.get(a);
+ toImport.add(imp);
+ }
+ }
+ if (existing != null) {
+ contactHashMap.remove(id);
+ }
+ } else {
+ for (int a = 0; a < value.phones.size(); a++) {
+ String sphone = value.shortPhones.get(a);
+ contactsBookShort.put(sphone, value);
+ int index = existing.shortPhones.indexOf(sphone);
+ if (index == -1) {
+ if (request) {
+ TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
+ imp.client_id = id;
+ imp.first_name = value.first_name;
+ imp.last_name = value.last_name;
+ imp.phone = value.phones.get(a);
+ toImport.add(imp);
+ }
+ } else {
+ value.phoneDeleted.set(a, existing.phoneDeleted.get(index));
+ existing.phones.remove(index);
+ existing.shortPhones.remove(index);
+ existing.phoneDeleted.remove(index);
+ }
+ }
+ if (existing.phones.isEmpty()) {
+ contactHashMap.remove(id);
+ }
+ }
+ }
+ if (!first && contactHashMap.isEmpty() && toImport.isEmpty() && oldCount == contactsMap.size()) {
+ FileLog.e("tmessages", "contacts not changed!");
+ return;
+ }
+ if (request && !contactHashMap.isEmpty() && !contactsMap.isEmpty()) {
+ if (toImport.isEmpty()) {
+ MessagesStorage.Instance.putCachedPhoneBook(contactsMap);
+ }
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ delayedDontactsToDelete = contactHashMap;
+ FileLog.e("tmessages", "need delete contacts");
+ }
+ });
+ }
+ } else if (request) {
+ for (HashMap.Entry pair : contactsMap.entrySet()) {
+ Contact value = pair.getValue();
+ int id = pair.getKey();
+ for (int a = 0; a < value.phones.size(); a++) {
+ TLRPC.TL_inputPhoneContact imp = new TLRPC.TL_inputPhoneContact();
+ imp.client_id = id;
+ imp.first_name = value.first_name;
+ imp.last_name = value.last_name;
+ imp.phone = value.phones.get(a);
+ toImport.add(imp);
+ }
+ }
+ }
+
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ contactsBookSPhones = contactsBookShort;
+ contactsBook = contactsMap;
+ contactsSyncInProgress = false;
+ contactsBookLoaded = true;
+ }
+ });
+
+ FileLog.e("tmessages", "done procrssing contacts");
+
+ if (request) {
+ if (!toImport.isEmpty()) {
+ FileLog.e("tmessages", "start import contacts");
+ TLRPC.TL_contacts_importContacts req = new TLRPC.TL_contacts_importContacts();
+ req.contacts = toImport;
+ req.replace = false;
+ ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() {
+ @Override
+ public void run(TLObject response, TLRPC.TL_error error) {
+ if (error == null) {
+ FileLog.e("tmessages", "contacts imported");
+ MessagesStorage.Instance.putCachedPhoneBook(contactsMap);
+ TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
+ MessagesStorage.Instance.putUsersAndChats(res.users, null, true, true);
+ ArrayList cArr = new ArrayList();
+ for (TLRPC.TL_importedContact c : res.imported) {
+ TLRPC.TL_contact contact = new TLRPC.TL_contact();
+ contact.user_id = c.user_id;
+ cArr.add(contact);
+ }
+ processLoadedContacts(cArr, res.users, 2);
+ } else {
+ FileLog.e("tmessages", "import contacts error " + error.text);
+ }
+ }
+ }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors | RPCRequest.RPCRequestClassCanCompress);
+ } else if (first) {
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ loadContacts(true);
+ }
+ });
+ } else {
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ updateUnregisteredContacts(contacts);
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+ ArrayList toDelete = getContactsToDelete(contacts, MessagesController.Instance.users, 0);
+ if (!toDelete.isEmpty()) {
+ deleteContact(toDelete);
+ }
+ }
+ });
+ }
+ } else {
+ MessagesStorage.Instance.putCachedPhoneBook(contactsMap);
+ }
+ }
+ });
+ }
+
+ public void loadContacts(boolean fromCache) {
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ loadingContacts = true;
+ }
+ });
+ if (fromCache) {
+ FileLog.e("tmessages", "load contacts from cache");
+ MessagesStorage.Instance.getContacts();
+ } else {
+ FileLog.e("tmessages", "load contacts from server");
+ TLRPC.TL_contacts_getContacts req = new TLRPC.TL_contacts_getContacts();
+ req.hash = UserConfig.contactsHash;
+ ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() {
+ @Override
+ public void run(TLObject response, TLRPC.TL_error error) {
+ if (error == null) {
+ TLRPC.contacts_Contacts res = (TLRPC.contacts_Contacts)response;
+ if (res instanceof TLRPC.TL_contacts_contactsNotModified) {
+ delayedDontactsToDelete = null;
+ contactsLoaded = true;
+ if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) {
+ applyContactsUpdates(delayedContactsUpdate, null, null, null);
+ delayedContactsUpdate.clear();
+ }
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ loadingContacts = false;
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+ }
+ });
+ return;
+ }
+ processLoadedContacts(res.contacts, res.users, 0);
+ }
+ }
+ }, null, true, RPCRequest.RPCRequestClassGeneric);
+ }
+ }
+
+ private ArrayList getContactsToDelete(ArrayList contactsArr, AbstractMap usersDict, int from) {
+ final ArrayList toDelete = new ArrayList();
+ if (delayedDontactsToDelete != null && !delayedDontactsToDelete.isEmpty()) {
+ try {
+ final HashMap contactsPhonesShort = new HashMap();
+
+ for (TLRPC.TL_contact value : contactsArr) {
+ TLRPC.User user = usersDict.get(value.user_id);
+ if (user == null || user.phone == null || user.phone.length() == 0) {
+ continue;
+ }
+ if (user.phone.length() > 8) {
+ String shortPhone = user.phone.substring(user.phone.length() - 8);
+ contactsPhonesShort.put(shortPhone, user);
+ } else {
+ contactsPhonesShort.put(user.phone, user);
+ }
+ }
+ int removed = 0;
+ for (HashMap.Entry entry : delayedDontactsToDelete.entrySet()) {
+ Contact contact = entry.getValue();
+ boolean was = false;
+ for (int a = 0; a < contact.shortPhones.size(); a++) {
+ String phone = contact.shortPhones.get(a);
+ TLRPC.User user = contactsPhonesShort.get(phone);
+ if (user != null) {
+ was = true;
+ toDelete.add(user);
+ contact.shortPhones.remove(a);
+ a--;
+ }
+ }
+ if (!was || contact.shortPhones.size() == 0) {
+ removed++;
+ }
+ }
+ if (from != 2 || removed == delayedDontactsToDelete.size()) {
+ delayedDontactsToDelete = null;
+ }
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ }
+ return toDelete;
+ }
+
+ public void processLoadedContacts(final ArrayList contactsArr, final ArrayList usersArr, final int from) {
+ //from:
+ //0 - from server
+ //1 - from db
+ //2 - from imported contacts
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ FileLog.e("tmessages", "done loading contacts");
+ if (from == 1 && contactsArr.isEmpty()) {
+ loadContacts(false);
+ return;
+ }
+ final HashMap usersDict = new HashMap();
+ for (TLRPC.User user : usersArr) {
+ usersDict.put(user.id, user);
+ }
+ if (from == 1) {
+ for (TLRPC.TL_contact contact : contactsArr) {
+ if (usersDict.get(contact.user_id) == null) {
+ loadContacts(false);
+ FileLog.e("tmessages", "contacts are broken, load from server");
+ return;
+ }
+ }
+ }
+
+ if (from == 0 || from == 2) {
+ MessagesStorage.Instance.putUsersAndChats(usersArr, null, true, true);
+ MessagesStorage.Instance.putContacts(contactsArr, true);
+ Collections.sort(contactsArr, new Comparator() {
+ @Override
+ public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) {
+ if (tl_contact.user_id > tl_contact2.user_id) {
+ return 1;
+ } else if (tl_contact.user_id < tl_contact2.user_id) {
+ return -1;
+ }
+ return 0;
+ }
+ });
+ String ids = "";
+ for (TLRPC.TL_contact aContactsArr : contactsArr) {
+ if (ids.length() != 0) {
+ ids += ",";
+ }
+ ids += aContactsArr.user_id;
+ }
+ UserConfig.contactsHash = Utilities.MD5(ids);
+ UserConfig.saveConfig(false);
+ if (from == 2) {
+ loadContacts(false);
+ }
+ }
+
+ Collections.sort(contactsArr, new Comparator() {
+ @Override
+ public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) {
+ TLRPC.User user1 = usersDict.get(tl_contact.user_id);
+ TLRPC.User user2 = usersDict.get(tl_contact2.user_id);
+ String name1 = user1.first_name;
+ if (name1 == null || name1.length() == 0) {
+ name1 = user1.last_name;
+ }
+ String name2 = user2.first_name;
+ if (name2 == null || name2.length() == 0) {
+ name2 = user2.last_name;
+ }
+ return name1.compareTo(name2);
+ }
+ });
+
+ final SparseArray contactsDictionary = new SparseArray();
+ final HashMap> sectionsDict = new HashMap>();
+ final ArrayList sortedSectionsArray = new ArrayList();
+
+ for (TLRPC.TL_contact value : contactsArr) {
+ TLRPC.User user = usersDict.get(value.user_id);
+ if (user == null) {
+ continue;
+ }
+ contactsDictionary.put(value.user_id, value);
+
+ String key = user.first_name;
+ if (key == null || key.length() == 0) {
+ key = user.last_name;
+ }
+ if (key.length() == 0) {
+ key = "#";
+ } else {
+ key = key.toUpperCase();
+ }
+ if (key.length() > 1) {
+ key = key.substring(0, 1);
+ }
+ ArrayList arr = sectionsDict.get(key);
+ if (arr == null) {
+ arr = new ArrayList();
+ sectionsDict.put(key, arr);
+ sortedSectionsArray.add(key);
+ }
+ arr.add(value);
+ }
+
+ Collections.sort(sortedSectionsArray, new Comparator() {
+ @Override
+ public int compare(String s, String s2) {
+ char cv1 = s.charAt(0);
+ char cv2 = s2.charAt(0);
+ if (cv1 == '#') {
+ return 1;
+ } else if (cv2 == '#') {
+ return -1;
+ }
+ return s.compareTo(s2);
+ }
+ });
+
+ final ArrayList toDelete = getContactsToDelete(contactsArr, usersDict, from);
+
+ if (from != 2) {
+ contactsLoaded = true;
+ }
+
+ if (!delayedContactsUpdate.isEmpty() && contactsLoaded && contactsBookLoaded) {
+ applyContactsUpdates(delayedContactsUpdate, null, null, null);
+ delayedContactsUpdate.clear();
+ }
+
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ for (TLRPC.User user : usersArr) {
+ if (from == 1) {
+ MessagesController.Instance.users.putIfAbsent(user.id, user);
+ } else {
+ MessagesController.Instance.users.put(user.id, user);
+ if (user.id == UserConfig.clientUserId) {
+ UserConfig.currentUser = user;
+ }
+ }
+ }
+ contacts = contactsArr;
+ contactsDict = contactsDictionary;
+ usersSectionsDict = sectionsDict;
+ sortedUsersSectionsArray = sortedSectionsArray;
+ if (from != 2) {
+ loadingContacts = false;
+ }
+ performWriteContactsToPhoneBook();
+ updateUnregisteredContacts(contactsArr);
+
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+
+ if (!toDelete.isEmpty()) {
+ deleteContact(toDelete);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ private void updateUnregisteredContacts(final ArrayList contactsArr) {
+ final HashMap contactsPhonesShort = new HashMap();
+
+ for (TLRPC.TL_contact value : contactsArr) {
+ TLRPC.User user = MessagesController.Instance.users.get(value.user_id);
+ if (user == null || user.phone == null || user.phone.length() == 0) {
+ continue;
+ }
+ if (user.phone.length() > 8) {
+ String shortPhone = user.phone.substring(user.phone.length() - 8);
+ contactsPhonesShort.put(shortPhone, value);
+ } else {
+ contactsPhonesShort.put(user.phone, value);
+ }
+ }
+
+ final HashMap> sectionsPhoneDict = new HashMap>();
+ final ArrayList sortedSectionsPhoneArray = new ArrayList();
+ for (HashMap.Entry pair : contactsBook.entrySet()) {
+ Contact value = pair.getValue();
+ int id = pair.getKey();
+
+ boolean skip = false;
+ for (int a = 0; a < value.phones.size(); a++) {
+ String sphone = value.shortPhones.get(a);
+ if (contactsPhonesShort.containsKey(sphone) || value.phoneDeleted.get(a) == 1) {
+ skip = true;
+ break;
+ }
+ }
+ if (skip) {
+ continue;
+ }
+
+ String key = value.first_name;
+ if (key.length() == 0) {
+ key = value.last_name;
+ }
+ if (key.length() == 0) {
+ key = "#";
+ if (value.phones.size() != 0) {
+ value.first_name = "+" + value.phones.get(0);
+ }
+ } else {
+ key = key.toUpperCase();
+ }
+ if (key.length() > 1) {
+ key = key.substring(0, 1);
+ }
+ ArrayList arr = sectionsPhoneDict.get(key);
+ if (arr == null) {
+ arr = new ArrayList();
+ sectionsPhoneDict.put(key, arr);
+ sortedSectionsPhoneArray.add(key);
+ }
+ arr.add(value);
+ }
+ for (HashMap.Entry> entry : sectionsPhoneDict.entrySet()) {
+ Collections.sort(entry.getValue(), new Comparator() {
+ @Override
+ public int compare(Contact contact, Contact contact2) {
+ String toComapre1 = contact.first_name;
+ if (toComapre1.length() == 0) {
+ toComapre1 = contact.last_name;
+ }
+ String toComapre2 = contact2.first_name;
+ if (toComapre2.length() == 0) {
+ toComapre2 = contact2.last_name;
+ }
+ return toComapre1.compareTo(toComapre2);
+ }
+ });
+ }
+ Collections.sort(sortedSectionsPhoneArray, new Comparator() {
+ @Override
+ public int compare(String s, String s2) {
+ char cv1 = s.charAt(0);
+ char cv2 = s2.charAt(0);
+ if (cv1 == '#') {
+ return 1;
+ } else if (cv2 == '#') {
+ return -1;
+ }
+ return s.compareTo(s2);
+ }
+ });
+
+ contactsSectionsDict = sectionsPhoneDict;
+ sortedContactsSectionsArray = sortedSectionsPhoneArray;
+ }
+
+ private void buildContactsSectionsArrays(boolean sort) {
+ if (sort) {
+ Collections.sort(contacts, new Comparator() {
+ @Override
+ public int compare(TLRPC.TL_contact tl_contact, TLRPC.TL_contact tl_contact2) {
+ TLRPC.User user1 = MessagesController.Instance.users.get(tl_contact.user_id);
+ TLRPC.User user2 = MessagesController.Instance.users.get(tl_contact2.user_id);
+ String name1 = user1.first_name;
+ if (name1 == null || name1.length() == 0) {
+ name1 = user1.last_name;
+ }
+ String name2 = user2.first_name;
+ if (name2 == null || name2.length() == 0) {
+ name2 = user2.last_name;
+ }
+ return name1.compareTo(name2);
+ }
+ });
+ }
+
+ String ids = "";
+ final HashMap> sectionsDict = new HashMap>();
+ final ArrayList sortedSectionsArray = new ArrayList();
+
+ for (TLRPC.TL_contact value : contacts) {
+ TLRPC.User user = MessagesController.Instance.users.get(value.user_id);
+ if (user == null) {
+ continue;
+ }
+
+ String key = user.first_name;
+ if (key == null || key.length() == 0) {
+ key = user.last_name;
+ }
+ if (key.length() == 0) {
+ key = "#";
+ } else {
+ key = key.toUpperCase();
+ }
+ if (key.length() > 1) {
+ key = key.substring(0, 1);
+ }
+ ArrayList arr = sectionsDict.get(key);
+ if (arr == null) {
+ arr = new ArrayList();
+ sectionsDict.put(key, arr);
+ sortedSectionsArray.add(key);
+ }
+ arr.add(value);
+ if (ids.length() != 0) {
+ ids += ",";
+ }
+ ids += value.user_id;
+ }
+ UserConfig.contactsHash = Utilities.MD5(ids);
+ UserConfig.saveConfig(false);
+
+ Collections.sort(sortedSectionsArray, new Comparator() {
+ @Override
+ public int compare(String s, String s2) {
+ char cv1 = s.charAt(0);
+ char cv2 = s2.charAt(0);
+ if (cv1 == '#') {
+ return 1;
+ } else if (cv2 == '#') {
+ return -1;
+ }
+ return s.compareTo(s2);
+ }
+ });
+
+ usersSectionsDict = sectionsDict;
+ sortedUsersSectionsArray = sortedSectionsArray;
+ }
+
+ private void performWriteContactsToPhoneBook() {
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build();
+ Cursor c1 = ApplicationLoader.applicationContext.getContentResolver().query(rawContactUri, new String[]{BaseColumns._ID, ContactsContract.RawContacts.SYNC2}, null, null, null);
+ HashMap bookContacts = new HashMap();
+ if (c1 != null) {
+ while (c1.moveToNext()) {
+ bookContacts.put(c1.getInt(1), c1.getLong(0));
+ }
+ c1.close();
+
+ for (TLRPC.TL_contact u : contacts) {
+ if (!bookContacts.containsKey(u.user_id)) {
+ TLRPC.User user = MessagesController.Instance.users.get(u.user_id);
+ addContactToPhoneBook(user);
+ }
+ }
+ }
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ }
+ });
+ }
+
+ private void applyContactsUpdates(ArrayList ids, ConcurrentHashMap userDict, ArrayList newC, ArrayList contactsTD) {
+ if (newC == null || contactsTD == null) {
+ newC = new ArrayList();
+ contactsTD = new ArrayList();
+ for (Integer uid : ids) {
+ if (uid > 0) {
+ TLRPC.TL_contact contact = new TLRPC.TL_contact();
+ contact.user_id = uid;
+ newC.add(contact);
+ if (!delayedContactsUpdate.isEmpty()) {
+ int idx = delayedContactsUpdate.indexOf(-uid);
+ if (idx != -1) {
+ delayedContactsUpdate.remove(idx);
+ }
+ }
+ } else if (uid < 0) {
+ contactsTD.add(-uid);
+ if (!delayedContactsUpdate.isEmpty()) {
+ int idx = delayedContactsUpdate.indexOf(-uid);
+ if (idx != -1) {
+ delayedContactsUpdate.remove(idx);
+ }
+ }
+ }
+ }
+ }
+ FileLog.e("tmessages", "process update - contacts add = " + newC.size() + " delete = " + contactsTD.size());
+
+ String toAdd = "";
+ String toDelete = "";
+ boolean reloadContacts = false;
+
+ for (TLRPC.TL_contact newContact : newC) {
+ TLRPC.User user = null;
+ if (userDict != null) {
+ user = userDict.get(newContact.user_id);
+ }
+ if (user == null) {
+ user = MessagesController.Instance.users.get(newContact.user_id);
+ }
+ if (user == null || user.phone == null && user.phone.length() == 0) {
+ reloadContacts = true;
+ continue;
+ }
+
+ String phone = user.phone;
+ if (phone.length() > 8) {
+ phone = phone.substring(phone.length() - 8);
+ }
+ Contact contact = contactsBookSPhones.get(phone);
+ if (contact != null) {
+ int index = contact.shortPhones.indexOf(phone);
+ if (index != -1) {
+ contact.phoneDeleted.set(index, 0);
+ }
+ }
+ if (toAdd.length() != 0) {
+ toAdd += ",";
+ }
+ toAdd += phone;
+ }
+
+ for (final Integer uid : contactsTD) {
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ deleteContactFromPhoneBook(uid);
+ }
+ });
+
+ TLRPC.User user = null;
+ if (userDict != null) {
+ user = userDict.get(uid);
+ }
+ if (user == null) {
+ user = MessagesController.Instance.users.get(uid);
+ }
+ if (user == null) {
+ reloadContacts = true;
+ continue;
+ }
+
+ if (user.phone != null && user.phone.length() > 0) {
+ String phone = user.phone;
+ if (phone.length() > 8) {
+ phone = phone.substring(phone.length() - 8);
+ }
+ Contact contact = contactsBookSPhones.get(phone);
+ if (contact != null) {
+ int index = contact.shortPhones.indexOf(phone);
+ if (index != -1) {
+ contact.phoneDeleted.set(index, 1);
+ }
+ }
+ if (toDelete.length() != 0) {
+ toDelete += ",";
+ }
+ toDelete += phone;
+ }
+ }
+
+ if (toAdd.length() != 0 || toDelete.length() != 0) {
+ MessagesStorage.Instance.applyPhoneBookUpdates(toAdd, toDelete);
+ }
+
+ if (reloadContacts) {
+ Utilities.stageQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ loadContacts(false);
+ }
+ });
+ } else {
+ final ArrayList newContacts = newC;
+ final ArrayList contactsToDelete = contactsTD;
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ for (TLRPC.TL_contact contact : newContacts) {
+ if (contactsDict.get(contact.user_id) == null) {
+ contacts.add(contact);
+ contactsDict.put(contact.user_id, contact);
+ }
+ }
+ for (Integer uid : contactsToDelete) {
+ TLRPC.TL_contact contact = contactsDict.get(uid);
+ if (contact != null) {
+ contacts.remove(contact);
+ contactsDict.remove(uid);
+ }
+ }
+ if (!newContacts.isEmpty()) {
+ updateUnregisteredContacts(contacts);
+ performWriteContactsToPhoneBook();
+ }
+ performSyncPhoneBook(getContactsCopy(contactsBook), false, false);
+ buildContactsSectionsArrays(!newContacts.isEmpty());
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+ }
+ });
+ }
+ }
+
+ public void processContactsUpdates(ArrayList ids, ConcurrentHashMap userDict) {
+ final ArrayList newContacts = new ArrayList();
+ final ArrayList contactsToDelete = new ArrayList();
+ for (Integer uid : ids) {
+ if (uid > 0) {
+ TLRPC.TL_contact contact = new TLRPC.TL_contact();
+ contact.user_id = uid;
+ newContacts.add(contact);
+ if (!delayedContactsUpdate.isEmpty()) {
+ int idx = delayedContactsUpdate.indexOf(-uid);
+ if (idx != -1) {
+ delayedContactsUpdate.remove(idx);
+ }
+ }
+ } else if (uid < 0) {
+ contactsToDelete.add(-uid);
+ if (!delayedContactsUpdate.isEmpty()) {
+ int idx = delayedContactsUpdate.indexOf(-uid);
+ if (idx != -1) {
+ delayedContactsUpdate.remove(idx);
+ }
+ }
+ }
+ }
+ if (!contactsToDelete.isEmpty()) {
+ MessagesStorage.Instance.deleteContacts(contactsToDelete);
+ }
+ if (!newContacts.isEmpty()) {
+ MessagesStorage.Instance.putContacts(newContacts, false);
+ }
+ if (!contactsLoaded || !contactsBookLoaded) {
+ delayedContactsUpdate.addAll(ids);
+ FileLog.e("tmessages", "delay update - contacts add = " + newContacts.size() + " delete = " + contactsToDelete.size());
+ } else {
+ applyContactsUpdates(ids, userDict, newContacts, contactsToDelete);
+ }
+ }
+
+ public long addContactToPhoneBook(TLRPC.User user) {
+ if (currentAccount == null || user == null || user.phone == null || user.phone.length() == 0) {
+ return -1;
+ }
+ long res = -1;
+ synchronized (observerLock) {
+ ignoreChanges = true;
+ }
+ ArrayList query = new ArrayList();
+
+ ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
+ builder.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name);
+ builder.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type);
+ builder.withValue(ContactsContract.RawContacts.SYNC1, user.phone);
+ builder.withValue(ContactsContract.RawContacts.SYNC2, user.id);
+ query.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, user.first_name);
+ builder.withValue(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, user.last_name);
+ query.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+ builder.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, "+" + user.phone);
+ builder.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
+ query.add(builder.build());
+
+ builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
+ builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
+ builder.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.org.telegram.messenger.android.profile");
+ builder.withValue(ContactsContract.Data.DATA1, "+" + user.phone);
+ builder.withValue(ContactsContract.Data.DATA2, "Telegram Profile");
+ builder.withValue(ContactsContract.Data.DATA3, "+" + user.phone);
+ builder.withValue(ContactsContract.Data.DATA4, user.id);
+ query.add(builder.build());
+ try {
+ ContentProviderResult[] result = ApplicationLoader.applicationContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, query);
+ res = Long.parseLong(result[0].uri.getLastPathSegment());
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ synchronized (observerLock) {
+ ignoreChanges = false;
+ }
+ return res;
+ }
+
+ private void deleteContactFromPhoneBook(int uid) {
+ ContentResolver contentResolver = ApplicationLoader.applicationContext.getContentResolver();
+ synchronized (observerLock) {
+ ignoreChanges = true;
+ }
+ try {
+ Uri rawContactUri = ContactsContract.RawContacts.CONTENT_URI.buildUpon().appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true").appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, currentAccount.name).appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, currentAccount.type).build();
+ int value = contentResolver.delete(rawContactUri, ContactsContract.RawContacts.SYNC2 + " = " + uid, null);
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ }
+ synchronized (observerLock) {
+ ignoreChanges = false;
+ }
+ }
+
+ public void addContact(TLRPC.User user) {
+ if (user == null) {
+ return;
+ }
+
+ TLRPC.TL_contacts_importContacts req = new TLRPC.TL_contacts_importContacts();
+ ArrayList contactsParams = new ArrayList();
+ TLRPC.TL_inputPhoneContact c = new TLRPC.TL_inputPhoneContact();
+ c.phone = user.phone;
+ c.first_name = user.first_name;
+ c.last_name = user.last_name;
+ c.client_id = 0;
+ contactsParams.add(c);
+ req.contacts = contactsParams;
+ req.replace = false;
+ ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() {
+ @Override
+ public void run(TLObject response, TLRPC.TL_error error) {
+ if (error != null) {
+ return;
+ }
+ final TLRPC.TL_contacts_importedContacts res = (TLRPC.TL_contacts_importedContacts)response;
+ MessagesStorage.Instance.putUsersAndChats(res.users, null, true, true);
+
+ for (final TLRPC.User u : res.users) {
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ addContactToPhoneBook(u);
+ }
+ });
+ TLRPC.TL_contact newContact = new TLRPC.TL_contact();
+ newContact.user_id = u.id;
+ ArrayList arrayList = new ArrayList();
+ arrayList.add(newContact);
+ MessagesStorage.Instance.putContacts(arrayList, false);
+
+ if (u.phone != null && u.phone.length() > 0) {
+ String name = Utilities.formatName(u.first_name, u.last_name);
+ String phone = u.phone;
+ if (phone.length() > 8) {
+ phone = phone.substring(phone.length() - 8);
+ }
+ MessagesStorage.Instance.applyPhoneBookUpdates(phone, "");
+ Contact contact = contactsBookSPhones.get(phone);
+ if (contact != null) {
+ int index = contact.shortPhones.indexOf(phone);
+ if (index != -1) {
+ contact.phoneDeleted.set(index, 0);
+ }
+ }
+ }
+ }
+
+ performSyncPhoneBook(getContactsCopy(contactsBook), false, false);
+
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ for (TLRPC.User u : res.users) {
+ MessagesController.Instance.users.put(u.id, u);
+ if (contactsDict.get(u.id) == null) {
+ TLRPC.TL_contact newContact = new TLRPC.TL_contact();
+ newContact.user_id = u.id;
+ contacts.add(newContact);
+ contactsDict.put(newContact.user_id, newContact);
+ }
+ }
+ buildContactsSectionsArrays(true);
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+ }
+ });
+ }
+ }, null, true, RPCRequest.RPCRequestClassGeneric | RPCRequest.RPCRequestClassFailOnServerErrors | RPCRequest.RPCRequestClassCanCompress);
+ }
+
+ public void deleteContact(final ArrayList users) {
+ if (users == null || users.isEmpty()) {
+ return;
+ }
+ TLRPC.TL_contacts_deleteContacts req = new TLRPC.TL_contacts_deleteContacts();
+ final ArrayList uids = new ArrayList();
+ for (TLRPC.User user : users) {
+ TLRPC.InputUser inputUser = MessagesController.getInputUser(user);
+ if (inputUser == null) {
+ continue;
+ }
+ uids.add(user.id);
+ req.id.add(inputUser);
+ }
+ ConnectionsManager.Instance.performRpc(req, new RPCRequest.RPCRequestDelegate() {
+ @Override
+ public void run(TLObject response, TLRPC.TL_error error) {
+ if (error != null) {
+ return;
+ }
+ MessagesStorage.Instance.deleteContacts(uids);
+ Utilities.globalQueue.postRunnable(new Runnable() {
+ @Override
+ public void run() {
+ for (TLRPC.User user : users) {
+ deleteContactFromPhoneBook(user.id);
+ }
+ }
+ });
+
+ for (TLRPC.User user : users) {
+ if (user.phone != null && user.phone.length() > 0) {
+ String name = Utilities.formatName(user.first_name, user.last_name);
+ String phone = user.phone;
+ if (phone.length() > 8) {
+ phone = phone.substring(phone.length() - 8);
+ }
+ MessagesStorage.Instance.applyPhoneBookUpdates(phone, "");
+ Contact contact = contactsBookSPhones.get(phone);
+ if (contact != null) {
+ int index = contact.shortPhones.indexOf(phone);
+ if (index != -1) {
+ contact.phoneDeleted.set(index, 1);
+ }
+ }
+ }
+ }
+
+ Utilities.RunOnUIThread(new Runnable() {
+ @Override
+ public void run() {
+ boolean remove = false;
+ for (TLRPC.User user : users) {
+ TLRPC.TL_contact contact = contactsDict.get(user.id);
+ if (contact != null) {
+ remove = true;
+ contacts.remove(contact);
+ contactsDict.remove(user.id);
+ }
+ }
+ if (remove) {
+ buildContactsSectionsArrays(false);
+ }
+ NotificationCenter.Instance.postNotificationName(MessagesController.updateInterfaces, MessagesController.UPDATE_MASK_NAME);
+ NotificationCenter.Instance.postNotificationName(MessagesController.contactsDidLoaded);
+ }
+ });
+ }
+ }, null, true, RPCRequest.RPCRequestClassGeneric);
+ }
+}
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java
index 00d974daa..d4d6ed09e 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/DispatchQueue.java
@@ -42,10 +42,14 @@ public class DispatchQueue extends Thread {
}
public void postRunnable(Runnable runnable) {
- postRunnable(runnable, 0);
+ postRunnable(runnable, 0, false);
}
- public void postRunnable(Runnable runnable, int delay) {
+ public void postRunnable(Runnable runnable, boolean inFront) {
+ postRunnable(runnable, 0, true);
+ }
+
+ public void postRunnable(Runnable runnable, int delay, boolean inFront) {
if (handler == null) {
try {
synchronized (handlerSyncObject) {
@@ -58,7 +62,11 @@ public class DispatchQueue extends Thread {
if (handler != null) {
if (delay <= 0) {
- handler.post(runnable);
+ if (inFront) {
+ handler.postAtFrontOfQueue(runnable);
+ } else {
+ handler.post(runnable);
+ }
} else {
handler.postDelayed(runnable, delay);
}
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java
index 355dd8512..9fe4965f3 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/ExportAuthorizationAction.java
@@ -53,7 +53,7 @@ public class ExportAuthorizationAction extends Action {
public void run() {
beginExport();
}
- }, retryCount * 1500);
+ }, retryCount * 1500, false);
}
}
}
@@ -84,7 +84,7 @@ public class ExportAuthorizationAction extends Action {
public void run() {
beginExport();
}
- }, retryCount * 1500);
+ }, retryCount * 1500, false);
}
}
}
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java
index f6d38bc08..93f2b9568 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoadOperation.java
@@ -420,7 +420,12 @@ public class FileLoadOperation {
if (renamed) {
image = BitmapFactory.decodeStream(new FileInputStream(cacheFileFinal), null, opts);
} else {
- image = BitmapFactory.decodeStream(new FileInputStream(cacheFileTemp), null, opts);
+ try {
+ image = BitmapFactory.decodeStream(new FileInputStream(cacheFileTemp), null, opts);
+ } catch (Exception e) {
+ FileLog.e("tmessages", e);
+ image = BitmapFactory.decodeStream(new FileInputStream(cacheFileFinal), null, opts);
+ }
}
if (filter != null && image != null) {
float bitmapW = image.getWidth();
@@ -440,10 +445,11 @@ public class FileLoadOperation {
if (FileLoader.Instance.runtimeHack != null) {
FileLoader.Instance.runtimeHack.trackFree(image.getRowBytes() * image.getHeight());
}
+ delegate.didFinishLoadingFile(FileLoadOperation.this);
} catch (Exception e) {
FileLog.e("tmessages", e);
+ delegate.didFailedLoadingFile(FileLoadOperation.this);
}
- delegate.didFinishLoadingFile(FileLoadOperation.this);
}
});
} else {
diff --git a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java
index 13fb988fc..1bd9f831b 100644
--- a/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java
+++ b/TMessagesProj/src/main/java/org/telegram/messenger/FileLoader.java
@@ -15,13 +15,12 @@ import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.media.ExifInterface;
import android.os.Build;
-import android.view.View;
-import android.widget.ImageView;
import org.telegram.TL.TLRPC;
import org.telegram.objects.MessageObject;
import org.telegram.ui.ApplicationLoader;
import org.telegram.ui.Views.BackupImageView;
+import org.telegram.ui.Views.ImageReceiver;
import java.io.ByteArrayOutputStream;
import java.io.File;
@@ -134,29 +133,28 @@ public class FileLoader {
private class CacheImage {
public String key;
- public ArrayList imageViewArray;
+ final public ArrayList