Merge "Refactor drag and drop to work for entire Dialer layout"
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 2cd796b..e4e2a23 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -46,30 +46,37 @@
             android:id="@+id/search_and_remove_view_container"
             android:visibility="gone"
             >
-            <com.android.dialer.list.RemoveView
-                android:layout_width="match_parent"
-                android:layout_height="56dp"
-                android:id="@+id/remove_view_container"
-                android:orientation="horizontal"
-                android:gravity="center">
-                <ImageView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:id="@+id/remove_view_icon"
-                    android:src="@drawable/ic_remove"
-                    android:contentDescription="@string/remove_contact"
-                    />
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:id="@+id/remove_view_text"
-                    android:textSize="@dimen/remove_text_size"
-                    android:textColor="@color/remove_text_color"
-                    android:text="@string/remove_contact"
-                    />
-            </com.android.dialer.list.RemoveView>
         </FrameLayout>
     </RelativeLayout>
+    <com.android.dialer.list.RemoveView
+        android:layout_width="match_parent"
+        android:layout_height="56dp"
+        android:id="@+id/remove_view"
+        android:layout_alignParentTop="true"
+        >
+        <LinearLayout
+            android:id="@+id/remove_view_container"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            android:gravity="center"
+            android:orientation="horizontal">
+            <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/remove_view_icon"
+                android:src="@drawable/ic_remove"
+                android:contentDescription="@string/remove_contact"
+                />
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:id="@+id/remove_view_text"
+                android:textSize="@dimen/remove_text_size"
+                android:textColor="@color/remove_text_color"
+                android:text="@string/remove_contact"
+                />
+        </LinearLayout>
+    </com.android.dialer.list.RemoveView >
     <FrameLayout
         android:layout_height="@dimen/floating_action_button_height"
         android:layout_width="@dimen/floating_action_button_width"
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index e0e9762..7aaa897 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -44,10 +44,12 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.Log;
+import android.view.DragEvent;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnDragListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
@@ -184,9 +186,6 @@
      */
     private String mPendingSearchViewQuery;
 
-    // This view points to the Framelayout that houses both the search view and remove view
-    // containers.
-    private View mSearchAndRemoveViewContainer;
     private EditText mSearchView;
     private View mSearchViewCloseButton;
     private View mVoiceSearchButton;
@@ -195,13 +194,14 @@
      * If the user releases a contact when hovering on top of this, the contact is unfavorited and
      * removed from the speed dial list.
      */
-    private RemoveView mRemoveViewContainer;
+    private View mRemoveViewContainer;
 
     final Interpolator hideActionBarInterpolator = new AccelerateInterpolator(1.5f);
     final Interpolator showActionBarInterpolator = new DecelerateInterpolator(1.5f);
     private String mSearchQuery;
 
     private DialerDatabaseHelper mDialerDatabaseHelper;
+    private DragDropController mDragDropController;
 
     private class OverflowPopupMenu extends PopupMenu {
         public OverflowPopupMenu(Context context, View anchor) {
@@ -219,6 +219,21 @@
     }
 
     /**
+     * Listener that listens to drag events and sends their x and y coordinates to a
+     * {@link DragDropController}.
+     */
+    private class LayoutOnDragListener implements OnDragListener {
+        @Override
+        public boolean onDrag(View v, DragEvent event) {
+            if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) {
+                mDragDropController.handleDragHovered(v, (int) event.getX(),
+                        (int) event.getY());
+            }
+            return true;
+        }
+    }
+
+    /**
      * Listener used when one of phone numbers in search UI is selected. This will initiate a
      * phone call using the phone number.
      */
@@ -362,11 +377,12 @@
         mDialpadButton = findViewById(R.id.dialpad_button);
         mDialpadButton.setOnClickListener(this);
 
-        mRemoveViewContainer = (RemoveView) findViewById(R.id.remove_view_container);
-        mSearchAndRemoveViewContainer = findViewById(R.id.search_and_remove_view_container);
+        mRemoveViewContainer = findViewById(R.id.remove_view_container);
 
         mDialerDatabaseHelper = DatabaseHelperManager.getDatabaseHelper(this);
         SmartDialPrefix.initializeNanpSettings(this);
+
+        findViewById(R.id.dialtacts_mainlayout).setOnDragListener(new LayoutOnDragListener());
     }
 
     @Override
@@ -574,13 +590,6 @@
         ft.commit();
     }
 
-    final AnimatorListener mHideListener = new AnimatorListenerAdapter() {
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            mSearchAndRemoveViewContainer.setVisibility(View.GONE);
-        }
-    };
-
     private boolean getInSearchUi() {
         return mInDialpadSearch || mInRegularSearch;
     }
@@ -936,11 +945,12 @@
     @Override
     public void onDragStarted(int x, int y, PhoneFavoriteSquareTileView view) {
         getActionBar().hide();
-        mSearchAndRemoveViewContainer.setVisibility(View.VISIBLE);
+        mRemoveViewContainer.setVisibility(View.VISIBLE);
     }
 
     @Override
