diff options
author | Martin Hibdon <mhibdon@google.com> | 2014-03-26 17:49:07 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-03-26 17:49:07 +0000 |
commit | 1b0675c1abb42c4a9add7178f1cc7a7bacbe76ec (patch) | |
tree | 10fd7c4a9e7cdd0b3d66d6fdeb3bc95e5419c2f0 /src | |
parent | 4f347e811052f446c3958c76db278bcd7b39a44f (diff) | |
parent | c7849b23a73d699b5e7f199f0a3afce5b9dee7a6 (diff) | |
download | android_packages_apps_UnifiedEmail-1b0675c1abb42c4a9add7178f1cc7a7bacbe76ec.tar.gz android_packages_apps_UnifiedEmail-1b0675c1abb42c4a9add7178f1cc7a7bacbe76ec.tar.bz2 android_packages_apps_UnifiedEmail-1b0675c1abb42c4a9add7178f1cc7a7bacbe76ec.zip |
Merge "Change the layout of the account selector" into ub-mail-master
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/mail/adapter/DrawerItem.java | 30 | ||||
-rw-r--r-- | src/com/android/mail/bitmap/AccountAvatarDrawable.java | 194 | ||||
-rw-r--r-- | src/com/android/mail/bitmap/ContactDrawable.java | 5 | ||||
-rw-r--r-- | src/com/android/mail/bitmap/ContactRequest.java | 5 | ||||
-rw-r--r-- | src/com/android/mail/bitmap/ContactResolver.java | 9 | ||||
-rw-r--r-- | src/com/android/mail/ui/AccountItemView.java | 72 | ||||
-rw-r--r-- | src/com/android/mail/ui/FolderListFragment.java | 29 |
7 files changed, 302 insertions, 42 deletions
diff --git a/src/com/android/mail/adapter/DrawerItem.java b/src/com/android/mail/adapter/DrawerItem.java index faea689c4..7c5771e1c 100644 --- a/src/com/android/mail/adapter/DrawerItem.java +++ b/src/com/android/mail/adapter/DrawerItem.java @@ -22,7 +22,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; +import com.android.bitmap.BitmapCache; import com.android.mail.R; +import com.android.mail.bitmap.ContactResolver; import com.android.mail.providers.Account; import com.android.mail.providers.Folder; import com.android.mail.ui.AccountItemView; @@ -32,6 +34,7 @@ import com.android.mail.utils.FolderUri; import com.android.mail.utils.LogTag; import com.android.mail.utils.LogUtils; + /** * An element that is shown in the {@link com.android.mail.ui.FolderListFragment}. This class is * only used for elements that are shown in the {@link com.android.mail.ui.DrawerFragment}. @@ -94,6 +97,9 @@ public class DrawerItem { /** True if this view is enabled, false otherwise. */ private final boolean mIsEnabled; + private BitmapCache mImagesCache; + private ContactResolver mContactResolver; + @Override public String toString() { switch(mType) { @@ -126,7 +132,8 @@ public class DrawerItem { * @param isCurrentAccount true if this item is the current account */ private DrawerItem(int type, ControllableActivity activity, Folder folder, int folderType, - Account account, int resource, boolean isCurrentAccount) { + Account account, int resource, boolean isCurrentAccount, BitmapCache cache, + ContactResolver contactResolver) { mActivity = activity; mFolder = folder; mFolderType = folderType; @@ -136,6 +143,8 @@ public class DrawerItem { mInflater = LayoutInflater.from(activity.getActivityContext()); mType = type; mIsEnabled = calculateEnabled(); + mImagesCache = cache; + mContactResolver = contactResolver; } /** @@ -149,7 +158,8 @@ public class DrawerItem { */ public static DrawerItem ofFolder(ControllableActivity activity, Folder folder, int folderType) { - return new DrawerItem(VIEW_FOLDER, activity, folder, folderType, null, -1, false); + return new DrawerItem(VIEW_FOLDER, activity, folder, folderType, null, -1, false, + null, null); } private String folderToString() { @@ -172,9 +182,10 @@ public class DrawerItem { * @return a drawer item for the account. */ public static DrawerItem ofAccount(ControllableActivity activity, Account account, - int unreadCount, boolean isCurrentAccount) { + int unreadCount, boolean isCurrentAccount, BitmapCache cache, + ContactResolver contactResolver) { return new DrawerItem(VIEW_ACCOUNT, activity, null, ACCOUNT, account, unreadCount, - isCurrentAccount); + isCurrentAccount, cache, contactResolver); } private String accountToString() { @@ -194,7 +205,8 @@ public class DrawerItem { * @return a drawer item for the header. */ public static DrawerItem ofHeader(ControllableActivity activity, int resource) { - return new DrawerItem(VIEW_HEADER, activity, null, INERT_HEADER, null, resource, false); + return new DrawerItem(VIEW_HEADER, activity, null, INERT_HEADER, null, resource, false, + null, null); } private String headerToString() { @@ -213,7 +225,8 @@ public class DrawerItem { * @return a drawer item with an indeterminate progress indicator. */ public static DrawerItem ofWaitView(ControllableActivity activity) { - return new DrawerItem(VIEW_WAITING_FOR_SYNC, activity, null, INERT_HEADER, null, -1, false); + return new DrawerItem(VIEW_WAITING_FOR_SYNC, activity, null, INERT_HEADER, null, -1, false, + null, null); } private static String waitToString() { @@ -338,9 +351,8 @@ public class DrawerItem { accountItemView = (AccountItemView) mInflater.inflate(R.layout.account_item, parent, false); } - accountItemView.bind(mAccount, mIsSelected, mResource); - View v = accountItemView.findViewById(R.id.account_graphic); - v.setBackgroundColor(mAccount.color); + accountItemView.bind(mActivity.getActivityContext(), mAccount, mIsSelected, mImagesCache, + mContactResolver); return accountItemView; } diff --git a/src/com/android/mail/bitmap/AccountAvatarDrawable.java b/src/com/android/mail/bitmap/AccountAvatarDrawable.java new file mode 100644 index 000000000..ad658b3aa --- /dev/null +++ b/src/com/android/mail/bitmap/AccountAvatarDrawable.java @@ -0,0 +1,194 @@ +package com.android.mail.bitmap; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.BitmapShader; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Rect; +import android.graphics.Shader.TileMode; +import android.graphics.drawable.Drawable; + +import com.android.bitmap.BitmapCache; +import com.android.bitmap.ReusableBitmap; +import com.android.bitmap.DecodeTask.Request; +import com.android.mail.R; +import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface; + +public class AccountAvatarDrawable extends Drawable implements ContactDrawableInterface { + + private final BitmapCache mCache; + private final ContactResolver mContactResolver; + + private ContactRequest mContactRequest; + private ReusableBitmap mBitmap; + private float mBorderWidth; + private final Paint mBitmapPaint; + private final Paint mBorderPaint; + private final Matrix mMatrix; + + private static Bitmap DEFAULT_AVATAR = null; + + public AccountAvatarDrawable(final Resources res, final BitmapCache cache, + final ContactResolver contactResolver) { + mCache = cache; + mContactResolver = contactResolver; + mBitmapPaint = new Paint(); + mBitmapPaint.setAntiAlias(true); + mBitmapPaint.setFilterBitmap(true); + mBitmapPaint.setDither(true); + + mBorderPaint = new Paint(); + mBorderPaint.setColor(Color.TRANSPARENT); + mBorderPaint.setStyle(Style.STROKE); + mBorderPaint.setStrokeWidth(mBorderWidth); + mBorderPaint.setAntiAlias(true); + + mBorderWidth = res.getDimensionPixelSize(R.dimen.avatar_border_width); + mMatrix = new Matrix(); + + if (DEFAULT_AVATAR == null) { + DEFAULT_AVATAR = BitmapFactory.decodeResource(res, R.drawable.avatar_placeholder_gray); + } + } + + @Override + public void draw(final Canvas canvas) { + final Rect bounds = getBounds(); + if (!isVisible() || bounds.isEmpty()) { + return; + } + + if (mBitmap != null && mBitmap.bmp != null) { + // Draw sender image. + drawBitmap(mBitmap.bmp, mBitmap.getLogicalWidth(), mBitmap.getLogicalHeight(), canvas); + } else { + // Draw the default image + drawBitmap(DEFAULT_AVATAR, DEFAULT_AVATAR.getWidth(), DEFAULT_AVATAR.getHeight(), + canvas); + } + } + + /** + * Draw the bitmap onto the canvas at the current bounds taking into account the current scale. + */ + private void drawBitmap(final Bitmap bitmap, final int width, final int height, + final Canvas canvas) { + Rect bounds = getBounds(); + // Draw bitmap through shader first. + BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, + TileMode.CLAMP); + mMatrix.reset(); + + // Fit bitmap to bounds. + float scale = Math.max((float) bounds.width() / width, + (float) bounds.height() / height); + mMatrix.postScale(scale, scale); + + // Translate bitmap to dst bounds. + mMatrix.postTranslate(bounds.left, bounds.top); + + shader.setLocalMatrix(mMatrix); + mBitmapPaint.setShader(shader); + int oldAlpha = mBitmapPaint.getAlpha(); + mBitmapPaint.setAlpha((int) (oldAlpha * 1f)); + canvas.drawCircle(bounds.centerX(), bounds.centerY(), bounds.width() / 2, + mBitmapPaint); + mBitmapPaint.setAlpha(oldAlpha); + + // Then draw the border. + canvas.drawCircle(bounds.centerX(), bounds.centerY(), + bounds.width() / 2f - mBorderWidth / 2, mBorderPaint); + } + + @Override + public void setAlpha(final int alpha) { + mBitmapPaint.setAlpha(alpha); + } + + @Override + public void setColorFilter(final ColorFilter cf) { + mBitmapPaint.setColorFilter(cf); + } + + @Override + public int getOpacity() { + return 0; + } + + public void setDecodeDimensions(final int decodeWidth, final int decodeHeight) { + mCache.setPoolDimensions(decodeWidth, decodeHeight); + } + + public void unbind() { + setImage(null); + } + + public void bind(final String name, final String email) { + setImage(new ContactRequest(name, email)); + } + + private void setImage(final ContactRequest contactRequest) { + if (mContactRequest != null && mContactRequest.equals(contactRequest)) { + return; + } + + if (mBitmap != null) { + mBitmap.releaseReference(); + mBitmap = null; + } + + mContactResolver.remove(mContactRequest, this); + mContactRequest = contactRequest; + + if (contactRequest == null) { + invalidateSelf(); + return; + } + + final ReusableBitmap cached = mCache.get(contactRequest, true /* incrementRefCount */); + if (cached != null) { + setBitmap(cached); + } else { + decode(); + } + } + + private void setBitmap(final ReusableBitmap bmp) { + if (mBitmap != null && mBitmap != bmp) { + mBitmap.releaseReference(); + } + mBitmap = bmp; + invalidateSelf(); + } + + private void decode() { + if (mContactRequest == null) { + return; + } + // Add to batch. + mContactResolver.add(mContactRequest, this); + } + + @Override + public void onDecodeComplete(final Request key, final ReusableBitmap result) { + final ContactRequest request = (ContactRequest) key; + // Remove from batch. + mContactResolver.remove(request, this); + if (request.equals(mContactRequest)) { + setBitmap(result); + } else { + // if the requests don't match (i.e. this request is stale), decrement the + // ref count to allow the bitmap to be pooled + if (result != null) { + result.releaseReference(); + } + } + } +} + diff --git a/src/com/android/mail/bitmap/ContactDrawable.java b/src/com/android/mail/bitmap/ContactDrawable.java index 34aa68230..93c8e8670 100644 --- a/src/com/android/mail/bitmap/ContactDrawable.java +++ b/src/com/android/mail/bitmap/ContactDrawable.java @@ -31,6 +31,7 @@ import com.android.bitmap.BitmapCache; import com.android.bitmap.DecodeTask.Request; import com.android.bitmap.ReusableBitmap; import com.android.mail.R; +import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface; /** * A drawable that encapsulates all the functionality needed to display a contact image, @@ -40,7 +41,7 @@ import com.android.mail.R; * <p/> * The actual contact resolving and decoding is handled by {@link ContactResolver}. */ -public class ContactDrawable extends Drawable { +public class ContactDrawable extends Drawable implements ContactDrawableInterface { private final BitmapCache mCache; private final ContactResolver mContactResolver; @@ -110,7 +111,7 @@ public class ContactDrawable extends Drawable { private void drawBitmap(final Bitmap bitmap, final int width, final int height, final Canvas canvas) { final Rect bounds = getBounds(); - + if (mScale != ContactGridDrawable.SCALE_TYPE_HALF) { sRect.set(0, 0, width, height); } else { diff --git a/src/com/android/mail/bitmap/ContactRequest.java b/src/com/android/mail/bitmap/ContactRequest.java index b0bd2a0ad..ebb41de8f 100644 --- a/src/com/android/mail/bitmap/ContactRequest.java +++ b/src/com/android/mail/bitmap/ContactRequest.java @@ -19,6 +19,7 @@ import android.content.res.AssetFileDescriptor; import android.text.TextUtils; import com.android.bitmap.DecodeTask; +import com.android.mail.bitmap.ContactResolver.ContactDrawableInterface; import java.io.ByteArrayInputStream; import java.io.IOException; @@ -112,10 +113,10 @@ public class ContactRequest implements DecodeTask.Request { public static class ContactRequestHolder { public final ContactRequest contactRequest; - public final ContactDrawable destination; + public final ContactDrawableInterface destination; public ContactRequestHolder(final ContactRequest contactRequest, - final ContactDrawable destination) { + final ContactDrawableInterface destination) { this.contactRequest = contactRequest; this.destination = destination; } diff --git a/src/com/android/mail/bitmap/ContactResolver.java b/src/com/android/mail/bitmap/ContactResolver.java index b3d1fd536..228e9bbd7 100644 --- a/src/com/android/mail/bitmap/ContactResolver.java +++ b/src/com/android/mail/bitmap/ContactResolver.java @@ -25,6 +25,7 @@ import android.os.Handler; import com.android.bitmap.BitmapCache; import com.android.bitmap.DecodeTask; import com.android.bitmap.ReusableBitmap; +import com.android.bitmap.DecodeTask.Request; import com.android.ex.photo.util.Trace; import com.android.mail.ContactInfo; import com.android.mail.SenderInfoLoader; @@ -63,6 +64,10 @@ public class ContactResolver implements Runnable { 1, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); private static final Executor EXECUTOR = SMALL_POOL_EXECUTOR; + public interface ContactDrawableInterface { + public void onDecodeComplete(final Request key, final ReusableBitmap result); + } + public ContactResolver(final ContentResolver resolver, final BitmapCache cache) { mResolver = resolver; mCache = cache; @@ -96,12 +101,12 @@ public class ContactResolver implements Runnable { Trace.endSection(); } - public void add(final ContactRequest request, final ContactDrawable drawable) { + public void add(final ContactRequest request, final ContactDrawableInterface drawable) { mBatch.add(new ContactRequestHolder(request, drawable)); notifyBatchReady(); } - public void remove(final ContactRequest request, final ContactDrawable drawable) { + public void remove(final ContactRequest request, final ContactDrawableInterface drawable) { mBatch.remove(new ContactRequestHolder(request, drawable)); } diff --git a/src/com/android/mail/ui/AccountItemView.java b/src/com/android/mail/ui/AccountItemView.java index 04a30d5f4..74b02455e 100644 --- a/src/com/android/mail/ui/AccountItemView.java +++ b/src/com/android/mail/ui/AccountItemView.java @@ -16,23 +16,28 @@ package com.android.mail.ui; import android.content.Context; +import android.graphics.Typeface; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.ImageView; -import android.widget.RelativeLayout; +import android.widget.LinearLayout; import android.widget.TextView; +import com.android.bitmap.BitmapCache; import com.android.mail.R; +import com.android.mail.bitmap.AccountAvatarDrawable; +import com.android.mail.bitmap.ContactResolver; import com.android.mail.providers.Account; -import com.android.mail.utils.Utils; /** * The view for each account in the folder list/drawer. */ -public class AccountItemView extends RelativeLayout { - private TextView mAccountTextView; - private TextView mUnreadCountTextView; - private ImageView mSelectedButton; +public class AccountItemView extends LinearLayout { + private TextView mAccountDisplayName; + private TextView mAccountAddress; + private ImageView mAvatar; + private ImageView mCheckmark; public AccountItemView(Context context) { super(context); @@ -49,9 +54,10 @@ public class AccountItemView extends RelativeLayout { @Override protected void onFinishInflate() { super.onFinishInflate(); - mAccountTextView = (TextView)findViewById(R.id.name); - mUnreadCountTextView = (TextView)findViewById(R.id.unread); - mSelectedButton = (ImageView)findViewById(R.id.account_radio_button); + mAccountDisplayName = (TextView)findViewById(R.id.account_display_name); + mAccountAddress = (TextView)findViewById(R.id.account_address); + mAvatar = (ImageView)findViewById(R.id.avatar); + mCheckmark = (ImageView)findViewById(R.id.checkmark); } /** @@ -61,24 +67,38 @@ public class AccountItemView extends RelativeLayout { * * @param account account whose name will be displayed * @param isCurrentAccount true if the account is the one in use, false otherwise - * @param count unread count */ - public void bind(final Account account, final boolean isCurrentAccount, final int count) { - mAccountTextView.setText(account.getDisplayName()); - setUnreadCount(count); - mUnreadCountTextView.setSelected(isCurrentAccount); - mAccountTextView.setSelected(isCurrentAccount); - mSelectedButton.setSelected(isCurrentAccount); - } - - /** - * Sets the unread count, taking care to hide/show the textview if the count - * is zero/non-zero. - */ - private void setUnreadCount(final int count) { - mUnreadCountTextView.setVisibility(count > 0 ? View.VISIBLE : View.GONE); - if (count > 0) { - mUnreadCountTextView.setText(Utils.getUnreadCountString(getContext(), count)); + public void bind(final Context context, final Account account, final boolean isCurrentAccount, + final BitmapCache imagesCache, final ContactResolver contactResolver) { + if (!TextUtils.isEmpty(account.getSenderName())) { + mAccountDisplayName.setText(account.getSenderName()); + mAccountAddress.setText(account.getEmailAddress()); + mAccountAddress.setVisibility(View.VISIBLE); + } else if (!TextUtils.isEmpty(account.getDisplayName()) && + !TextUtils.equals(account.getDisplayName(), account.getEmailAddress())) { + mAccountDisplayName.setText(account.getDisplayName()); + mAccountAddress.setText(account.getEmailAddress()); + mAccountAddress.setVisibility(View.VISIBLE); + } else { + mAccountDisplayName.setText(account.getEmailAddress()); + mAccountAddress.setVisibility(View.GONE); } + if (isCurrentAccount) { + mCheckmark.setVisibility(View.VISIBLE); + mAccountDisplayName.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); + } else { + mCheckmark.setVisibility(View.GONE); + mAccountDisplayName.setTypeface(Typeface.DEFAULT); + } + + ImageView v = (ImageView) mAvatar.findViewById(R.id.avatar); + AccountAvatarDrawable drawable = new AccountAvatarDrawable( + context.getResources(), imagesCache, contactResolver); + final int size = context.getResources().getDimensionPixelSize( + R.dimen.account_avatar_dimension); + drawable.setDecodeDimensions(size, size); + drawable.bind(account.getSenderName(), account.getEmailAddress()); + v.setImageDrawable(drawable); + } } diff --git a/src/com/android/mail/ui/FolderListFragment.java b/src/com/android/mail/ui/FolderListFragment.java index 9d6fb7715..68bdcaae1 100644 --- a/src/com/android/mail/ui/FolderListFragment.java +++ b/src/com/android/mail/ui/FolderListFragment.java @@ -39,9 +39,12 @@ import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; +import com.android.bitmap.AltBitmapCache; +import com.android.bitmap.BitmapCache; import com.android.mail.R; import com.android.mail.adapter.DrawerItem; import com.android.mail.analytics.Analytics; +import com.android.mail.bitmap.ContactResolver; import com.android.mail.browse.MergedAdapter; import com.android.mail.browse.ScrollIndicatorsView; import com.android.mail.content.ObjectCursor; @@ -149,6 +152,17 @@ public class FolderListFragment extends ListFragment implements private static final String BUNDLE_SELECTED_ITEM_TYPE = "flf-selected-item-type"; private static final String BUNDLE_SELECTED_TYPE = "flf-selected-type"; + /** Number of avatars to we whould like to fit in the avatar cache */ + private static final int IMAGE_CACHE_COUNT = 10; + /** + * This is the fractional portion of the total cache size above that's dedicated to non-pooled + * bitmaps. (This is basically the portion of cache dedicated to GIFs.) + */ + private static final float AVATAR_IMAGES_PREVIEWS_CACHE_NON_POOLED_FRACTION = 0f; + /** Each string has upper estimate of 50 bytes, so this cache would be 5KB. */ + private static final int AVATAR_IMAGES_PREVIEWS_CACHE_NULL_CAPACITY = 100; + + /** Adapter used by the list that wraps both the folder adapter and the accounts adapter. */ private MergedAdapter<ListAdapter> mMergedAdapter; /** Adapter containing the list of accounts. */ @@ -193,6 +207,9 @@ public class FolderListFragment extends ListFragment implements private static final Interpolator INTERPOLATOR_SHOW_FLOATY = new DecelerateInterpolator(2.0f); private static final Interpolator INTERPOLATOR_HIDE_FLOATY = new DecelerateInterpolator(); + private BitmapCache mImagesCache; + private ContactResolver mContactResolver; + /** * Constructor needs to be public to handle orientation changes and activity lifecycle events. */ @@ -383,6 +400,16 @@ public class FolderListFragment extends ListFragment implements mFolderWatcher.updateAccountList(getAllAccounts()); setListAdapter(mMergedAdapter); + + final int avatarSize = getActivity().getResources().getDimensionPixelSize( + R.dimen.account_avatar_dimension); + + mImagesCache = new AltBitmapCache(Utils.isLowRamDevice(getActivity()) ? + 0 : avatarSize * avatarSize * IMAGE_CACHE_COUNT, + AVATAR_IMAGES_PREVIEWS_CACHE_NON_POOLED_FRACTION, + AVATAR_IMAGES_PREVIEWS_CACHE_NULL_CAPACITY); + mContactResolver = new ContactResolver(getActivity().getContentResolver(), + mImagesCache); } /** @@ -1124,7 +1151,7 @@ public class FolderListFragment extends ListFragment implements for (final Account account : allAccounts) { final int unreadCount = getUnreadCount(account); accountList.add(DrawerItem.ofAccount(mActivity, account, unreadCount, - currentAccountUri.equals(account.uri))); + currentAccountUri.equals(account.uri), mImagesCache, mContactResolver)); } if (mCurrentAccount == null) { LogUtils.wtf(LOG_TAG, "buildAccountList() with null current account."); |