Refactor drag and drop to work for entire Dialer layout

Add a drag listener to the main dialer layout and manipulate
drag events correctly in DragDropController so that drag and
drop works throughout the main Dialer layout, rather than just the
speed dial fragment.

Also shift the remove view into dialtacts_mainlayout so that it can
continue receiving drag events.

Bug: 14393052

Change-Id: I90c26fee4fe681d0e237aa490185e850628e4cd0
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 ce71245..b965530 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;
@@ -56,7 +58,6 @@
 import android.widget.EditText;
 import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
-import android.widget.SearchView;
 import android.widget.Toast;
 
 import com.android.contacts.common.CallUtil;
@@ -185,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;
@@ -196,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) {
@@ -220,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.
      */
@@ -363,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 +589,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 +944,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 +957,7 @@
     @Override
     public void onDragFinished(int x, int y) {
         getActionBar().show();
-        mSearchAndRemoveViewContainer.setVisibility(View.GONE);
+        mRemoveViewContainer.setVisibility(View.GONE);
     }
 
     @Override
@@ -960,7 +969,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() {