From 234983f0afcf96455b4d33be87d9cb84970f42be Mon Sep 17 00:00:00 2001 From: Sylvain Berfini Date: Thu, 16 Feb 2017 12:13:13 +0100 Subject: [PATCH] Now that contacts loading are lightning fast, stop fetching them from async task + fixed contacts refreshing when rotating device --- src/org/linphone/ContactsManager.java | 351 +++++++++++-------------- src/org/linphone/LinphoneActivity.java | 13 +- src/org/linphone/LinphoneContact.java | 2 +- 3 files changed, 153 insertions(+), 213 deletions(-) diff --git a/src/org/linphone/ContactsManager.java b/src/org/linphone/ContactsManager.java index d0ebd440f..0ea30dc8a 100644 --- a/src/org/linphone/ContactsManager.java +++ b/src/org/linphone/ContactsManager.java @@ -48,7 +48,6 @@ import android.database.MatrixCursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; -import android.os.AsyncTask; import android.os.Handler; import android.os.Message; import android.provider.ContactsContract; @@ -60,13 +59,12 @@ interface ContactsUpdatedListener { } public class ContactsManager extends ContentObserver { - public static int CONTACTS_STEP = 15; private static ContactsManager instance; + private List contacts, sipContacts; private boolean preferLinphoneContacts = false, isContactPresenceDisabled = true, hasContactAccess = false; private ContentResolver contentResolver; private Context context; - private ContactsFetchTask contactsFetchTask; private HashMap contactsCache; private HashMap androidContactsCache; private LinphoneContact contactNotFound; @@ -99,13 +97,13 @@ public class ContactsManager extends ContentObserver { } public void destroy() { - if (contactsFetchTask != null && !contactsFetchTask.isCancelled()) { - contactsFetchTask.cancel(true); - } defaultAvatar.recycle(); instance = null; } + public boolean contactsFetchedOnce() { + return contacts.size() > 0; + } public Bitmap getDefaultAvatarBitmap() { return defaultAvatar; @@ -118,31 +116,31 @@ public class ContactsManager extends ContentObserver { @Override public void onChange(boolean selfChange, Uri uri) { - fetchContactsAsync(); + fetchContactsSync(); } public ContentResolver getContentResolver() { return contentResolver; } - public static final synchronized ContactsManager getInstance() { + public static final ContactsManager getInstance() { if (instance == null) instance = new ContactsManager(handler); return instance; } - public synchronized boolean hasContacts() { + public boolean hasContacts() { return contacts.size() > 0; } - public synchronized List getContacts() { + public List getContacts() { return contacts; } - public synchronized List getSIPContacts() { + public List getSIPContacts() { return sipContacts; } - public synchronized List getContacts(String search) { + public List getContacts(String search) { search = search.toLowerCase(Locale.getDefault()); List searchContactsBegin = new ArrayList(); List searchContactsContain = new ArrayList(); @@ -159,7 +157,7 @@ public class ContactsManager extends ContentObserver { return searchContactsBegin; } - public synchronized List getSIPContacts(String search) { + public List getSIPContacts(String search) { search = search.toLowerCase(Locale.getDefault()); List searchContactsBegin = new ArrayList(); List searchContactsContain = new ArrayList(); @@ -223,7 +221,7 @@ public class ContactsManager extends ContentObserver { initializeContactManager(context, contentResolver); } - public synchronized LinphoneContact findContactFromAddress(LinphoneAddress address) { + public LinphoneContact findContactFromAddress(LinphoneAddress address) { String sipUri = address.asStringUriOnly(); String username = address.getUserName(); @@ -257,7 +255,7 @@ public class ContactsManager extends ContentObserver { return null; } - public synchronized LinphoneContact findContactFromPhoneNumber(String phoneNumber) { + public LinphoneContact findContactFromPhoneNumber(String phoneNumber) { LinphoneContact cache = contactsCache.get(phoneNumber); if (cache != null) { if (cache == contactNotFound) return null; @@ -286,15 +284,15 @@ public class ContactsManager extends ContentObserver { return null; } - public synchronized void setContacts(List c) { + public void setContacts(List c) { contacts = c; } - public synchronized void setSipContacts(List c) { + public void setSipContacts(List c) { sipContacts = c; } - public synchronized void refreshSipContact(LinphoneFriend lf) { + public void refreshSipContact(LinphoneFriend lf) { LinphoneContact contact = (LinphoneContact)((LinphoneFriendImpl)lf).getUserData(); if (!sipContacts.contains(contact)) { sipContacts.add(contact); @@ -306,210 +304,157 @@ public class ContactsManager extends ContentObserver { } } - public synchronized void fetchContactsAsync() { - if (contactsFetchTask != null && !contactsFetchTask.isCancelled()) { - contactsFetchTask.cancel(true); - } - contactsFetchTask = new ContactsFetchTask(); - contactsFetchTask.execute(); - } - - private class ContactsLists { - public List contacts; - public List sipContacts; - - public ContactsLists(List c, List s) { - contacts = c; - sipContacts = s; - } - } + public void fetchContactsSync() { + List contacts = new ArrayList(); + List sipContacts = new ArrayList(); + Date contactsTime = new Date(); + androidContactsCache.clear(); - private class ContactsFetchTask extends AsyncTask { - protected ContactsLists doInBackground(Void... params) { - List contacts = new ArrayList(); - List sipContacts = new ArrayList(); - Date contactsTime = new Date(); - androidContactsCache.clear(); - - //We need to check sometimes to know if Linphone was destroyed - if (this.isCancelled()) { - return null; + if (hasContactsAccess()) { + Cursor c = getContactsCursor(contentResolver); + if (c != null) { + while (c.moveToNext()) { + String id = c.getString(c.getColumnIndex(Data.CONTACT_ID)); + String displayName = c.getString(c.getColumnIndex(Data.DISPLAY_NAME_PRIMARY)); + LinphoneContact contact = new LinphoneContact(); + + contact.setFullName(displayName); + contact.setAndroidId(id); + /*contact.getAndroidIds();*/ + contacts.add(contact); + androidContactsCache.put(id, contact); + } + c.close(); } - if (hasContactsAccess()) { - Cursor c = getContactsCursor(contentResolver); + boolean isOrgVisible = LinphoneManager.getInstance().getContext().getResources().getBoolean(R.bool.display_contact_organization); + if (isOrgVisible) { + c = getOrganizationCursor(contentResolver); if (c != null) { while (c.moveToNext()) { - String id = c.getString(c.getColumnIndex(Data.CONTACT_ID)); - String displayName = c.getString(c.getColumnIndex(Data.DISPLAY_NAME_PRIMARY)); - LinphoneContact contact = new LinphoneContact(); - - contact.setFullName(displayName); - contact.setAndroidId(id); - /*contact.getAndroidIds();*/ - contacts.add(contact); - androidContactsCache.put(id, contact); + String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); + String org = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY)); + LinphoneContact contact = androidContactsCache.get(id); + if (contact != null) { + contact.setOrganization(org); + } } c.close(); } - - boolean isOrgVisible = LinphoneManager.getInstance().getContext().getResources().getBoolean(R.bool.display_contact_organization); - if (isOrgVisible) { - c = getOrganizationCursor(contentResolver); - if (c != null) { - while (c.moveToNext()) { - String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); - String org = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Organization.COMPANY)); - LinphoneContact contact = androidContactsCache.get(id); - if (contact != null) { - contact.setOrganization(org); - } + } + } else { + Log.w("[Permission] Read contacts permission wasn't granted, only fetch LinphoneFriends"); + } + long timeElapsed = (new Date()).getTime() - contactsTime.getTime(); + String time = String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(timeElapsed), + TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); + Log.i("[ContactsManager] Step 1 for " + contacts.size() + " contacts executed in " + time); + + LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); + if (lc != null) { + for (LinphoneFriend friend : lc.getFriendList()) { + String refkey = friend.getRefKey(); + if (refkey != null) { + boolean found = false; + for (LinphoneContact contact : contacts) { + if (refkey.equals(contact.getAndroidId())) { + // Native matching contact found, link the friend to it + contact.setFriend(friend); + found = true; + break; } - c.close(); } - } - } else { - Log.w("[Permission] Read contacts permission wasn't granted, only fetch LinphoneFriends"); - } - long timeElapsed = (new Date()).getTime() - contactsTime.getTime(); - String time = String.format("%02d:%02d", - TimeUnit.MILLISECONDS.toMinutes(timeElapsed), - TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); - Log.i("[ContactsManager] Step 1 for " + contacts.size() + " contacts executed in " + time); - - //We need to check sometimes to know if Linphone was destroyed - if (this.isCancelled()) { - return null; - } - LinphoneCore lc = LinphoneManager.getLcIfManagerNotDestroyedOrNull(); - if (lc != null) { - for (LinphoneFriend friend : lc.getFriendList()) { - String refkey = friend.getRefKey(); - if (refkey != null) { - boolean found = false; - for (LinphoneContact contact : contacts) { - if (refkey.equals(contact.getAndroidId())) { - // Native matching contact found, link the friend to it - contact.setFriend(friend); - found = true; - break; - } - } - if (!found) { - if (hasContactAccess) { - // If refkey != null and hasContactAccess but there isn't a native contact with this value, then this contact has been deleted. Let's do the same with the LinphoneFriend - lc.removeFriend(friend); - } else { - // Refkey not null but no contact access => can't link it to native contact so display it on is own - LinphoneContact contact = new LinphoneContact(); - contact.setFriend(friend); - contact.refresh(); - if (contact.hasAddress()) { - sipContacts.add(contact); - } - contacts.add(contact); - } - } else { - // Now that we no longer store friends in database that match one in the system, let's remove it + if (!found) { + if (hasContactAccess) { + // If refkey != null and hasContactAccess but there isn't a native contact with this value, then this contact has been deleted. Let's do the same with the LinphoneFriend lc.removeFriend(friend); + } else { + // Refkey not null but no contact access => can't link it to native contact so display it on is own + LinphoneContact contact = new LinphoneContact(); + contact.setFriend(friend); + contact.refresh(); + if (contact.hasAddress()) { + sipContacts.add(contact); + } + contacts.add(contact); } } else { - // No refkey so it's a standalone contact - LinphoneContact contact = new LinphoneContact(); - contact.setFriend(friend); - contact.refresh(); - if (contact.hasAddress()) { - sipContacts.add(contact); - } - contacts.add(contact); + // Now that we no longer store friends in database that match one in the system, let's remove it + lc.removeFriend(friend); } - } - } - Collections.sort(contacts); - Collections.sort(sipContacts); - publishProgress(new ContactsLists(contacts, sipContacts)); - - //We need to check sometimes to know if Linphone was destroyed - if (this.isCancelled()) { - return null; - } - - if (hasContactsAccess()) { - Cursor c = getPhonesCursor(contentResolver); - if (c != null) { - while (c.moveToNext()) { - String id = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)); - String number = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); - LinphoneContact contact = androidContactsCache.get(id); - if (contact != null) { - contact.addNumberOrAddress(new LinphoneNumberOrAddress(number, false)); - } + } else { + // No refkey so it's a standalone contact + LinphoneContact contact = new LinphoneContact(); + contact.setFriend(friend); + contact.refresh(); + if (contact.hasAddress()) { + sipContacts.add(contact); } - c.close(); - } - c = getSipCursor(contentResolver); - if (c != null) { - while (c.moveToNext()) { - String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); - String sip = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS)); - LinphoneContact contact = androidContactsCache.get(id); - if (contact != null) { - contact.addNumberOrAddress(new LinphoneNumberOrAddress(sip, true)); - if (!sipContacts.contains(contact)) { - sipContacts.add(contact); - } - } - } - c.close(); - } - Collections.sort(sipContacts); - } - publishProgress(new ContactsLists(contacts, sipContacts)); - - timeElapsed = (new Date()).getTime() - contactsTime.getTime(); - time = String.format("%02d:%02d", - TimeUnit.MILLISECONDS.toMinutes(timeElapsed), - TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); - Log.i("[ContactsManager] Step 2 for " + contacts.size() + " contacts executed in " + time); - - for (LinphoneContact contact : contacts) { - //We need to check sometimes to know if Linphone was destroyed - if (this.isCancelled()) { - return null; - } - // Create the LinphoneFriends matching the native contacts - contact.createOrUpdateLinphoneFriendFromNativeContact(); - } - timeElapsed = (new Date()).getTime() - contactsTime.getTime(); - time = String.format("%02d:%02d", - TimeUnit.MILLISECONDS.toMinutes(timeElapsed), - TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); - Log.i("[ContactsManager] Step 3 for " + contacts.size() + " contacts executed in " + time); - - androidContactsCache.clear(); - return new ContactsLists(contacts, sipContacts); - } - - protected void onProgressUpdate(ContactsLists... result) { - synchronized (ContactsManager.this) { - setContacts(result[0].contacts); - setSipContacts(result[0].sipContacts); - contactsCache.clear(); - for (ContactsUpdatedListener listener : contactsUpdatedListeners) { - listener.onContactsUpdated(); + contacts.add(contact); } } } - - protected void onPostExecute(ContactsLists result) { - Log.d("[ContactsManager] Updating contacts subscribtions"); - LinphoneManager.getLc().getFriendLists()[0].updateSubscriptions(); - for (ContactsUpdatedListener listener : contactsUpdatedListeners) { - listener.onContactsUpdated(); + + if (hasContactsAccess()) { + Cursor c = getPhonesCursor(contentResolver); + if (c != null) { + while (c.moveToNext()) { + String id = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.CONTACT_ID)); + String number = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); + LinphoneContact contact = androidContactsCache.get(id); + if (contact != null) { + contact.addNumberOrAddress(new LinphoneNumberOrAddress(number, false)); + } + } + c.close(); } + c = getSipCursor(contentResolver); + if (c != null) { + while (c.moveToNext()) { + String id = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID)); + String sip = c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.SipAddress.SIP_ADDRESS)); + LinphoneContact contact = androidContactsCache.get(id); + if (contact != null) { + contact.addNumberOrAddress(new LinphoneNumberOrAddress(sip, true)); + if (!sipContacts.contains(contact)) { + sipContacts.add(contact); + } + } + } + c.close(); + } + } + + timeElapsed = (new Date()).getTime() - contactsTime.getTime(); + time = String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(timeElapsed), + TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); + Log.i("[ContactsManager] Step 2 for " + contacts.size() + " contacts executed in " + time); + + for (LinphoneContact contact : contacts) { + // Create the LinphoneFriends matching the native contacts + contact.createOrUpdateLinphoneFriendFromNativeContact(); + } + timeElapsed = (new Date()).getTime() - contactsTime.getTime(); + time = String.format("%02d:%02d", + TimeUnit.MILLISECONDS.toMinutes(timeElapsed), + TimeUnit.MILLISECONDS.toSeconds(timeElapsed) - + TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(timeElapsed))); + Log.i("[ContactsManager] Step 3 for " + contacts.size() + " contacts executed in " + time); + + androidContactsCache.clear(); + Collections.sort(contacts); + Collections.sort(sipContacts); + setContacts(contacts); + setSipContacts(sipContacts); + contactsCache.clear(); + + LinphoneManager.getLc().getFriendLists()[0].updateSubscriptions(); + for (ContactsUpdatedListener listener : contactsUpdatedListeners) { + listener.onContactsUpdated(); } } diff --git a/src/org/linphone/LinphoneActivity.java b/src/org/linphone/LinphoneActivity.java index 9a3b2fe45..8a26ff5b4 100644 --- a/src/org/linphone/LinphoneActivity.java +++ b/src/org/linphone/LinphoneActivity.java @@ -133,7 +133,6 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick private RelativeLayout sideMenuContent, quitLayout, defaultAccount; private ListView accountsList, sideMenuItemList; private ImageView menu; - private boolean fetchedContactsOnce = false; private boolean doNotGoToCallActivity = false; private List sideMenuItems; private boolean callTransfer = false; @@ -1264,10 +1263,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick } if (readContactsI >= 0 && grantResults[readContactsI] == PackageManager.PERMISSION_GRANTED) { ContactsManager.getInstance().enableContactsAccess(); - if (!fetchedContactsOnce) { + if (!ContactsManager.getInstance().contactsFetchedOnce()) { ContactsManager.getInstance().enableContactsAccess(); - ContactsManager.getInstance().fetchContactsAsync(); - fetchedContactsOnce = true; + ContactsManager.getInstance().fetchContactsSync(); } } } @@ -1304,10 +1302,9 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick permissionsList.add(Manifest.permission.READ_CONTACTS); } } else { - if (!fetchedContactsOnce) { + if (!ContactsManager.getInstance().contactsFetchedOnce()) { ContactsManager.getInstance().enableContactsAccess(); - ContactsManager.getInstance().fetchContactsAsync(); - fetchedContactsOnce = true; + ContactsManager.getInstance().fetchContactsSync(); } } @@ -1321,14 +1318,12 @@ public class LinphoneActivity extends LinphoneGenericActivity implements OnClick @Override protected void onSaveInstanceState(Bundle outState) { outState.putSerializable("currentFragment", currentFragment); - outState.putBoolean("fetchedContactsOnce", fetchedContactsOnce); super.onSaveInstanceState(outState); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - fetchedContactsOnce = savedInstanceState.getBoolean("fetchedContactsOnce"); } @Override diff --git a/src/org/linphone/LinphoneContact.java b/src/org/linphone/LinphoneContact.java index a8bc62671..2b9bd3118 100644 --- a/src/org/linphone/LinphoneContact.java +++ b/src/org/linphone/LinphoneContact.java @@ -463,7 +463,7 @@ public class LinphoneContact implements Serializable, Comparable