Refactor the D-N-D, let list view handle all the events

Bug: 10686781

By moving the Drag and Drop logic to the list view, it helps
- Making the code more readable
- Making it feasible to control the drag shadow and animating more
  easily

Change-Id: Ia30ea2d9379c1823ff056360fdcb8faadca553e8
diff --git a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java b/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
deleted file mode 100644
index f0e97ac..0000000
--- a/src/com/android/dialer/list/PhoneFavoriteDragAndDropListeners.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.dialer.list;
-
-import android.graphics.Rect;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.View;
-import android.view.View.OnDragListener;
-
-import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
-
-/**
- * Implements the OnLongClickListener and OnDragListener for phone's favorite tiles and rows.
- */
-public class PhoneFavoriteDragAndDropListeners {
-
-    private static final String TAG = PhoneFavoriteDragAndDropListeners.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
-    /**
-     * Implements the OnDragListener to handle drag events.
-     */
-    public static class PhoneFavoriteDragListener implements OnDragListener {
-        /** Location of the drag event. */
-        private float mX = 0;
-        private float mY = 0;
-        private final ContactTileRow mContactTileRow;
-        private final PhoneFavoritesTileAdapter mTileAdapter;
-
-        public PhoneFavoriteDragListener(ContactTileRow contactTileRow,
-                PhoneFavoritesTileAdapter tileAdapter) {
-            super();
-            mContactTileRow = contactTileRow;
-            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) {
-                Log.v(TAG, event.toString());
-            }
-            // Handles drag events.
-            switch (event.getAction()) {
-                case DragEvent.ACTION_DRAG_STARTED:
-                    final int itemIndex = getDragItemIndex(event);
-                    if (itemIndex != -1) {
-                        // Indicates a drag has started.
-                        mTileAdapter.setInDragging(true);
-
-                        // Temporarily pops out the Contact entry.
-                        mTileAdapter.popContactEntry(itemIndex);
-                    }
-                    break;
-                case DragEvent.ACTION_DRAG_ENTERED:
-                    break;
-                case DragEvent.ACTION_DRAG_EXITED:
-                    break;
-                case DragEvent.ACTION_DROP:
-                    // Indicates a drag has finished.
-                    if (mTileAdapter != null && mContactTileRow != null) {
-                        mTileAdapter.setInDragging(false);
-                        // 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 != 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.handleDrop();
-                    }
-                    break;
-                case DragEvent.ACTION_DRAG_LOCATION:
-                    break;
-                default:
-                    break;
-            }
-            return true;
-        }
-    }
-}
diff --git a/src/com/android/dialer/list/PhoneFavoriteFragment.java b/src/com/android/dialer/list/PhoneFavoriteFragment.java
index 270ed63..d8179e7 100644
--- a/src/com/android/dialer/list/PhoneFavoriteFragment.java
+++ b/src/com/android/dialer/list/PhoneFavoriteFragment.java
@@ -211,6 +211,7 @@
         mListView.setVerticalScrollbarPosition(View.SCROLLBAR_POSITION_RIGHT);
         mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
         mListView.setOnItemSwipeListener(mContactTileAdapter);
+        mListView.setOnDragDropListener(mContactTileAdapter);
 
         mEmptyView = inflater.inflate(R.layout.phone_no_favorites, mListView, false);
 
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
index 00f645a..4636e17 100644
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteListView.java
@@ -47,6 +47,7 @@
     private boolean mEnableSwipe = true;
 
     private OnItemGestureListener mOnItemGestureListener;
+    private OnDragDropListener mOnDragDropListener;
 
     private float mDensityScale;
     private float mTouchSlop;
@@ -60,6 +61,7 @@
     private final int DRAG_SCROLL_PX_UNIT = 10;
 
     private boolean mIsDragScrollerRunning = false;
