From 765734c1d602c9a6d166d653b3684e6408b771c4 Mon Sep 17 00:00:00 2001 From: Yorke Lee Date: Mon, 10 Feb 2014 16:09:12 -0800 Subject: Switch favorites screen to grid layout * Add logic to PhoneFavoritesTileAdapter so that it now supports an unlimited number of tiled rows. * Tiles now have a configurable height to width ratio. * Fix animations so that tiles moving up and down rows appear to animate in from the correct direction. Tiles moving to the row above should animate in from right to left. Tiles moving to the row below should animate in from left to right. * Update the number of columns in the grid to 2. * Update layout of individual tiles to match redlines from UX. * Tweak font sizes for tiles * No longer truncate names in tiles * Tiles have a 2-3 height to width ratio * Update assets and layout for favorite and more info icons * Add content description for the favorite button * Add tests for PhoneFavoritesTileAdapter Change-Id: I50b298f0941698985d281f13e6a87c5a9b613efa --- .../android/dialer/list/PhoneFavoriteFragment.java | 53 +++++++++++++++++----- .../dialer/list/PhoneFavoriteSquareTileView.java | 12 ----- .../dialer/list/PhoneFavoritesTileAdapter.java | 53 ++++++++++++++-------- 3 files changed, 74 insertions(+), 44 deletions(-) (limited to 'src/com/android') diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java index 2791f1569..027674a96 100644 --- a/src/com/android/dialer/list/PhoneFavoriteFragment.java +++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java @@ -17,9 +17,7 @@ package com.android.dialer.list; import android.animation.Animator; import android.animation.AnimatorSet; -import android.animation.ArgbEvaluator; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.app.Activity; import android.app.Fragment; import android.app.LoaderManager; @@ -36,9 +34,9 @@ import android.provider.CallLog; import android.util.Log; import android.view.LayoutInflater; import android.view.View; -import android.view.ViewTreeObserver; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; @@ -55,10 +53,10 @@ import com.android.contacts.common.list.ContactListItemView; import com.android.contacts.common.list.ContactTileView; import com.android.dialer.DialtactsActivity; import com.android.dialer.R; -import com.android.dialer.calllog.CallLogQuery; -import com.android.dialer.calllog.ContactInfoHelper; import com.android.dialer.calllog.CallLogAdapter; +import com.android.dialer.calllog.CallLogQuery; import com.android.dialer.calllog.CallLogQueryHandler; +import com.android.dialer.calllog.ContactInfoHelper; import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow; import com.android.dialerbind.ObjectFactory; @@ -238,8 +236,8 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen // that will be available on onCreateView(). mContactTileAdapter = new PhoneFavoritesTileAdapter(activity, mContactTileAdapterListener, this, - getResources().getInteger(R.integer.contact_tile_column_count_in_favorites_new), - 1); + getResources().getInteger(R.integer.contact_tile_column_count_in_favorites), + PhoneFavoritesTileAdapter.NO_ROW_LIMIT); mContactTileAdapter.setPhotoLoader(ContactPhotoManager.getInstance(activity)); } @@ -424,7 +422,10 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen } /** - * Saves the current view offsets into memory + * Cache the current view offsets into memory. Once a relayout of views in the ListView + * has happened due to a dataset change, the cached offsets are used to create animations + * that slide views from their previous positions to their new ones, to give the appearance + * that the views are sliding into their new positions. */ @SuppressWarnings("unchecked") private void saveOffsets(int removedItemHeight) { @@ -440,7 +441,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen if (itemViewType == PhoneFavoritesTileAdapter.ViewTypes.TOP) { // This is a tiled row, so save horizontal offsets instead saveHorizontalOffsets((ContactTileRow) child, (ArrayList) - mAdapter.getItem(position)); + mAdapter.getItem(position), position); } if (DEBUG) { Log.d(TAG, "Saving itemId: " + itemId + " for listview child " + i + " Top: " @@ -452,7 +453,13 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen mItemIdTopMap.put(KEY_REMOVED_ITEM_HEIGHT, removedItemHeight); } - private void saveHorizontalOffsets(ContactTileRow row, ArrayList list) { + /** + * Saves the horizontal offsets for contacts that are displayed as tiles in a row. Saving + * these offsets allow us to animate tiles sliding left and right within the same row. + * See {@link #saveOffsets(int removedItemHeight)} + */ + private void saveHorizontalOffsets(ContactTileRow row, ArrayList list, + int currentRowIndex) { for (int i = 0; i < list.size() && i < row.getChildCount(); i++) { final View child = row.getChildAt(i); if (child == null) { @@ -464,6 +471,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen Log.d(TAG, "Saving itemId: " + itemId + " for tileview child " + i + " Left: " + child.getTop()); } + mItemIdTopMap.put(itemId, currentRowIndex); mItemIdLeftMap.put(itemId, child.getLeft()); } } @@ -472,7 +480,7 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen * Performs a animations for a row of tiles */ private void performHorizontalAnimations(ContactTileRow row, ArrayList list, - long[] idsInPlace) { + long[] idsInPlace, int currentRow) { if (mItemIdLeftMap.isEmpty()) { return; } @@ -490,6 +498,26 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen } else { Integer startLeft = mItemIdLeftMap.get(itemId); int left = child.getLeft(); + + Integer startRow = mItemIdTopMap.get(itemId); + + if (startRow != null) { + if (startRow > currentRow) { + // Item has shifted upwards to the previous row. + // It should now animate in from right to left. + startLeft = left + child.getWidth(); + } else if (startRow < currentRow) { + // Item has shifted downwards to the next row. + // It should now animate in from left to right. + startLeft = left - child.getWidth(); + } + + // If the item hasn't shifted rows (startRow == currentRow), it either remains + // in the same position or has shifted left or right within its current row. + // Either way, startLeft has already been correctly saved and retrieved from + // mItemIdTopMap. + } + if (startLeft != null) { if (startLeft != left) { int delta = startLeft - left; @@ -545,7 +573,8 @@ public class PhoneFavoriteFragment extends Fragment implements OnItemClickListen child instanceof ContactTileRow) { // This is a tiled row, so perform horizontal animations instead performHorizontalAnimations((ContactTileRow) child, ( - ArrayList) mAdapter.getItem(position), idsInPlace); + ArrayList) mAdapter.getItem(position), idsInPlace, + position); } final long itemId = mAdapter.getItemId(position); diff --git a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java index 85e721619..e855c88c5 100644 --- a/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java +++ b/src/com/android/dialer/list/PhoneFavoriteSquareTileView.java @@ -36,10 +36,6 @@ public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView { private static final String TAG = PhoneFavoriteSquareTileView.class.getSimpleName(); private ImageButton mSecondaryButton; - // TODO: Use a more expansive name token separator if needed. For now it should be fine to - // not split by dashes, underscore etc. - private static final Pattern NAME_TOKEN_SEPARATOR_PATTERN = Pattern.compile("\\s+"); - public PhoneFavoriteSquareTileView(Context context, AttributeSet attrs) { super(context, attrs); } @@ -62,14 +58,6 @@ public class PhoneFavoriteSquareTileView extends PhoneFavoriteTileView { getLookupUri(), QuickContact.MODE_LARGE, null); } - @Override - protected String getNameForView(String name) { - if (TextUtils.isEmpty(name)) return name; - final String[] tokens = NAME_TOKEN_SEPARATOR_PATTERN.split(name, 2); - if (tokens.length < 1) return name; - return tokens[0]; - } - @Override public void loadFromContact(ContactEntry entry) { super.loadFromContact(entry); diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java index a85fc6a60..dff68b2be 100644 --- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java +++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java @@ -15,7 +15,9 @@ */ package com.android.dialer.list; -import android.animation.ObjectAnimator; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ComparisonChain; + import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; @@ -44,9 +46,6 @@ import com.android.contacts.common.list.ContactTileView; import com.android.dialer.list.SwipeHelper.OnItemGestureListener; import com.android.dialer.list.SwipeHelper.SwipeHelperCallback; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.collect.ComparisonChain; - import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedList; @@ -64,7 +63,9 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName(); private static final boolean DEBUG = false; - public static final int ROW_LIMIT_DEFAULT = 1; + public static final int NO_ROW_LIMIT = -1; + + public static final int ROW_LIMIT_DEFAULT = NO_ROW_LIMIT; private ContactTileView.Listener mListener; private OnDataSetChangedForAnimationListener mDataSetChangedListener; @@ -155,7 +156,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements mContactEntries = new ArrayList(); // Converting padding in dips to padding in pixels mPaddingInPixels = mContext.getResources() - .getDimensionPixelSize(R.dimen.contact_tile_divider_padding); + .getDimensionPixelSize(R.dimen.contact_tile_divider_width); bindColumnIndices(); } @@ -386,18 +387,24 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements protected int getRowCount(int entryCount) { if (entryCount == 0) return 0; final int nonLimitedRows = ((entryCount - 1) / mColumnCount) + 1; + if (mMaxTiledRows == NO_ROW_LIMIT) { + return nonLimitedRows; + } return Math.min(mMaxTiledRows, nonLimitedRows); } private int getMaxContactsInTiles() { + if (mMaxTiledRows == NO_ROW_LIMIT) { + return Integer.MAX_VALUE; + } return mColumnCount * mMaxTiledRows; } public int getRowIndex(int entryIndex) { - if (entryIndex < mMaxTiledRows * mColumnCount) { + if (entryIndex < getMaxContactsInTiles()) { return entryIndex / mColumnCount; } else { - return entryIndex - mMaxTiledRows * mColumnCount + mMaxTiledRows; + return entryIndex - mMaxTiledRows * (mColumnCount + 1); } } @@ -467,9 +474,13 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements } /** - * Calculates the stable itemId for a particular entry based on its contactID + * Calculates the stable itemId for a particular entry based on the entry's contact ID. This + * stable itemId is used for animation purposes. */ public long getAdjustedItemId(long id) { + if (mMaxTiledRows == NO_ROW_LIMIT) { + return id; + } return mMaxTiledRows + id; } @@ -479,7 +490,6 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements } @Override - public boolean areAllItemsEnabled() { // No dividers, so all items are enabled. return true; @@ -541,7 +551,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements @Override public int getItemViewType(int position) { - if (position < getRowCount(getMaxContactsInTiles())) { + if (position < getMaxContactsInTiles()) { return ViewTypes.TOP; } else { return ViewTypes.FREQUENT; @@ -700,6 +710,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements private final int mRowPaddingEnd; private final int mRowPaddingTop; private final int mRowPaddingBottom; + private final float mHeightToWidthRatio; private int mPosition; private SwipeHelper mSwipeHelper; private OnItemGestureListener mOnItemSwipeListener; @@ -712,6 +723,9 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements final Resources resources = mContext.getResources(); + mHeightToWidthRatio = getResources().getFraction( + R.dimen.contact_tile_height_to_width_ratio, 1, 1); + if (mItemViewType == ViewTypes.TOP) { // For tiled views, we still want padding to be set on the ContactTileRow. // Otherwise the padding would be set around each of the tiles, which we don't want @@ -723,8 +737,6 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements R.dimen.favorites_row_start_padding); mRowPaddingEnd = resources.getDimensionPixelSize( R.dimen.favorites_row_end_padding); - - setBackgroundResource(R.drawable.bottom_border_background); } else { // For row views, padding is set on the view itself. mRowPaddingTop = 0; @@ -894,21 +906,22 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements // Preferred width / height for images (excluding the padding). // The actual width may be 1 pixel larger than this if we have a remainder. - final int imageSize = (width - totalPaddingsInPixels) / mColumnCount; - final int remainder = width - (imageSize * mColumnCount) - totalPaddingsInPixels; + final int imageWidth = (width - totalPaddingsInPixels) / mColumnCount; + final int remainder = width - (imageWidth * mColumnCount) - totalPaddingsInPixels; + + final int height = (int) (mHeightToWidthRatio * imageWidth); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); - final int childWidth = imageSize + child.getPaddingRight() + final int childWidth = imageWidth + child.getPaddingRight() // Compensate for the remainder + (i < remainder ? 1 : 0); - final int childHeight = imageSize; child.measure( MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY) + MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY) ); } - setMeasuredDimension(width, imageSize + getPaddingTop() + getPaddingBottom()); + setMeasuredDimension(width, height + getPaddingTop() + getPaddingBottom()); } /** @@ -919,7 +932,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements * @return Index of the selected item in the cached array. */ public int getItemIndex(float itemX, float itemY) { - if (mPosition < mMaxTiledRows) { + if (mMaxTiledRows == NO_ROW_LIMIT || mPosition < mMaxTiledRows) { if (DEBUG) { Log.v(TAG, String.valueOf(itemX) + " " + String.valueOf(itemY)); } -- cgit v1.2.3