Loading src/com/android/documentsui/ItemDragListener.java +12 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.Timer; import java.util.TimerTask; import javax.annotation.Nullable; /** * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want * items to pop-open when user hovers on them during a drag n drop. Loading Loading @@ -59,7 +61,7 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { case DragEvent.ACTION_DRAG_STARTED: return true; case DragEvent.ACTION_DRAG_ENTERED: handleEnteredEvent(v); handleEnteredEvent(v, event); return true; case DragEvent.ACTION_DRAG_LOCATION: handleLocationEvent(v, event.getX(), event.getY()); Loading @@ -75,11 +77,12 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { return false; } private void handleEnteredEvent(View v) { private void handleEnteredEvent(View v, DragEvent event) { @Nullable TimerTask task = createOpenTask(v, event); if (task == null) { return; } mDragHost.setDropTargetHighlight(v, true); TimerTask task = createOpenTask(v); assert (task != null); v.setTag(R.id.drag_hovering_tag, task); mHoverTimer.schedule(task, SPRING_TIMEOUT); } Loading Loading @@ -110,8 +113,10 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { return handleDropEventChecked(v, event); } @VisibleForTesting TimerTask createOpenTask(final View v) { /** * Sub-classes such as {@link DirectoryDragListener} can override this method and return null. */ public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { TimerTask task = new TimerTask() { @Override public void run() { Loading src/com/android/documentsui/dirlist/DirectoryDragListener.java +10 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ import android.view.View; import com.android.documentsui.ItemDragListener; import java.util.TimerTask; import javax.annotation.Nullable; class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { DirectoryDragListener(DirectoryFragment fragment) { Loading @@ -45,4 +49,10 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { public boolean handleDropEventChecked(View v, DragEvent event) { return mDragHost.handleDropEvent(v, event); } @Override public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { return mDragHost.shouldCopyTo(event.getLocalState(), v) ? super.createOpenTask(v, event) : null; } } No newline at end of file src/com/android/documentsui/dirlist/DirectoryFragment.java +20 −9 Original line number Diff line number Diff line Loading @@ -1351,7 +1351,6 @@ public class DirectoryFragment extends Fragment if (getModelId(view) != null) { activity.springOpenDirectory(getDestination(view)); } activity.setRootsDrawerOpen(false); } Loading @@ -1364,13 +1363,7 @@ public class DirectoryFragment extends Fragment assert(DocumentClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); // Don't copy from the cwd into the cwd. Note: this currently doesn't work for // multi-window drag, because localState isn't carried over from one process to // another. Object src = event.getLocalState(); DocumentInfo dst = getDestination(v); if (Objects.equals(src, dst)) { if (DEBUG) Log.d(TAG, "Drop target same as source. Ignoring."); if (!shouldCopyTo(event.getLocalState(), v)) { return false; } Loading @@ -1380,13 +1373,31 @@ public class DirectoryFragment extends Fragment // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(getContext(), src == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = getDestination(v); mClipper.copyFromClipData(dst, getDisplayState().stack, clipData, activity.fileOpCallback); return true; } // Don't copy from the cwd into a provided list of prohibited directories. (ie. into cwd, into // a selected directory). Note: this currently doesn't work for multi-window drag, because // localState isn't carried over from one process to another. boolean shouldCopyTo(Object dragLocalState, View destinationView) { if (dragLocalState == null || !(dragLocalState instanceof List<?>)) { if (DEBUG) Log.d(TAG, "Invalid local state object. Will allow copy."); return true; } DocumentInfo dst = getDestination(destinationView); List<?> src = (List<?>) dragLocalState; if (src.contains(dst)) { if (DEBUG) Log.d(TAG, "Drop target same as source. Ignoring."); return false; } return true; } private DocumentInfo getDestination(View v) { String id = getModelId(v); if (id != null) { Loading src/com/android/documentsui/dirlist/DragStartListener.java +10 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperationService.OpType; import java.util.List; import java.util.function.Function; import javax.annotation.Nullable; Loading Loading @@ -70,6 +71,7 @@ interface DragStartListener { private final Function<View, String> mIdFinder; private final ClipDataFactory mClipFactory; private final Function<Selection, DragShadowBuilder> mShadowFactory; private Function<Selection, List<DocumentInfo>> mDocsConverter; // use DragStartListener.create @VisibleForTesting Loading @@ -78,6 +80,7 @@ interface DragStartListener { MultiSelectManager selectionMgr, ViewFinder viewFinder, Function<View, String> idFinder, Function<Selection, List<DocumentInfo>> docsConverter, ClipDataFactory clipFactory, Function<Selection, DragShadowBuilder> shadowFactory) { Loading @@ -85,6 +88,7 @@ interface DragStartListener { mSelectionMgr = selectionMgr; mViewFinder = viewFinder; mIdFinder = idFinder; mDocsConverter = docsConverter; mClipFactory = clipFactory; mShadowFactory = shadowFactory; } Loading Loading @@ -133,6 +137,8 @@ interface DragStartListener { mSelectionMgr.getSelection(selection); } final List<DocumentInfo> invalidDest = mDocsConverter.apply(selection); invalidDest.add(mState.stack.peek()); // NOTE: Preparation of the ClipData object can require a lot of time // and ideally should be done in the background. Unfortunately // the current code layout and framework assumptions don't support Loading @@ -143,7 +149,7 @@ interface DragStartListener { selection, FileOperationService.OPERATION_COPY), mShadowFactory.apply(selection), mState.stack.peek(), invalidDest, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE); Loading @@ -159,10 +165,10 @@ interface DragStartListener { View view, ClipData data, DragShadowBuilder shadowBuilder, DocumentInfo currentDirectory, Object localState, int flags) { view.startDragAndDrop(data, shadowBuilder, currentDirectory, flags); view.startDragAndDrop(data, shadowBuilder, localState, flags); } } Loading @@ -185,6 +191,7 @@ interface DragStartListener { selectionMgr, viewFinder, idFinder, model::getDocuments, (Selection selection, @OpType int operationType) -> { return clipper.getClipDataForDocuments( model::getItemUri, Loading tests/src/com/android/documentsui/ItemDragListenerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -177,8 +177,8 @@ public class ItemDragListenerTest { } @Override public TimerTask createOpenTask(View v) { TimerTask task = super.createOpenTask(v); public TimerTask createOpenTask(View v, DragEvent event) { TimerTask task = super.createOpenTask(v, event); TestTimer.Task testTask = new TestTimer.Task(task); return testTask; Loading Loading
src/com/android/documentsui/ItemDragListener.java +12 −7 Original line number Diff line number Diff line Loading @@ -29,6 +29,8 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.Timer; import java.util.TimerTask; import javax.annotation.Nullable; /** * An {@link OnDragListener} that adds support for "spring loading views". Use this when you want * items to pop-open when user hovers on them during a drag n drop. Loading Loading @@ -59,7 +61,7 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { case DragEvent.ACTION_DRAG_STARTED: return true; case DragEvent.ACTION_DRAG_ENTERED: handleEnteredEvent(v); handleEnteredEvent(v, event); return true; case DragEvent.ACTION_DRAG_LOCATION: handleLocationEvent(v, event.getX(), event.getY()); Loading @@ -75,11 +77,12 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { return false; } private void handleEnteredEvent(View v) { private void handleEnteredEvent(View v, DragEvent event) { @Nullable TimerTask task = createOpenTask(v, event); if (task == null) { return; } mDragHost.setDropTargetHighlight(v, true); TimerTask task = createOpenTask(v); assert (task != null); v.setTag(R.id.drag_hovering_tag, task); mHoverTimer.schedule(task, SPRING_TIMEOUT); } Loading Loading @@ -110,8 +113,10 @@ public class ItemDragListener<H extends DragHost> implements OnDragListener { return handleDropEventChecked(v, event); } @VisibleForTesting TimerTask createOpenTask(final View v) { /** * Sub-classes such as {@link DirectoryDragListener} can override this method and return null. */ public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { TimerTask task = new TimerTask() { @Override public void run() { Loading
src/com/android/documentsui/dirlist/DirectoryDragListener.java +10 −0 Original line number Diff line number Diff line Loading @@ -21,6 +21,10 @@ import android.view.View; import com.android.documentsui.ItemDragListener; import java.util.TimerTask; import javax.annotation.Nullable; class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { DirectoryDragListener(DirectoryFragment fragment) { Loading @@ -45,4 +49,10 @@ class DirectoryDragListener extends ItemDragListener<DirectoryFragment> { public boolean handleDropEventChecked(View v, DragEvent event) { return mDragHost.handleDropEvent(v, event); } @Override public @Nullable TimerTask createOpenTask(final View v, DragEvent event) { return mDragHost.shouldCopyTo(event.getLocalState(), v) ? super.createOpenTask(v, event) : null; } } No newline at end of file
src/com/android/documentsui/dirlist/DirectoryFragment.java +20 −9 Original line number Diff line number Diff line Loading @@ -1351,7 +1351,6 @@ public class DirectoryFragment extends Fragment if (getModelId(view) != null) { activity.springOpenDirectory(getDestination(view)); } activity.setRootsDrawerOpen(false); } Loading @@ -1364,13 +1363,7 @@ public class DirectoryFragment extends Fragment assert(DocumentClipper.getOpType(clipData) == FileOperationService.OPERATION_COPY); // Don't copy from the cwd into the cwd. Note: this currently doesn't work for // multi-window drag, because localState isn't carried over from one process to // another. Object src = event.getLocalState(); DocumentInfo dst = getDestination(v); if (Objects.equals(src, dst)) { if (DEBUG) Log.d(TAG, "Drop target same as source. Ignoring."); if (!shouldCopyTo(event.getLocalState(), v)) { return false; } Loading @@ -1380,13 +1373,31 @@ public class DirectoryFragment extends Fragment // The localState could also be null for copying from Recents in single window // mode, but Recents doesn't offer this functionality (no directories). Metrics.logUserAction(getContext(), src == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW event.getLocalState() == null ? Metrics.USER_ACTION_DRAG_N_DROP_MULTI_WINDOW : Metrics.USER_ACTION_DRAG_N_DROP); DocumentInfo dst = getDestination(v); mClipper.copyFromClipData(dst, getDisplayState().stack, clipData, activity.fileOpCallback); return true; } // Don't copy from the cwd into a provided list of prohibited directories. (ie. into cwd, into // a selected directory). Note: this currently doesn't work for multi-window drag, because // localState isn't carried over from one process to another. boolean shouldCopyTo(Object dragLocalState, View destinationView) { if (dragLocalState == null || !(dragLocalState instanceof List<?>)) { if (DEBUG) Log.d(TAG, "Invalid local state object. Will allow copy."); return true; } DocumentInfo dst = getDestination(destinationView); List<?> src = (List<?>) dragLocalState; if (src.contains(dst)) { if (DEBUG) Log.d(TAG, "Drop target same as source. Ignoring."); return false; } return true; } private DocumentInfo getDestination(View v) { String id = getModelId(v); if (id != null) { Loading
src/com/android/documentsui/dirlist/DragStartListener.java +10 −3 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.services.FileOperationService; import com.android.documentsui.services.FileOperationService.OpType; import java.util.List; import java.util.function.Function; import javax.annotation.Nullable; Loading Loading @@ -70,6 +71,7 @@ interface DragStartListener { private final Function<View, String> mIdFinder; private final ClipDataFactory mClipFactory; private final Function<Selection, DragShadowBuilder> mShadowFactory; private Function<Selection, List<DocumentInfo>> mDocsConverter; // use DragStartListener.create @VisibleForTesting Loading @@ -78,6 +80,7 @@ interface DragStartListener { MultiSelectManager selectionMgr, ViewFinder viewFinder, Function<View, String> idFinder, Function<Selection, List<DocumentInfo>> docsConverter, ClipDataFactory clipFactory, Function<Selection, DragShadowBuilder> shadowFactory) { Loading @@ -85,6 +88,7 @@ interface DragStartListener { mSelectionMgr = selectionMgr; mViewFinder = viewFinder; mIdFinder = idFinder; mDocsConverter = docsConverter; mClipFactory = clipFactory; mShadowFactory = shadowFactory; } Loading Loading @@ -133,6 +137,8 @@ interface DragStartListener { mSelectionMgr.getSelection(selection); } final List<DocumentInfo> invalidDest = mDocsConverter.apply(selection); invalidDest.add(mState.stack.peek()); // NOTE: Preparation of the ClipData object can require a lot of time // and ideally should be done in the background. Unfortunately // the current code layout and framework assumptions don't support Loading @@ -143,7 +149,7 @@ interface DragStartListener { selection, FileOperationService.OPERATION_COPY), mShadowFactory.apply(selection), mState.stack.peek(), invalidDest, View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE); Loading @@ -159,10 +165,10 @@ interface DragStartListener { View view, ClipData data, DragShadowBuilder shadowBuilder, DocumentInfo currentDirectory, Object localState, int flags) { view.startDragAndDrop(data, shadowBuilder, currentDirectory, flags); view.startDragAndDrop(data, shadowBuilder, localState, flags); } } Loading @@ -185,6 +191,7 @@ interface DragStartListener { selectionMgr, viewFinder, idFinder, model::getDocuments, (Selection selection, @OpType int operationType) -> { return clipper.getClipDataForDocuments( model::getItemUri, Loading
tests/src/com/android/documentsui/ItemDragListenerTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -177,8 +177,8 @@ public class ItemDragListenerTest { } @Override public TimerTask createOpenTask(View v) { TimerTask task = super.createOpenTask(v); public TimerTask createOpenTask(View v, DragEvent event) { TimerTask task = super.createOpenTask(v, event); TestTimer.Task testTask = new TestTimer.Task(task); return testTask; Loading