-    public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {}
+    public void onDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
+    }
 
     /**
      * Called when the user has released a contact tile after long-pressing it.
@@ -948,7 +958,7 @@
     @Override
     public void onDragFinished(int x, int y) {
         getActionBar().show();
-        mSearchAndRemoveViewContainer.setVisibility(View.GONE);
+        mRemoveViewContainer.setVisibility(View.GONE);
     }
 
     @Override
@@ -960,7 +970,9 @@
      */
     @Override
     public void setDragDropController(DragDropController dragController) {
-        mRemoveViewContainer.setDragDropController(dragController);
+        mDragDropController = dragController;
+        ((RemoveView) findViewById(R.id.remove_view))
+                .setDragDropController(dragController);
     }
 
     @Override
diff --git a/src/com/android/dialer/list/DragDropController.java b/src/com/android/dialer/list/DragDropController.java
index db4dd59..8cd1046 100644
--- a/src/com/android/dialer/list/DragDropController.java
+++ b/src/com/android/dialer/list/DragDropController.java
@@ -1,5 +1,8 @@
 package com.android.dialer.list;
 
+import android.util.Log;
+import android.view.View;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -8,27 +11,48 @@
  * off events to any OnDragDropListeners that have registered for callbacks.
  */
 public class DragDropController {
-    private List<OnDragDropListener> mOnDragDropListeners = new ArrayList<OnDragDropListener>();
+
+    private final List<OnDragDropListener> mOnDragDropListeners =
+            new ArrayList<OnDragDropListener>();
+    private final DragItemContainer mDragItemContainer;
+    private final int[] mLocationOnScreen = new int[2];
+
+    /**
+     * Callback interface used to retrieve views based on the current touch coordinates of the
+     * drag event. The {@link DragItemContainer} houses the draggable views that this
+     * {@link DragDropController} controls.
+     */
+    public interface DragItemContainer {
+        public PhoneFavoriteSquareTileView getViewForLocation(int x, int y);
+    }
+
+    public DragDropController(DragItemContainer dragItemContainer) {
+        mDragItemContainer = dragItemContainer;
+    }
 
     /**
      * @return True if the drag is started, false if the drag is cancelled for some reason.
      */
-    boolean handleDragStarted(int x, int y, PhoneFavoriteSquareTileView tileView) {
+    boolean handleDragStarted(int x, int y) {
+        final PhoneFavoriteSquareTileView tileView = mDragItemContainer.getViewForLocation(x, y);
         if (tileView == null) {
             return false;
         }
-        if (tileView != null && !mOnDragDropListeners.isEmpty()) {
-            for (int i = 0; i < mOnDragDropListeners.size(); i++) {
-                mOnDragDropListeners.get(i).onDragStarted(x, y, tileView);
-            }
+        for (int i = 0; i < mOnDragDropListeners.size(); i++) {
+            mOnDragDropListeners.get(i).onDragStarted(x, y, tileView);
         }
 
         return true;
     }
 
-    public void handleDragHovered(int x, int y, PhoneFavoriteSquareTileView view) {
+    public void handleDragHovered(View v, int x, int y) {
+        v.getLocationOnScreen(mLocationOnScreen);
+        final int screenX = x + mLocationOnScreen[0];
+        final int screenY = y + mLocationOnScreen[1];
+        final PhoneFavoriteSquareTileView view = mDragItemContainer.getViewForLocation(
+                screenX, screenY);
         for (int i = 0; i < mOnDragDropListeners.size(); i++) {
-            mOnDragDropListeners.get(i).onDragHovered(x, y, view);
+            mOnDragDropListeners.get(i).onDragHovered(screenX, screenY, view);
         }
     }
 
diff --git a/src/com/android/dialer/list/PhoneFavoriteListView.java b/src/com/android/dialer/list/PhoneFavoriteListView.java
index 6c3d62a..074cc07 100644
--- a/src/com/android/dialer/list/PhoneFavoriteListView.java
+++ b/src/com/android/dialer/list/PhoneFavoriteListView.java
@@ -33,11 +33,13 @@
 import android.widget.ImageView;
 
 import com.android.dialer.R;
+import com.android.dialer.list.DragDropController.DragItemContainer;
 
 /**
  * Viewgroup that presents the user's speed dial contacts in a grid.
  */
-public class PhoneFavoriteListView extends GridView implements OnDragDropListener {
+public class PhoneFavoriteListView extends GridView implements OnDragDropListener,
+        DragItemContainer {
 
     public static final String LOG_TAG = PhoneFavoriteListView.class.getSimpleName();
 
@@ -59,6 +61,8 @@
     private ImageView mDragShadowOverlay;
     private int mAnimationDuration;
 
+    final int[] mLocationOnScreen = new int[2];
+
     // X and Y offsets inside the item from where the user grabbed to the
     // child's left coordinate. This is used to aid in the drawing of the drag shadow.
     private int mTouchOffsetToChildLeft;
@@ -67,7 +71,7 @@
     private int mDragShadowLeft;
     private int mDragShadowTop;
 
-    private DragDropController mDragDropController = new DragDropController();
+    private DragDropController mDragDropController = new DragDropController(this);
 
     private final float DRAG_SHADOW_ALPHA = 0.7f;
 
@@ -138,39 +142,20 @@
     }
 
     @Override
-    public boolean dispatchDragEvent(DragEvent event) {
+    public boolean onDragEvent(DragEvent event) {
         final int action = event.getAction();
         final int eX = (int) event.getX();
         final int eY = (int) event.getY();
         switch (action) {
             case DragEvent.ACTION_DRAG_STARTED: {
-                final int[] coordinates = new int[2];
-                getLocationOnScreen(coordinates);
-                // Calculate the X and Y coordinates of the drag event relative to the view
-                final int viewX = eX - coordinates[0];
-                final int viewY = eY - coordinates[1];
-                final View child = getViewAtPosition(viewX, viewY);
-
-                if (!(child instanceof PhoneFavoriteSquareTileView)) {
-                    // Bail early.
-                    return false;
-                }
-
-                final PhoneFavoriteSquareTileView tile = (PhoneFavoriteSquareTileView) child;
-                if (!mDragDropController.handleDragStarted(viewX, viewY, tile)) {
+                if (!mDragDropController.handleDragStarted(eX, eY)) {
                     return false;
                 }
                 break;
             }
             case DragEvent.ACTION_DRAG_LOCATION:
                 mLastDragY = eY;
-                final View child = getViewAtPosition(eX, eY);
-
-                PhoneFavoriteSquareTileView tile = null;
-                if (child instanceof PhoneFavoriteSquareTileView) {
-                    tile = (PhoneFavoriteSquareTileView) child;
-                }
-                mDragDropController.handleDragHovered(eX, eY, tile);
+                mDragDropController.handleDragHovered(this, 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
@@ -313,4 +298,19 @@
 
         return bitmap;
     }
+
+    @Override
+    public PhoneFavoriteSquareTileView getViewForLocation(int x, int y) {
+        getLocationOnScreen(mLocationOnScreen);
+        // Calculate the X and Y coordinates of the drag event relative to the view
+        final int viewX = x - mLocationOnScreen[0];
+        final int viewY = y - mLocationOnScreen[1];
+        final View child = getViewAtPosition(viewX, viewY);
+
+        if (!(child instanceof PhoneFavoriteSquareTileView)) {
+            return null;
+        }
+
+        return (PhoneFavoriteSquareTileView) child;
+    }
 }
diff --git a/src/com/android/dialer/list/RemoveView.java b/src/com/android/dialer/list/RemoveView.java
index 16942fe..ae358fc 100644
--- a/src/com/android/dialer/list/RemoveView.java
+++ b/src/com/android/dialer/list/RemoveView.java
@@ -4,14 +4,16 @@
 import android.content.res.Resources;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.DragEvent;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.dialer.R;
 
-public class RemoveView extends LinearLayout {
+public class RemoveView extends FrameLayout {
 
     DragDropController mDragDropController;
     TextView mRemoveText;
@@ -49,31 +51,30 @@
     }
 
     @Override
-    public boolean dispatchDragEvent(DragEvent event) {
-      final int action = event.getAction();
-      switch (action) {
-        case DragEvent.ACTION_DRAG_ENTERED:
-            setAppearanceHighlighted();
-            break;
-        case DragEvent.ACTION_DRAG_EXITED:
-            setAppearanceNormal();
-            break;
-        case DragEvent.ACTION_DRAG_LOCATION:
-            if (mDragDropController != null) {
-                mDragDropController.handleDragHovered((int) event.getX(),
-                        // the true y-coordinate of the event with respect to the listview is
-                        // offset by the height of the remove view
-                        (int) event.getY() - getHeight(), null);
-            }
-            break;
-        case DragEvent.ACTION_DROP:
-            if (mDragDropController != null) {
-                mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(), true);
-            }
-            setAppearanceNormal();
-            break;
-      }
-      return true;
+    public boolean onDragEvent(DragEvent event) {
+        final int action = event.getAction();
+        switch (action) {
+            case DragEvent.ACTION_DRAG_ENTERED:
+                setAppearanceHighlighted();
+                break;
+            case DragEvent.ACTION_DRAG_EXITED:
+                setAppearanceNormal();
+                break;
+            case DragEvent.ACTION_DRAG_LOCATION:
+                if (mDragDropController != null) {
+                    mDragDropController.handleDragHovered(this, (int) event.getX(),
+                            (int) event.getY());
+                }
+                break;
+            case DragEvent.ACTION_DROP:
+                if (mDragDropController != null) {
+                    mDragDropController.handleDragFinished((int) event.getX(), (int) event.getY(),
+                            true);
+                }
+                setAppearanceNormal();
+                break;
+        }
+        return true;
     }
 
     private void setAppearanceNormal() {