Enable dragon drop for touch.

- Start a drag when the user long-presses on an already-selected item.
- Start a drag when the user mouse-drags on an already-selected item.
- Add highlighting of drop targets.

BUG=20556237

Change-Id: I450fd6768eeb08906304227385476942d641fd11
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 1a4e3eb..e1650e1 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -8,6 +8,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
 # The design lib requires that the client package use appcompat themes.
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
 # Supplies material design components, e.g. Snackbar.
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
 LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 7138c2d..f24fcc9 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -52,6 +52,7 @@
 import android.provider.DocumentsContract.Document;
 import android.support.annotation.Nullable;
 import android.support.design.widget.Snackbar;
+import android.support.v13.view.DragStartHelper;
 import android.support.v7.widget.GridLayoutManager;
 import android.support.v7.widget.GridLayoutManager.SpanSizeLookup;
 import android.support.v7.widget.RecyclerView;
@@ -72,7 +73,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewParent;
 import android.widget.ImageView;
 import android.widget.TextView;
 
@@ -258,7 +258,8 @@
         }
         mRecView.setLayoutManager(mLayout);
 
-        mGestureDetector = new ListeningGestureDetector(this.getContext(), new GestureListener());
+        mGestureDetector =
+                new ListeningGestureDetector(this.getContext(), mDragHelper, new GestureListener());
 
         mRecView.addOnItemTouchListener(mGestureDetector);
 
@@ -1051,7 +1052,7 @@
             view.setOnDragListener(mOnDragListener);
         }
 
-        view.setOnLongClickListener(mLongClickListener);
+        view.setOnLongClickListener(mDragHelper);
     }
 
     private View.OnDragListener mOnDragListener = new View.OnDragListener() {
@@ -1062,11 +1063,15 @@
                     // TODO: Check if the event contains droppable data.
                     return true;
 
-                // TODO: Highlight potential drop target directory?
                 // TODO: Expand drop target directory on hover?
                 case DragEvent.ACTION_DRAG_ENTERED:
-                case DragEvent.ACTION_DRAG_LOCATION:
+                    highlightDropTarget(v, true);
+                    return true;
                 case DragEvent.ACTION_DRAG_EXITED:
+                    highlightDropTarget(v, false);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_LOCATION:
                 case DragEvent.ACTION_DRAG_ENDED:
                     return true;
 
@@ -1084,6 +1089,17 @@
             }
             return false;
         }
+
+        private void highlightDropTarget(View v, boolean highlight) {
+            // Note: use exact comparison - this code is searching for views which are children of
+            // the RecyclerView instance in the UI.
+            if (v.getParent() == mRecView) {
+                RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(v);
+                if (vh instanceof DocumentHolder) {
+                    ((DocumentHolder) vh).setHighlighted(highlight);
+                }
+            }
+        }
     };
 
     /**
@@ -1109,21 +1125,14 @@
      *     a document item view.
      */
     private String getModelId(View view) {
-        while (true) {
-            if (view.getLayoutParams() instanceof RecyclerView.LayoutParams) {
-                RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(view);
-                if (vh instanceof DocumentHolder) {
-                    return ((DocumentHolder) vh).modelId;
-                } else {
-                    return null;
-                }
+        View itemView = mRecView.findContainingItemView(view);
+        if (itemView != null) {
+            RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView);
+            if (vh instanceof DocumentHolder) {
+                return ((DocumentHolder) vh).modelId;
             }
-            ViewParent parent = view.getParent();
-            if (parent == null || !(parent instanceof View)) {
-                return null;
-            }
-            view = (View) parent;
         }
+        return null;
     }
 
     private List<DocumentInfo> getDraggableDocuments(View currentItemView) {
@@ -1299,10 +1308,10 @@
         }
     }
 
-    private View.OnLongClickListener mLongClickListener = new View.OnLongClickListener() {
+    private DragStartHelper mDragHelper = new DragStartHelper(null) {
         @Override
-        public boolean onLongClick(View v) {
-            if (mGestureDetector.mouseSpawnedLastEvent()) {
+        protected boolean onDragStart(View v) {
+            if (isSelected(getModelId(v))) {
                 List<DocumentInfo> docs = getDraggableDocuments(v);
                 if (docs.isEmpty()) {
                     return false;
@@ -1328,9 +1337,12 @@
             implements OnItemTouchListener {
 
         private int mLastTool = -1;
+        private DragStartHelper mDragHelper;
 
-        public ListeningGestureDetector(Context context, GestureListener listener) {
+        public ListeningGestureDetector(
+                Context context, DragStartHelper dragHelper, GestureListener listener) {
             super(context, listener);
+            mDragHelper = dragHelper;
             setOnDoubleTapListener(listener);
         }
 
@@ -1345,12 +1357,27 @@
         @Override
         public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
             mLastTool = e.getToolType(0);
-            onTouchEvent(e);  // bounce this forward to our detecty heart
+
+            // Detect drag events. When a drag is detected, intercept the rest of the gesture.
+            View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+            if (itemView != null && mDragHelper.handleTouch(itemView,  e)) {
+                return true;
+            }
+            // Forward unhandled events to the GestureDetector.
+            onTouchEvent(e);
+
             return false;
         }
 
         @Override
-        public void onTouchEvent(RecyclerView rv, MotionEvent e) {}
+        public void onTouchEvent(RecyclerView rv, MotionEvent e) {
+            View itemView = rv.findChildViewUnder(e.getX(), e.getY());
+            mDragHelper.handleTouch(itemView,  e);
+            // Note: even though this event is being handled as part of a drag gesture, continue
+            // forwarding to the GestureDetector. The detector needs to see the entire cluster of
+            // events in order to properly interpret gestures.
+            onTouchEvent(e);
+        }
 
         @Override
         public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
index 1bfc6e9..2967a90 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DocumentHolder.java
@@ -75,11 +75,25 @@
      */
     public abstract void bind(Cursor cursor, String modelId, State state);
 
+    /**
+     * Makes the associated item view appear selected. Note that this merely affects the appearance
+     * of the view, it doesn't actually select the item.
+     *
+     * @param selected
+     */
     public void setSelected(boolean selected) {
         itemView.setActivated(selected);
         itemView.setBackgroundColor(selected ? mSelectedItemColor : mDefaultItemColor);
     }
 
+    /**
+     * Highlights the associated item view.
+     * @param highlighted
+     */
+    public void setHighlighted(boolean highlighted) {
+        itemView.setBackgroundColor(highlighted ? mSelectedItemColor : mDefaultItemColor);
+    }
+
     @Override
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         // Event listener should always be set.