Turning contact photo loader into an application-wide service.
It will no longer need to reload everything on orientation change
and other similar events.
Change-Id: Ibd4c823673d6b380df96a91a2829d24f910bcfbd
diff --git a/src/com/android/contacts/ContactPhotoLoader.java b/src/com/android/contacts/ContactPhotoManager.java
similarity index 90%
rename from src/com/android/contacts/ContactPhotoLoader.java
rename to src/com/android/contacts/ContactPhotoManager.java
index b511c65..ddd6a0e 100644
--- a/src/com/android/contacts/ContactPhotoLoader.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -16,6 +16,7 @@
package com.android.contacts;
+import com.android.contacts.model.AccountTypeManager;
import com.google.android.collect.Lists;
import android.content.ContentResolver;
@@ -41,13 +42,71 @@
import java.util.concurrent.ConcurrentHashMap;
/**
- * Asynchronously loads contact photos and maintains cache of photos. The class is
- * mostly single-threaded. The only two methods accessed by the loader thread are
- * {@link #cacheBitmap} and {@link #obtainPhotoIdsAndUrisToLoad}. Those methods access concurrent
- * hash maps shared with the main thread.
+ * Asynchronously loads contact photos and maintains a cache of photos.
*/
-public class ContactPhotoLoader implements Callback {
- private static final String TAG = "ContactPhotoLoader";
+public abstract class ContactPhotoManager {
+
+ static final String TAG = "ContactPhotoManager";
+
+ public static final String CONTACT_PHOTO_SERVICE = "contactPhotos";
+
+ /**
+ * The resource ID of the image to be used when the photo is unavailable or being
+ * loaded.
+ */
+ protected final int mDefaultResourceId = R.drawable.ic_contact_picture;
+
+ /**
+ * Requests the singleton instance of {@link AccountTypeManager} with data bound from
+ * the available authenticators. This method can safely be called from the UI thread.
+ */
+ public static ContactPhotoManager getInstance(Context context) {
+ ContactPhotoManager service =
+ (ContactPhotoManager) context.getSystemService(CONTACT_PHOTO_SERVICE);
+ if (service == null) {
+ service = createContactPhotoManager(context);
+ Log.e(TAG, "No contact photo service in context: " + context);
+ }
+ return service;
+ }
+
+ public static synchronized ContactPhotoManager createContactPhotoManager(Context context) {
+ return new ContactPhotoManagerImpl(context);
+ }
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the database.
+ */
+ public abstract void loadPhoto(ImageView view, long photoId);
+
+ /**
+ * Load photo into the supplied image view. If the photo is already cached,
+ * it is displayed immediately. Otherwise a request is sent to load the photo
+ * from the location specified by the URI.
+ */
+ public abstract void loadPhoto(ImageView view, Uri photoUri);
+
+ /**
+ * Temporarily stops loading photos from the database.
+ */
+ public abstract void pause();
+
+ /**
+ * Resumes loading photos from the database.
+ */
+ public abstract void resume();
+
+ /**
+ * Marks all cached photos for reloading. We can continue using cache but should
+ * also make sure the photos haven't changed in the background and notify the views
+ * if so.
+ */
+ public abstract void refreshCache();
+}
+
+class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback {
private static final String LOADER_THREAD_NAME = "ContactPhotoLoader";
/**
@@ -67,12 +126,6 @@
private final String[] COLUMNS = new String[] { Photo._ID, Photo.PHOTO };
/**
- * The resource ID of the image to be used when the photo is unavailable or being
- * loaded.
- */
- private final int mDefaultResourceId;
-
- /**
* Maintains the state of a particular photo.
*/
private static class BitmapHolder {
@@ -86,6 +139,8 @@
SoftReference<Bitmap> bitmapRef;
}
+ private final Context mContext;
+
/**
* A soft cache for photos.
*/
@@ -120,25 +175,11 @@
*/
private boolean mPaused;
- private final Context mContext;
-
- /**
- * Constructor.
- *
- * @param context content context
- * @param defaultResourceId the image resource ID to be used when there is
- * no photo for a contact
- */
- public ContactPhotoLoader(Context context, int defaultResourceId) {
- mDefaultResourceId = defaultResourceId;
+ public ContactPhotoManagerImpl(Context context) {
mContext = context;
}
- /**
- * Load photo into the supplied image view. If the photo is already cached,
- * it is displayed immediately. Otherwise a request is sent to load the photo
- * from the database.
- */
+ @Override
public void loadPhoto(ImageView view, long photoId) {
if (photoId == 0) {
// No photo is needed
@@ -149,11 +190,7 @@
}
}
- /**
- * Load photo into the supplied image view. If the photo is already cached,
- * it is displayed immediately. Otherwise a request is sent to load the photo
- * from the location specified by the URI.
- */
+ @Override
public void loadPhoto(ImageView view, Uri photoUri) {
if (photoUri == null) {
// No photo is needed
@@ -177,11 +214,7 @@
}
}
- /**
- * Mark all cached photos for reloading. We can continue using cache but should
- * also make sure the photos haven't changed in the background and notify the views
- * if so.
- */
+ @Override
public void refreshCache() {
for (BitmapHolder holder : mBitmapCache.values()) {
if (holder.state == BitmapHolder.LOADED) {
@@ -232,36 +265,17 @@
return false;
}
- /**
- * Stops loading images, kills the image loader thread and clears all caches.
- */
- public void stop() {
- pause();
-
- if (mLoaderThread != null) {
- mLoaderThread.quit();
- mLoaderThread = null;
- }
-
- mPendingRequests.clear();
- mBitmapCache.clear();
- }
-
public void clear() {
mPendingRequests.clear();
mBitmapCache.clear();
}
- /**
- * Temporarily stops loading photos from the database.
- */
+ @Override
public void pause() {
mPaused = true;
}
- /**
- * Resumes loading photos from the database.
- */
+ @Override
public void resume() {
mPaused = false;
if (!mPendingRequests.isEmpty()) {
diff --git a/src/com/android/contacts/ContactsApplication.java b/src/com/android/contacts/ContactsApplication.java
index 8346c04..42ea641 100644
--- a/src/com/android/contacts/ContactsApplication.java
+++ b/src/com/android/contacts/ContactsApplication.java
@@ -31,6 +31,7 @@
private static InjectedServices sInjectedServices;
private AccountTypeManager mAccountTypeManager;
+ private ContactPhotoManager mContactPhotoManager;
/**
* Overrides the system services with mocks for testing.
@@ -82,6 +83,13 @@
return mAccountTypeManager;
}
+ if (ContactPhotoManager.CONTACT_PHOTO_SERVICE.equals(name)) {
+ if (mContactPhotoManager == null) {
+ mContactPhotoManager = ContactPhotoManager.createContactPhotoManager(this);
+ }
+ return mContactPhotoManager;
+ }
+
return super.getSystemService(name);
}
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index e2cec51..a2b264b 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -15,7 +15,7 @@
*/
package com.android.contacts.list;
-import com.android.contacts.ContactPhotoLoader;
+import com.android.contacts.ContactPhotoManager;
import com.android.contacts.R;
import com.android.contacts.widget.IndexerListAdapter;
import com.android.contacts.widget.TextWithHighlightingFactory;
@@ -62,7 +62,7 @@
private boolean mDisplayPhotos;
private boolean mQuickContactEnabled;
- private ContactPhotoLoader mPhotoLoader;
+ private ContactPhotoManager mPhotoLoader;
private String mQueryString;
private char[] mUpperCaseQueryString;
@@ -226,11 +226,11 @@
return mTextWithHighlightingFactory;
}
- public void setPhotoLoader(ContactPhotoLoader photoLoader) {
+ public void setPhotoLoader(ContactPhotoManager photoLoader) {
mPhotoLoader = photoLoader;
}
- protected ContactPhotoLoader getPhotoLoader() {
+ protected ContactPhotoManager getPhotoLoader() {
return mPhotoLoader;
}
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 0b9e8b0..c635d1d 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -18,7 +18,7 @@
import com.android.common.widget.CompositeCursorAdapter.Partition;
import com.android.contacts.ContactListEmptyView;
-import com.android.contacts.ContactPhotoLoader;
+import com.android.contacts.ContactPhotoManager;
import com.android.contacts.ContactsSearchManager;
import com.android.contacts.R;
import com.android.contacts.preference.ContactsPreferences;
@@ -125,7 +125,7 @@
private int mDirectoryResultLimit = DEFAULT_DIRECTORY_RESULT_LIMIT;
private ContextMenuAdapter mContextMenuAdapter;
- private ContactPhotoLoader mPhotoLoader;
+ private ContactPhotoManager mPhotoManager;
private ContactListEmptyView mEmptyView;
private ProviderStatusLoader mProviderStatusLoader;
private ContactsPreferences mContactsPrefs;
@@ -712,7 +712,7 @@
boolean searchMode = isSearchMode();
mAdapter.setSearchMode(searchMode);
mAdapter.configureDefaultPartition(false, searchMode);
- mAdapter.setPhotoLoader(mPhotoLoader);
+ mAdapter.setPhotoLoader(mPhotoManager);
mListView.setAdapter(mAdapter);
if (!isSearchMode()) {
@@ -763,14 +763,14 @@
protected void configurePhotoLoader() {
if (isPhotoLoaderEnabled() && mContext != null) {
- if (mPhotoLoader == null) {
- mPhotoLoader = new ContactPhotoLoader(mContext, R.drawable.ic_contact_picture);
+ if (mPhotoManager == null) {
+ mPhotoManager = ContactPhotoManager.getInstance(mContext);
}
if (mListView != null) {
mListView.setOnScrollListener(this);
}
if (mAdapter != null) {
- mAdapter.setPhotoLoader(mPhotoLoader);
+ mAdapter.setPhotoLoader(mPhotoManager);
}
}
}
@@ -814,29 +814,12 @@
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {
- mPhotoLoader.pause();
+ mPhotoManager.pause();
} else if (isPhotoLoaderEnabled()) {
- mPhotoLoader.resume();
+ mPhotoManager.resume();
}
}
- @Override
- public void onResume() {
- super.onResume();
-
- if (isPhotoLoaderEnabled()) {
- mPhotoLoader.resume();
- }
- }
-
- @Override
- public void onDestroy() {
- if (isPhotoLoaderEnabled()) {
- mPhotoLoader.stop();
- }
- super.onDestroy();
- }
-
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
hideSoftKeyboard();