Fix issues with photo selection on Prime.
1. Per Marco's request, we no longer expand the photo in landscape
on phone.
2. Passing the full bitmap through the intent appears to be a no-no,
which was exposed in the "black-screen" behavior when clicking on
a default avatar photo on Prime. To handle that, we now retrieve
the photo from the default avatar resource if no URI is specified,
and we retrieve the bitmap for real photos through the photo
manager cache. To ensure performance, I've added a method to the
cache to force caching a given bitmap/byte[] for a photo URI.
Bug 5703659
Bug 5703700
Change-Id: I316f27c7ded575dd1bb00c0bb2e2c75917df1e19
diff --git a/src/com/android/contacts/ContactPhotoManager.java b/src/com/android/contacts/ContactPhotoManager.java
index 7c54e80..f53766d 100644
--- a/src/com/android/contacts/ContactPhotoManager.java
+++ b/src/com/android/contacts/ContactPhotoManager.java
@@ -177,6 +177,14 @@
public abstract void refreshCache();
/**
+ * Stores the given bitmap directly in the LRU bitmap cache.
+ * @param photoUri The URI of the photo (for future requests).
+ * @param bitmap The bitmap.
+ * @param photoBytes The bytes that were parsed to create the bitmap.
+ */
+ public abstract void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes);
+
+ /**
* Initiates a background process that over time will fill up cache with
* preload photos.
*/
@@ -649,6 +657,14 @@
mBitmapHolderCache.put(key, holder);
}
+ @Override
+ public void cacheBitmap(Uri photoUri, Bitmap bitmap, byte[] photoBytes) {
+ Request request = Request.createFromUri(photoUri, true, false, DEFAULT_AVATER);
+ BitmapHolder holder = new BitmapHolder(photoBytes);
+ mBitmapHolderCache.put(request.getKey(), holder);
+ mBitmapCache.put(request, bitmap);
+ }
+
/**
* Populates an array of photo IDs that need to be loaded.
*/
diff --git a/src/com/android/contacts/activities/PhotoSelectionActivity.java b/src/com/android/contacts/activities/PhotoSelectionActivity.java
index 73a85eb..de129cb 100644
--- a/src/com/android/contacts/activities/PhotoSelectionActivity.java
+++ b/src/com/android/contacts/activities/PhotoSelectionActivity.java
@@ -15,6 +15,7 @@
*/
package com.android.contacts.activities;
+import com.android.contacts.ContactPhotoManager;
import com.android.contacts.ContactSaveService;
import com.android.contacts.R;
import com.android.contacts.detail.PhotoSelectionHandler;
@@ -30,6 +31,7 @@
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
@@ -58,8 +60,8 @@
private static final String KEY_SUB_ACTIVITY_IN_PROGRESS = "subinprogress";
- /** Intent extra to get the photo bitmap. */
- public static final String PHOTO_BITMAP = "photo_bitmap";
+ /** Intent extra to get the photo URI. */
+ public static final String PHOTO_URI = "photo_uri";
/** Intent extra to get the entity delta list. */
public static final String ENTITY_DELTA_LIST = "entity_delta_list";
@@ -80,8 +82,10 @@
/** Source bounds of the image that was clicked on. */
private Rect mSourceBounds;
- /** The photo bitmap. */
- private Bitmap mPhotoBitmap;
+ /**
+ * The photo URI. May be null, in which case the default avatar will be used.
+ */
+ private Uri mPhotoUri;
/** Entity delta list of the contact. */
private EntityDeltaList mState;
@@ -149,7 +153,7 @@
// Pull data out of the intent.
final Intent intent = getIntent();
- mPhotoBitmap = intent.getParcelableExtra(PHOTO_BITMAP);
+ mPhotoUri = intent.getParcelableExtra(PHOTO_URI);
mState = (EntityDeltaList) intent.getParcelableExtra(ENTITY_DELTA_LIST);
mIsProfile = intent.getBooleanExtra(IS_PROFILE, false);
mIsDirectoryContact = intent.getBooleanExtra(IS_DIRECTORY_CONTACT, false);
@@ -193,7 +197,12 @@
/**
* Builds a well-formed intent for invoking this activity.
* @param context The context.
- * @param photoBitmap The bitmap of the current photo.
+ * @param photoUri The URI of the current photo (may be null, in which case the default
+ * avatar image will be displayed).
+ * @param photoBitmap The bitmap of the current photo (may be null, in which case the default
+ * avatar image will be displayed).
+ * @param photoBytes The bytes for the current photo (may be null, in which case the default
+ * avatar image will be displayed).
* @param photoBounds The pixel bounds of the current photo.
* @param delta The entity delta list for the contact.
* @param isProfile Whether the contact is the user's profile.
@@ -202,11 +211,13 @@
* this should be true for phones, and false for tablets).
* @return An intent that can be used to invoke the photo selection activity.
*/
- public static Intent buildIntent(Context context, Bitmap photoBitmap, Rect photoBounds,
- EntityDeltaList delta, boolean isProfile, boolean isDirectoryContact,
- boolean expandPhotoOnClick) {
+ public static Intent buildIntent(Context context, Uri photoUri, Bitmap photoBitmap,
+ byte[] photoBytes, Rect photoBounds, EntityDeltaList delta, boolean isProfile,
+ boolean isDirectoryContact, boolean expandPhotoOnClick) {
Intent intent = new Intent(context, PhotoSelectionActivity.class);
- intent.putExtra(PHOTO_BITMAP, photoBitmap);
+ if (photoUri != null && photoBitmap != null && photoBytes != null) {
+ intent.putExtra(PHOTO_URI, photoUri);
+ }
intent.setSourceBounds(photoBounds);
intent.putExtra(ENTITY_DELTA_LIST, (Parcelable) delta);
intent.putExtra(IS_PROFILE, isProfile);
@@ -233,48 +244,55 @@
}
private void displayPhoto() {
- if (mPhotoBitmap != null) {
- final int[] pos = new int[2];
- mBackdrop.getLocationOnScreen(pos);
- LayoutParams layoutParams = new LayoutParams(mSourceBounds.width(),
- mSourceBounds.height());
- mOriginalPos.left = mSourceBounds.left - pos[0];
- mOriginalPos.top = mSourceBounds.top - pos[1];
- mOriginalPos.right = mOriginalPos.left + mSourceBounds.width();
- mOriginalPos.bottom = mOriginalPos.top + mSourceBounds.height();
- layoutParams.setMargins(mOriginalPos.left, mOriginalPos.top, mOriginalPos.right,
- mOriginalPos.bottom);
- mPhotoStartParams = layoutParams;
- mPhotoView.setLayoutParams(layoutParams);
- mPhotoView.requestLayout();
-
- mPhotoView.setImageBitmap(mPhotoBitmap);
- mPhotoView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right, int bottom,
- int oldLeft, int oldTop, int oldRight, int oldBottom) {
- if (mAnimationPending) {
- mAnimationPending = false;
- PropertyValuesHolder pvhLeft =
- PropertyValuesHolder.ofInt("left", mOriginalPos.left, left);
- PropertyValuesHolder pvhTop =
- PropertyValuesHolder.ofInt("top", mOriginalPos.top, top);
- PropertyValuesHolder pvhRight =
- PropertyValuesHolder.ofInt("right", mOriginalPos.right, right);
- PropertyValuesHolder pvhBottom =
- PropertyValuesHolder.ofInt("bottom", mOriginalPos.bottom, bottom);
- ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mPhotoView,
- pvhLeft, pvhTop, pvhRight, pvhBottom).setDuration(
- PHOTO_EXPAND_DURATION);
- if (mAnimationListener != null) {
- anim.addListener(mAnimationListener);
- }
- anim.start();
- }
- }
- });
- attachPhotoHandler();
+ // Load the photo.
+ if (mPhotoUri != null) {
+ // If we have a URI, the bitmap should be cached directly.
+ ContactPhotoManager.getInstance(this).loadPhoto(mPhotoView, mPhotoUri, true, false);
+ } else {
+ // Fall back to avatar image.
+ mPhotoView.setImageResource(ContactPhotoManager.getDefaultAvatarResId(true, false));
}
+
+ // Animate the photo view into its end location.
+ final int[] pos = new int[2];
+ mBackdrop.getLocationOnScreen(pos);
+ LayoutParams layoutParams = new LayoutParams(mSourceBounds.width(),
+ mSourceBounds.height());
+ mOriginalPos.left = mSourceBounds.left - pos[0];
+ mOriginalPos.top = mSourceBounds.top - pos[1];
+ mOriginalPos.right = mOriginalPos.left + mSourceBounds.width();
+ mOriginalPos.bottom = mOriginalPos.top + mSourceBounds.height();
+ layoutParams.setMargins(mOriginalPos.left, mOriginalPos.top, mOriginalPos.right,
+ mOriginalPos.bottom);
+ mPhotoStartParams = layoutParams;
+ mPhotoView.setLayoutParams(layoutParams);
+ mPhotoView.requestLayout();
+
+ mPhotoView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ if (mAnimationPending) {
+ mAnimationPending = false;
+ PropertyValuesHolder pvhLeft =
+ PropertyValuesHolder.ofInt("left", mOriginalPos.left, left);
+ PropertyValuesHolder pvhTop =
+ PropertyValuesHolder.ofInt("top", mOriginalPos.top, top);
+ PropertyValuesHolder pvhRight =
+ PropertyValuesHolder.ofInt("right", mOriginalPos.right, right);
+ PropertyValuesHolder pvhBottom =
+ PropertyValuesHolder.ofInt("bottom", mOriginalPos.bottom, bottom);
+ ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(mPhotoView,
+ pvhLeft, pvhTop, pvhRight, pvhBottom).setDuration(
+ PHOTO_EXPAND_DURATION);
+ if (mAnimationListener != null) {
+ anim.addListener(mAnimationListener);
+ }
+ anim.start();
+ }
+ }
+ });
+ attachPhotoHandler();
}
private LayoutParams getPhotoEndParams() {
diff --git a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
index 588e6ff..912d7fb 100644
--- a/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
+++ b/src/com/android/contacts/detail/ContactDetailDisplayUtils.java
@@ -29,7 +29,6 @@
import com.android.contacts.util.StreamItemPhotoEntry;
import com.google.common.annotations.VisibleForTesting;
-import android.app.Activity;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
@@ -45,7 +44,6 @@
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
-import android.os.Parcelable;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.Data;
@@ -228,7 +226,7 @@
// Set up the photo to display a full-screen photo selection activity when clicked.
OnClickListener clickListener = new PhotoClickListener(context, contactData, bitmap,
- expandPhotoOnClick);
+ photo, expandPhotoOnClick);
photoView.setOnClickListener(clickListener);
return clickListener;
}
@@ -238,12 +236,14 @@
private final Context mContext;
private final Result mContactData;
private final Bitmap mPhotoBitmap;
+ private final byte[] mPhotoBytes;
private final boolean mExpandPhotoOnClick;
public PhotoClickListener(Context context, Result contactData, Bitmap photoBitmap,
- boolean expandPhotoOnClick) {
+ byte[] photoBytes, boolean expandPhotoOnClick) {
mContext = context;
mContactData = contactData;
mPhotoBitmap = photoBitmap;
+ mPhotoBytes = photoBytes;
mExpandPhotoOnClick = expandPhotoOnClick;
}
@@ -266,9 +266,16 @@
rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+ Uri photoUri = null;
+ if (mContactData.getPhotoUri() != null) {
+ photoUri = Uri.parse(mContactData.getPhotoUri());
+ }
Intent photoSelectionIntent = PhotoSelectionActivity.buildIntent(mContext,
- mPhotoBitmap, rect, delta, mContactData.isUserProfile(),
+ photoUri, mPhotoBitmap, mPhotoBytes, rect, delta, mContactData.isUserProfile(),
mContactData.isDirectoryEntry(), mExpandPhotoOnClick);
+ // Cache the bitmap directly, so the activity can pull it from the photo manager.
+ ContactPhotoManager.getInstance(mContext).cacheBitmap(photoUri, mPhotoBitmap,
+ mPhotoBytes);
mContext.startActivity(photoSelectionIntent);
}
}
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index 577062e..84e1d0c 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -454,7 +454,7 @@
mStaticPhotoContainer.setVisibility(View.VISIBLE);
ImageView photoView = (ImageView) mStaticPhotoContainer.findViewById(R.id.photo);
OnClickListener listener = ContactDetailDisplayUtils.setPhoto(mContext,
- mContactData, photoView, !PhoneCapabilityTester.isUsingTwoPanes(mContext));
+ mContactData, photoView, false);
if (mPhotoTouchOverlay != null) {
mPhotoTouchOverlay.setVisibility(View.VISIBLE);
mPhotoTouchOverlay.setOnClickListener(listener);