From e3dfebf8464e8608ff790cca67609aff7d8c2820 Mon Sep 17 00:00:00 2001 From: Ben Kwa Date: Thu, 18 Feb 2016 16:45:45 -0800 Subject: 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 --- packages/DocumentsUI/Android.mk | 1 + .../documentsui/dirlist/DirectoryFragment.java | 75 +++++++++++++++------- .../documentsui/dirlist/DocumentHolder.java | 14 ++++ 3 files changed, 66 insertions(+), 24 deletions(-) diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk index 1a4e3eb18e14..e1650e1bb411 100644 --- a/packages/DocumentsUI/Android.mk +++ b/packages/DocumentsUI/Android.mk @@ -8,6 +8,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) 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 7138c2d19fe6..f24fcc9d940f 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; 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.MenuItem; 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 @@ public class DirectoryFragment extends Fragment } 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 @@ public class DirectoryFragment extends Fragment view.setOnDragListener(mOnDragListener); } - view.setOnLongClickListener(mLongClickListener); + view.setOnLongClickListener(mDragHelper); } private View.OnDragListener mOnDragListener = new View.OnDragListener() { @@ -1062,11 +1063,15 @@ public class DirectoryFragment extends Fragment // 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 @@ public class DirectoryFragment extends Fragment } 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 @@ public class DirectoryFragment extends Fragment * 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; - } - } - ViewParent parent = view.getParent(); - if (parent == null || !(parent instanceof View)) { - return null; + View itemView = mRecView.findContainingItemView(view); + if (itemView != null) { + RecyclerView.ViewHolder vh = mRecView.getChildViewHolder(itemView); + if (vh instanceof DocumentHolder) { + return ((DocumentHolder) vh).modelId; } - view = (View) parent; } + return null; } private List getDraggableDocuments(View currentItemView) { @@ -1299,10 +1308,10 @@ public class DirectoryFragment extends Fragment } } - 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 docs = getDraggableDocuments(v); if (docs.isEmpty()) { return false; @@ -1328,9 +1337,12 @@ public class DirectoryFragment extends Fragment 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 @@ public class DirectoryFragment extends Fragment @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 1bfc6e909c5a..2967a908678d 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 class DocumentHolder */ 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. -- cgit v1.2.3-59-g8ed1b From 1b685e5363cde6bcbf6d71409f6ab46a9fd10a1e Mon Sep 17 00:00:00 2001 From: Ben Kwa Date: Wed, 24 Feb 2016 10:05:53 -0800 Subject: Clean up after a drop event. - Clear the drop target highlight. - Exit selection mode. BUG=27296889 Change-Id: I869bd01cb49555b28b19164638b4c0af48ca8583 --- .../src/com/android/documentsui/dirlist/DirectoryFragment.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java index f24fcc9d940f..e1b4a835c88c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/dirlist/DirectoryFragment.java @@ -1065,10 +1065,10 @@ public class DirectoryFragment extends Fragment // TODO: Expand drop target directory on hover? case DragEvent.ACTION_DRAG_ENTERED: - highlightDropTarget(v, true); + setDropTargetHighlight(v, true); return true; case DragEvent.ACTION_DRAG_EXITED: - highlightDropTarget(v, false); + setDropTargetHighlight(v, false); return true; case DragEvent.ACTION_DRAG_LOCATION: @@ -1085,12 +1085,15 @@ public class DirectoryFragment extends Fragment // TODO: Do not drop into the directory where the documents came from. } copyFromClipData(event.getClipData(), dstDir); + // Clean up the UI. + setDropTargetHighlight(v, false); + mSelectionManager.clearSelection(); return true; } return false; } - private void highlightDropTarget(View v, boolean highlight) { + private void setDropTargetHighlight(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) { -- cgit v1.2.3-59-g8ed1b