+    private int mTouchDownForDragStartX;
     private int mTouchDownForDragStartY;
 
     /**
@@ -121,9 +123,14 @@
         mOnItemGestureListener = listener;
     }
 
+    public void setOnDragDropListener(OnDragDropListener listener) {
+        mOnDragDropListener = listener;
+    }
+
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         if (ev.getAction() == MotionEvent.ACTION_DOWN) {
+            mTouchDownForDragStartX = (int) ev.getX();
             mTouchDownForDragStartY = (int) ev.getY();
         }
         if (isSwipeEnabled()) {
@@ -147,24 +154,13 @@
 
     @Override
     public View getChildAtPosition(MotionEvent ev) {
-        // find the view under the pointer, accounting for GONE views
-        final int count = getChildCount();
-        final int touchY = (int) ev.getY();
-        View slidingChild;
-        for (int childIdx = 0; childIdx < count; childIdx++) {
-            slidingChild = getChildAt(childIdx);
-            if (slidingChild.getVisibility() == GONE) {
-                continue;
-            }
-            if (touchY >= slidingChild.getTop() && touchY <= slidingChild.getBottom()) {
-                if (SwipeHelper.isSwipeable(slidingChild)) {
-                    // If this view is swipable in this listview, then return it. Otherwise
-                    // return a null view, which will simply be ignored by the swipe helper.
-                    return slidingChild;
-                } else {
-                    return null;
-                }
-            }
+        final View view = getViewAtPosition((int) ev.getX(), (int) ev.getY());
+        if (view != null &&
+                SwipeHelper.isSwipeable(view) &&
+                view.getVisibility() != GONE) {
+            // If this view is swipable in this listview, then return it. Otherwise
+            // return a null view, which will simply be ignored by the swipe helper.
+            return view;
         }
         return null;
     }
@@ -205,10 +201,16 @@
 
     @Override
     public boolean dispatchDragEvent(DragEvent event) {
-        switch (event.getAction()) {
+        final int action = event.getAction();
+        final int eX = (int) event.getX();
+        final int eY = (int) event.getY();
+        switch (action) {
+            case DragEvent.ACTION_DRAG_STARTED:
+                handleDragStarted(mTouchDownForDragStartX, mTouchDownForDragStartY);
+                break;
             case DragEvent.ACTION_DRAG_LOCATION:
-                mLastDragY = (int) event.getY();
-                handleDrag((int) event.getX(), mLastDragY);
+                mLastDragY = eY;
+                handleDragHovered(eX, eY);
                 // Kick off {@link #mScrollHandler} if it's not started yet.
                 if (!mIsDragScrollerRunning &&
                         // And if the distance traveled while dragging exceeds the touch slop
@@ -229,13 +231,30 @@
                 ensureScrollHandler();
                 mScrollHandler.removeCallbacks(mDragScroller);
                 mIsDragScrollerRunning = false;
+                if (action != DragEvent.ACTION_DRAG_EXITED) {
+                    handleDragFinished();
+                }
                 break;
-            case DragEvent.ACTION_DRAG_STARTED:
-                // Not a receiver
             default:
                 break;
         }
-        return super.dispatchDragEvent(event);
+        // This ListView will consumer the drag events on behalf of its children.
+        return true;
+    }
+
+    /**
+     * Find the view under the pointer.
+     */
+    private View getViewAtPosition(int x, int y) {
+        final int count = getChildCount();
+        View child;
+        for (int childIdx = 0; childIdx < count; childIdx++) {
+            child = getChildAt(childIdx);
+            if (y >= child.getTop() && y <= child.getBottom()) {
+                return child;
+            }
+        }
+        return null;
     }
 
     private void ensureScrollHandler() {
@@ -244,29 +263,37 @@
         }
     }
 
-    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 handleDragStarted(int x, int y) {
+        final View child = getViewAtPosition(x, y);
+        if (child instanceof ContactTileRow) {
+            final ContactTileRow tile = (ContactTileRow) child;
+            final int itemIndex = tile.getItemIndex(x, y);
+            if (itemIndex != -1 && mOnDragDropListener != null) {
+                mOnDragDropListener.onDragStarted(itemIndex);
             }
         }
     }
 
-    private void reportDragEnteredItemIndex(int itemIndex) {
-        final PhoneFavoriteMergedAdapter adapter =
-                (PhoneFavoriteMergedAdapter) getAdapter();
-        if (adapter != null) {
-            adapter.reportDragEnteredItemIndex(itemIndex);
+    private void handleDragHovered(int x, int y) {
+        final View child = getViewAtPosition(x, y);
+        if (child instanceof ContactTileRow) {
+            final ContactTileRow tile = (ContactTileRow) child;
+            final int itemIndex = tile.getItemIndex(x, y);
+            if (itemIndex != -1 && mOnDragDropListener != null) {
+                mOnDragDropListener.onDragHovered(itemIndex);
+            }
         }
     }
+
+    private void handleDragFinished() {
+        if (mOnDragDropListener != null) {
+            mOnDragDropListener.onDragFinished();
+        }
+    }
+
+    public interface OnDragDropListener {
+        public void onDragStarted(int itemIndex);
+        public void onDragHovered(int itemIndex);
+        public void onDragFinished();
+    }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
index 084a34e..c7554e2 100644
--- a/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoriteMergedAdapter.java
@@ -346,10 +346,4 @@
             mOnItemSwipeListener = listener;
         }
     }
