diff options
5 files changed, 177 insertions, 103 deletions
diff --git a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java index a976ead8c..f0e97ac0b 100644 --- a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java +++ b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java @@ -48,6 +48,40 @@ public class PhoneFavoriteDragAndDropListeners { mTileAdapter = tileAdapter; } + /** + * @return The item index in {@link #mTileAdapter} for the given {@link DragEvent}. + * Returns -1 if {@link #mTileAdapter} is not in dragging or index can not be found. + */ + private int getDragItemIndex(DragEvent event) { + int itemIndex = -1; + if (mTileAdapter != null && mContactTileRow != null + && !mTileAdapter.getInDragging()) { + mX = event.getX(); + mY = event.getY(); + if (DEBUG) { + Log.v(TAG, String.valueOf(mX) + "; " + String.valueOf(mY)); + } + + final int[] rowLocation = new int[2]; + mContactTileRow.getLocationOnScreen(rowLocation); + + final Rect locationRect = new Rect(rowLocation[0], rowLocation[1], + rowLocation[0] + mContactTileRow.getWidth(), + rowLocation[1] + mContactTileRow.getHeight()); + + if (locationRect.contains((int) mX, (int) mY)) { + // Finds out which item is being dragged. + // Computes relative coordinates as we get absolute coordinates. + itemIndex = mContactTileRow.getItemIndex( + mX - rowLocation[0], mY - rowLocation[1]); + if (DEBUG) { + Log.v(TAG, "Start dragging " + String.valueOf(itemIndex)); + } + } + } + return itemIndex; + } + @Override public boolean onDrag(View v, DragEvent event) { if (DEBUG) { @@ -56,35 +90,13 @@ public class PhoneFavoriteDragAndDropListeners { // Handles drag events. switch (event.getAction()) { case DragEvent.ACTION_DRAG_STARTED: - if (mTileAdapter != null && mContactTileRow != null - && !mTileAdapter.getInDragging()) { - mX = event.getX(); - mY = event.getY(); - if (DEBUG) { - Log.v(TAG, String.valueOf(mX) + "; " + String.valueOf(mY)); - } + final int itemIndex = getDragItemIndex(event); + if (itemIndex != -1) { + // Indicates a drag has started. + mTileAdapter.setInDragging(true); - final int[] rowLocation = new int[2]; - mContactTileRow.getLocationOnScreen(rowLocation); - - final Rect locationRect = new Rect(rowLocation[0], rowLocation[1], - rowLocation[0] + mContactTileRow.getWidth(), - rowLocation[1] + mContactTileRow.getHeight()); - - if (locationRect.contains((int) mX, (int) mY)) { - // Finds out which item is being dragged. - // Computes relative coordinates as we get absolute coordinates. - final int dragIndex = mContactTileRow.getItemIndex( - mX - rowLocation[0], mY - rowLocation[1]); - if (DEBUG) { - Log.v(TAG, "Start dragging " + String.valueOf(dragIndex)); - } - // Indicates a drag has started. - mTileAdapter.setInDragging(true); - - // Temporarily pops out the Contact entry. - mTileAdapter.popContactEntry(dragIndex); - } + // Temporarily pops out the Contact entry. + mTileAdapter.popContactEntry(itemIndex); } break; case DragEvent.ACTION_DRAG_ENTERED: @@ -92,33 +104,21 @@ public class PhoneFavoriteDragAndDropListeners { case DragEvent.ACTION_DRAG_EXITED: break; case DragEvent.ACTION_DROP: - mX = event.getX(); - mY = event.getY(); - if (DEBUG) { - Log.v(TAG, String.valueOf(mX) + "; " + String.valueOf(mY)); - } - // Indicates a drag has finished. if (mTileAdapter != null && mContactTileRow != null) { mTileAdapter.setInDragging(false); - - // Finds out at which position of the list the Contact is being dropped. - final int dropIndex = mContactTileRow.getItemIndex(mX, mY); - if (DEBUG) { - Log.v(TAG, "Stop dragging " + String.valueOf(dropIndex)); - } - - // Adds the dragged contact to the drop position. - mTileAdapter.dropContactEntry(dropIndex); + // The drop to position has been reported to the adapter + // via {@link DragEvent#ACTION_DRAG_LOCATION} events in ListView. + mTileAdapter.handleDrop(); } break; case DragEvent.ACTION_DRAG_ENDED: - if (mTileAdapter.getInDragging()) { + if (mTileAdapter != null && mTileAdapter.getInDragging()) { // If the drag and drop ends when the drop happens outside of any rows, // we will end the drag here and put the item back to where it was dragged // from before. mTileAdapter.setInDragging(false); - mTileAdapter.dropToUnsupportedView(); + mTileAdapter.handleDrop(); } break; case DragEvent.ACTION_DRAG_LOCATION: diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java index b5da054f9..00f645ab0 100644 --- a/src/com/android/dialer/list/PhoneFavoriteListView.java +++ b/src/com/android/dialer/list/PhoneFavoriteListView.java @@ -25,7 +25,6 @@ import android.util.Log; import android.view.DragEvent; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnDragListener; import android.view.ViewConfiguration; import android.widget.ListView; @@ -40,8 +39,7 @@ import com.android.dialer.list.SwipeHelper.SwipeHelperCallback; * - Swiping, which is borrowed from packages/apps/UnifiedEmail (com.android.mail.ui.Swipeable) * - Drag and drop */ -public class PhoneFavoriteListView extends ListView implements - SwipeHelperCallback, OnDragListener { +public class PhoneFavoriteListView extends ListView implements SwipeHelperCallback { public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName(); @@ -61,6 +59,9 @@ public class PhoneFavoriteListView extends ListView implements private final long SCROLL_HANDLER_DELAY_MILLIS = 5; private final int DRAG_SCROLL_PX_UNIT = 10; + private boolean mIsDragScrollerRunning = false; + private int mTouchDownForDragStartY; + /** * {@link #mTopScrollBound} and {@link mBottomScrollBound} will be * offseted to the top / bottom by {@link #getHeight} * {@link #BOUND_GAP_RATIO} pixels. @@ -94,7 +95,6 @@ public class PhoneFavoriteListView extends ListView implements mSwipeHelper = new SwipeHelper(context, SwipeHelper.X, this, mDensityScale, mTouchSlop); setItemsCanFocus(true); - setOnDragListener(this); } @Override @@ -123,6 +123,9 @@ public class PhoneFavoriteListView extends ListView implements @Override public boolean onInterceptTouchEvent(MotionEvent ev) { + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mTouchDownForDragStartY = (int) ev.getY(); + } if (isSwipeEnabled()) { return mSwipeHelper.onInterceptTouchEvent(ev) || super.onInterceptTouchEvent(ev); } else { @@ -189,8 +192,7 @@ public class PhoneFavoriteListView extends ListView implements } @Override - public void onDragCancelled(View v) { - } + public void onDragCancelled(View v) {} @Override public void onBeginDrag(View v) { @@ -205,11 +207,16 @@ public class PhoneFavoriteListView extends ListView implements public boolean dispatchDragEvent(DragEvent event) { switch (event.getAction()) { case DragEvent.ACTION_DRAG_LOCATION: - if (mScrollHandler == null) { - mScrollHandler = getHandler(); - } mLastDragY = (int) event.getY(); - mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS); + handleDrag((int) event.getX(), mLastDragY); + // Kick off {@link #mScrollHandler} if it's not started yet. + if (!mIsDragScrollerRunning && + // And if the distance traveled while dragging exceeds the touch slop + (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) { + mIsDragScrollerRunning = true; + ensureScrollHandler(); + mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS); + } break; case DragEvent.ACTION_DRAG_ENTERED: final int boundGap = (int) (getHeight() * BOUND_GAP_RATIO); @@ -218,20 +225,48 @@ public class PhoneFavoriteListView extends ListView implements break; case DragEvent.ACTION_DRAG_EXITED: case DragEvent.ACTION_DRAG_ENDED: + case DragEvent.ACTION_DROP: + ensureScrollHandler(); mScrollHandler.removeCallbacks(mDragScroller); + mIsDragScrollerRunning = false; break; case DragEvent.ACTION_DRAG_STARTED: // Not a receiver - case DragEvent.ACTION_DROP: - // Not a receiver default: break; } return super.dispatchDragEvent(event); } - @Override - public boolean onDrag(View v, DragEvent event) { - return true; + private void ensureScrollHandler() { + if (mScrollHandler == null) { + mScrollHandler = getHandler(); + } + } + + private void handleDrag(int x, int y) { + // find the view under the pointer, accounting for GONE views + final int count = getChildCount(); + View slidingChild; + for (int childIdx = 0; childIdx < count; childIdx++) { + slidingChild = getChildAt(childIdx); + if (slidingChild.getVisibility() == GONE) { + continue; + } + if (y >= slidingChild.getTop() && + y <= slidingChild.getBottom() && + slidingChild instanceof ContactTileRow) { + final ContactTileRow tile = (ContactTileRow) slidingChild; + reportDragEnteredItemIndex(tile.getItemIndex(x, y)); + } + } + } + + private void reportDragEnteredItemIndex(int itemIndex) { + final PhoneFavoriteMergedAdapter adapter = + (PhoneFavoriteMergedAdapter) getAdapter(); + if (adapter != null) { + adapter.reportDragEnteredItemIndex(itemIndex); + } } } diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java index c7554e212..084a34e98 100644 --- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java +++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java @@ -346,4 +346,10 @@ public class PhoneFavoriteMergedAdapter extends BaseAdapter { mOnItemSwipeListener = listener; } } + + public void reportDragEnteredItemIndex(int itemIndex) { + if (mContactTileAdapter != null) { + mContactTileAdapter.reportDragEnteredItemIndex(itemIndex); + } + } } diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java index 43dbad347..76a0e352b 100644 --- a/src/com/android/dialer/list/PhoneFavoriteTileView.java +++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java @@ -99,7 +99,11 @@ public abstract class PhoneFavoriteTileView extends ContactTileView { // If the view is regular row, start drag the row view. final View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view.getParentRow()); - view.getParentRow().startDrag(data, shadowBuilder, null, 0); + final ContactTileRow parent = (ContactTileRow) view.getParentRow(); + // Drag is not available for the item exceeds the PIN_LIMIT. + if (parent.getRegularRowItemIndex() < PhoneFavoritesTileAdapter.PIN_LIMIT) { + view.getParentRow().startDrag(data, shadowBuilder, null, 0); + } } else { // If the view is a tile view, start drag the tile. final View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view); diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java index a4d62d864..4eaeaf51a 100644 --- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java +++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java @@ -84,6 +84,8 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements private int mDraggedEntryIndex = -1; /** New position of the temporarily removed contact in the cache. */ private int mDropEntryIndex = -1; + /** New position of the temporarily entered contact in the cache. */ + private int mDragEnteredEntryIndex = -1; /** Position of the contact pending removal. */ private int mPotentialRemoveEntryIndex = -1; private long mIdToKeepInPlace = -1; @@ -121,7 +123,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements /** Indicates whether a drag is in process. */ private boolean mInDragging = false; - private static final int PIN_LIMIT = 20; + public static final int PIN_LIMIT = 20; /** * The soft limit on how many contact tiles to show. @@ -569,34 +571,58 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements * @param index Position of the contact to be removed. */ public void popContactEntry(int index) { - if (index >= 0 && index < mContactEntries.size()) { + if (isIndexInBound(index)) { mDraggedEntry = mContactEntries.get(index); - mContactEntries.set(index, ContactEntry.BLANK_ENTRY); - ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id; mDraggedEntryIndex = index; + mDragEnteredEntryIndex = index; + markDropArea(mDragEnteredEntryIndex); + } + } + + /** + * @param itemIndex Position of the contact in {@link #mContactEntries}. + * @return True if the given index is valid for {@link #mContactEntries}. + */ + private boolean isIndexInBound(int itemIndex) { + return itemIndex >= 0 && itemIndex < mContactEntries.size(); + } + + /** + * Mark the tile as drop area by given the item index in {@link #mContactEntries}. + * + * @param itemIndex Position of the contact in {@link #mContactEntries}. + */ + private void markDropArea(int itemIndex) { + if (isIndexInBound(mDragEnteredEntryIndex) && isIndexInBound(itemIndex)) { + // Remove the old placeholder item and place the new placeholder item. + mContactEntries.remove(mDragEnteredEntryIndex); + mDragEnteredEntryIndex = itemIndex; + mContactEntries.add(mDragEnteredEntryIndex, ContactEntry.BLANK_ENTRY); + ContactEntry.BLANK_ENTRY.id = mDraggedEntry.id; notifyDataSetChanged(); } } /** * Drops the temporarily removed contact to the desired location in the list. - * - * @param index Location where the contact will be dropped. */ - public void dropContactEntry(int index) { + public void handleDrop() { boolean changed = false; if (mDraggedEntry != null) { - if (index >= 0 && index < mContactEntries.size()) { + if (isIndexInBound(mDragEnteredEntryIndex)) { // Don't add the ContactEntry here (to prevent a double animation from occuring). // When we receive a new cursor the list of contact entries will automatically be // populated with the dragged ContactEntry at the correct spot. - mDropEntryIndex = index; + mDropEntryIndex = mDragEnteredEntryIndex; + mContactEntries.set(mDropEntryIndex, mDraggedEntry); mIdToKeepInPlace = getAdjustedItemId(mDraggedEntry.id); mDataSetChangedListener.cacheOffsetsForDatasetChange(); changed = true; - } else if (mDraggedEntryIndex >= 0 && mDraggedEntryIndex <= mContactEntries.size()) { - /** If the index is invalid, falls back to the original position of the contact. */ - mContactEntries.set(mDraggedEntryIndex, mDraggedEntry); + } else if (isIndexInBound(mDraggedEntryIndex)) { + // If {@link #mDragEnteredEntryIndex} is invalid, + // falls back to the original position of the contact. + mContactEntries.remove(mDragEnteredEntryIndex); + mContactEntries.add(mDraggedEntryIndex, mDraggedEntry); mDropEntryIndex = mDraggedEntryIndex; notifyDataSetChanged(); } @@ -605,7 +631,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements final ContentValues cv = getReflowedPinnedPositions(mContactEntries, mDraggedEntry, mDraggedEntryIndex, mDropEntryIndex); final Uri pinUri = PinnedPositions.UPDATE_URI.buildUpon().appendQueryParameter( - PinnedPositions.STAR_WHEN_PINNING, "true").build(); + PinnedPositions.STAR_WHEN_PINNING, "true").build(); // update the database here with the new pinned positions mContext.getContentResolver().update(pinUri, cv, null, null); } @@ -618,7 +644,11 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements * contact back to where it was dragged from. */ public void dropToUnsupportedView() { - dropContactEntry(-1); + if (isIndexInBound(mDragEnteredEntryIndex)) { + mContactEntries.remove(mDragEnteredEntryIndex); + mContactEntries.add(mDraggedEntryIndex, mDraggedEntry); + notifyDataSetChanged(); + } } /** @@ -638,7 +668,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements */ public boolean removePendingContactEntry() { boolean removed = false; - if (mPotentialRemoveEntryIndex >= 0 && mPotentialRemoveEntryIndex < mContactEntries.size()) { + if (isIndexInBound(mPotentialRemoveEntryIndex)) { final ContactEntry entry = mContactEntries.get(mPotentialRemoveEntryIndex); unstarAndUnpinContact(entry.lookupKey); removed = true; @@ -661,6 +691,7 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements public void cleanTempVariables() { mDraggedEntryIndex = -1; mDropEntryIndex = -1; + mDragEnteredEntryIndex = -1; mDraggedEntry = null; mPotentialRemoveEntryIndex = -1; } @@ -917,11 +948,20 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements } } else { /** If the selected item is one of the rows, compute the index. */ - return (mPosition - mMaxTiledRows) + mColumnCount * mMaxTiledRows; + return getRegularRowItemIndex(); } return -1; } + /** + * Gets the index of the regular row item. + * + * @return Index of the selected item in the cached array. + */ + public int getRegularRowItemIndex() { + return (mPosition - mMaxTiledRows) + mColumnCount * mMaxTiledRows; + } + public PhoneFavoritesTileAdapter getTileAdapter() { return PhoneFavoritesTileAdapter.this; } @@ -1110,32 +1150,12 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements ContactEntry entryToPin, int oldPos, int newPinPos) { final ContentValues cv = new ContentValues(); - // Add the dragged contact at the user-requested spot. - cv.put(String.valueOf(entryToPin.id), newPinPos); - - final int listSize = list.size(); - if (oldPos < newPinPos && list.get(listSize - 1).pinned == (listSize - 1)) { - // The only time we should get here is it we are completely full - i.e. starting - // from the newly pinned contact to the end of the list, every single contact - // thereafter is pinned, and a contact is being shifted to the right by the user. - // Instead of trying to make room to the right, we should thus try to shift contacts - // to the left instead, working backwards through the list, starting from the contact - // which just got bumped. - for (int i = newPinPos; i >= 0; i--) { - final ContactEntry entry = list.get(i); - // Once we find an unpinned spot(or a blank entry), we can stop pushing contacts - // to the left. - if (entry.pinned > PIN_LIMIT) break; - cv.put(String.valueOf(entry.id), entry.pinned - 1); - } - } else { - // Shift any pinned contacts to the right as necessary, until an unpinned - // spot is found - for (int i = newPinPos; i < PIN_LIMIT && i < list.size(); i++) { - final ContactEntry entry = list.get(i); - if (entry.pinned > PIN_LIMIT) break; - cv.put(String.valueOf(entry.id), entry.pinned + 1); - } + final int lowerBound = Math.min(oldPos, newPinPos); + final int upperBound = Math.max(oldPos, newPinPos); + for (int i = lowerBound; i <= upperBound; i++) { + final ContactEntry entry = list.get(i); + if (entry.pinned == i) continue; + cv.put(String.valueOf(entry.id), i); } return cv; } @@ -1175,4 +1195,13 @@ public class PhoneFavoritesTileAdapter extends BaseAdapter implements public void setEmptyView(View emptyView) { mEmptyView = emptyView; } + + public void reportDragEnteredItemIndex(int itemIndex) { + if (mInDragging && + mDragEnteredEntryIndex != itemIndex && + isIndexInBound(itemIndex) && + itemIndex < PIN_LIMIT) { + markDropArea(itemIndex); + } + } } |