-
-    public void reportDragEnteredItemIndex(int itemIndex) {
-        if (mContactTileAdapter != null) {
-            mContactTileAdapter.reportDragEnteredItemIndex(itemIndex);
-        }
-    }
 }
diff --git a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
index 374f733..efe7bdd 100644
--- a/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteRegularRowView.java
@@ -23,7 +23,6 @@
 
 import com.android.contacts.common.util.ViewUtil;
 import com.android.dialer.R;
-import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
 
 import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
 
diff --git a/src/com/android/dialer/list/PhoneFavoriteTileView.java b/src/com/android/dialer/list/PhoneFavoriteTileView.java
index 76a0e35..374ff87 100644
--- a/src/com/android/dialer/list/PhoneFavoriteTileView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteTileView.java
@@ -20,7 +20,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
-import android.content.ClipData;
 import android.content.Context;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -32,7 +31,6 @@
 import com.android.contacts.common.MoreContactUtils;
 import com.android.contacts.common.list.ContactEntry;
 import com.android.contacts.common.list.ContactTileView;
-import com.android.dialer.list.PhoneFavoriteDragAndDropListeners.PhoneFavoriteDragListener;
 import com.android.dialer.list.PhoneFavoritesTileAdapter.ContactTileRow;
 import com.android.dialer.list.PhoneFavoritesTileAdapter.ViewTypes;
 
@@ -94,20 +92,16 @@
             public boolean onLongClick(View v) {
                 setPressed(false);
                 final PhoneFavoriteTileView view = (PhoneFavoriteTileView) v;
-                final ClipData data = ClipData.newPlainText("", "");
                 if (view instanceof PhoneFavoriteRegularRowView) {
-                    // If the view is regular row, start drag the row view.
-                    final View.DragShadowBuilder shadowBuilder =
-                            new View.DragShadowBuilder(view.getParentRow());
                     final ContactTileRow parent = (ContactTileRow) view.getParentRow();
+                    // If the view is regular row, start drag the row view.
                     // Drag is not available for the item exceeds the PIN_LIMIT.
                     if (parent.getRegularRowItemIndex() < PhoneFavoritesTileAdapter.PIN_LIMIT) {
-                        view.getParentRow().startDrag(data, shadowBuilder, null, 0);
+                        parent.startDrag(null, new View.DragShadowBuilder(parent), null, 0);
                     }
                 } else {
                     // If the view is a tile view, start drag the tile.
-                    final View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
-                    view.startDrag(data, shadowBuilder, null, 0);
+                    view.startDrag(null, new View.DragShadowBuilder(view), null, 0);
                 }
                 return true;
             }
@@ -205,8 +199,6 @@
     @Override
     protected void onAttachedToWindow() {
         mParentRow = (ContactTileRow) getParent();
-        mParentRow.setOnDragListener(new PhoneFavoriteDragListener(mParentRow,
-                mParentRow.getTileAdapter()));
     }
 
     @Override
diff --git a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
index 4eaeaf5..8d0fb86 100644
--- a/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
+++ b/src/com/android/dialer/list/PhoneFavoritesTileAdapter.java
@@ -62,7 +62,7 @@
  *
  */
 public class PhoneFavoritesTileAdapter extends BaseAdapter implements
-        SwipeHelper.OnItemGestureListener {
+        SwipeHelper.OnItemGestureListener, PhoneFavoriteListView.OnDragDropListener {
     private static final String TAG = PhoneFavoritesTileAdapter.class.getSimpleName();
     private static final boolean DEBUG = false;
 
@@ -1192,11 +1192,14 @@
         return !mAwaitingRemove;
     }
 
-    public void setEmptyView(View emptyView) {
-        mEmptyView = emptyView;
+    @Override
+    public void onDragStarted(int itemIndex) {
+        setInDragging(true);
+        popContactEntry(itemIndex);
     }
 
-    public void reportDragEnteredItemIndex(int itemIndex) {
+    @Override
+    public void onDragHovered(int itemIndex) {
         if (mInDragging &&
                 mDragEnteredEntryIndex != itemIndex &&
                 isIndexInBound(itemIndex) &&
@@ -1204,4 +1207,14 @@
             markDropArea(itemIndex);
         }
     }
+
+    @Override
+    public void onDragFinished() {
+        setInDragging(false);
+        handleDrop();
+    }
+
+    public void setEmptyView(View emptyView) {
+        mEmptyView = emptyView;
+    }
 }