Merge "Add Cut/Copy/Delete operation to currently focused item." into nyc-andromeda-dev
diff --git a/src/com/android/documentsui/AbstractActionHandler.java b/src/com/android/documentsui/AbstractActionHandler.java
index 1a26bcc..86093f9 100644
--- a/src/com/android/documentsui/AbstractActionHandler.java
+++ b/src/com/android/documentsui/AbstractActionHandler.java
@@ -38,6 +38,7 @@
 import com.android.documentsui.dirlist.AnimationView.AnimationType;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.files.LauncherActivity;
 import com.android.documentsui.queries.SearchViewManager;
 import com.android.documentsui.roots.LoadRootTask;
@@ -62,15 +63,18 @@
     protected final State mState;
     protected final RootsAccess mRoots;
     protected final DocumentsAccess mDocs;
+    protected final FocusHandler mFocusHandler;
     protected final SelectionManager mSelectionMgr;
     protected final SearchViewManager mSearchMgr;
     protected final Lookup<String, Executor> mExecutors;
 
+
     public AbstractActionHandler(
             T activity,
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors) {
@@ -78,6 +82,7 @@
         assert(activity != null);
         assert(state != null);
         assert(roots != null);
+        assert(focusHandler != null);
         assert(selectionMgr != null);
         assert(searchMgr != null);
         assert(docs != null);
@@ -86,6 +91,7 @@
         mState = state;
         mRoots = roots;
         mDocs = docs;
+        mFocusHandler = focusHandler;
         mSelectionMgr = selectionMgr;
         mSearchMgr = searchMgr;
         mExecutors = executors;
@@ -232,6 +238,16 @@
     }
 
     @Override
+    public void cutToClipboard() {
+        throw new UnsupportedOperationException("Cut not supported!");
+    }
+
+    @Override
+    public void copyToClipboard() {
+        throw new UnsupportedOperationException("Copy not supported!");
+    }
+
+    @Override
     public void deleteSelectedDocuments() {
         throw new UnsupportedOperationException("Delete not supported!");
     }
diff --git a/src/com/android/documentsui/ActionHandler.java b/src/com/android/documentsui/ActionHandler.java
index 7f6f923..d92ebbb 100644
--- a/src/com/android/documentsui/ActionHandler.java
+++ b/src/com/android/documentsui/ActionHandler.java
@@ -65,6 +65,13 @@
 
     void openContainerDocument(DocumentInfo doc);
 
+    void cutToClipboard();
+
+    void copyToClipboard();
+
+    /**
+     * In general, selected = selection or single focused item
+     */
     void deleteSelectedDocuments();
 
     void shareSelectedDocuments();
diff --git a/src/com/android/documentsui/BaseActivity.java b/src/com/android/documentsui/BaseActivity.java
index 186da09..6d08c43 100644
--- a/src/com/android/documentsui/BaseActivity.java
+++ b/src/com/android/documentsui/BaseActivity.java
@@ -666,20 +666,21 @@
      * locked, open/close it as appropriate.
      */
     void toggleNavDrawerFocus() {
+        boolean toogleHappened = false;
         if (mNavDrawerHasFocus) {
             mDrawer.setOpen(false);
             DirectoryFragment df = DirectoryFragment.get(getFragmentManager());
-            if (df != null) {
-                df.requestFocus();
-            }
+            assert (df != null);
+            toogleHappened = df.requestFocus();
         } else {
             mDrawer.setOpen(true);
             RootsFragment rf = RootsFragment.get(getFragmentManager());
-            if (rf != null) {
-                rf.requestFocus();
-            }
+            assert (rf != null);
+            toogleHappened = rf.requestFocus();
         }
-        mNavDrawerHasFocus = !mNavDrawerHasFocus;
+        if (toogleHappened) {
+            mNavDrawerHasFocus = !mNavDrawerHasFocus;
+        }
     }
 
     /**
diff --git a/src/com/android/documentsui/FocusManager.java b/src/com/android/documentsui/FocusManager.java
index 2371b7f..8f5d388 100644
--- a/src/com/android/documentsui/FocusManager.java
+++ b/src/com/android/documentsui/FocusManager.java
@@ -146,6 +146,16 @@
         return mScope.lastFocusPosition;
     }
 
+    @Override
+    public @Nullable String getFocusModelId() {
+        if (mScope.lastFocusPosition != RecyclerView.NO_POSITION) {
+            DocumentHolder holder = (DocumentHolder) mScope.view
+                    .findViewHolderForAdapterPosition(mScope.lastFocusPosition);
+            return holder.getModelId();
+        }
+        return null;
+    }
+
     /**
      * Finds the destination position where the focus should land for a given navigation event.
      *
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index fb45749..35252de 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -633,11 +633,11 @@
                 return true;
 
             case R.id.menu_cut_to_clipboard:
-                cutSelectedToClipboard();
+                mActions.cutToClipboard();
                 return true;
 
             case R.id.menu_copy_to_clipboard:
-                copySelectedToClipboard();
+                mActions.copyToClipboard();
                 return true;
 
             case R.id.menu_paste_from_clipboard:
@@ -838,34 +838,6 @@
         mRecView.requestFocus();
     }
 
-    public void copySelectedToClipboard() {
-        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_COPY_CLIPBOARD);
-
-        Selection selection = mSelectionMgr.getSelection(new Selection());
-        if (selection.isEmpty()) {
-            return;
-        }
-        mSelectionMgr.clearSelection();
-
-        mClipper.clipDocumentsForCopy(mModel::getItemUri, selection);
-
-        Snackbars.showDocumentsClipped(getActivity(), selection.size());
-    }
-
-    public void cutSelectedToClipboard() {
-        Metrics.logUserAction(getContext(), Metrics.USER_ACTION_CUT_CLIPBOARD);
-
-        Selection selection = mSelectionMgr.getSelection(new Selection());
-        if (selection.isEmpty()) {
-            return;
-        }
-        mSelectionMgr.clearSelection();
-
-        mClipper.clipDocumentsForCut(mModel::getItemUri, selection, mState.stack.peek());
-
-        Snackbars.showDocumentsClipped(getActivity(), selection.size());
-    }
-
     public void pasteFromClipboard() {
         Metrics.logUserAction(getContext(), Metrics.USER_ACTION_PASTE_CLIPBOARD);
 
@@ -920,8 +892,12 @@
     /**
      * Attempts to restore focus on the directory listing.
      */
-    public void requestFocus() {
+    public boolean requestFocus() {
+        if (mSelectionMgr.hasSelection()) {
+            return false;
+        }
         mFocusManager.restoreLastFocus();
+        return true;
     }
 
     private void setupDragAndDropOnDocumentView(View view, Cursor cursor) {
diff --git a/src/com/android/documentsui/dirlist/FocusHandler.java b/src/com/android/documentsui/dirlist/FocusHandler.java
index 0f43ac0..1cbb8a9 100644
--- a/src/com/android/documentsui/dirlist/FocusHandler.java
+++ b/src/com/android/documentsui/dirlist/FocusHandler.java
@@ -16,6 +16,7 @@
 
 package com.android.documentsui.dirlist;
 
+import android.annotation.Nullable;
 import android.view.KeyEvent;
 import android.view.View;
 
@@ -52,4 +53,8 @@
      */
     int getFocusPosition();
 
+    /**
+     * @return The modelId of the last focused item. If no item is focused, this should return null.
+     */
+    @Nullable String getFocusModelId();
 }
diff --git a/src/com/android/documentsui/files/ActionHandler.java b/src/com/android/documentsui/files/ActionHandler.java
index afea4cd..09f5ce1 100644
--- a/src/com/android/documentsui/files/ActionHandler.java
+++ b/src/com/android/documentsui/files/ActionHandler.java
@@ -48,6 +48,7 @@
 import com.android.documentsui.clipping.UrisSupplier;
 import com.android.documentsui.dirlist.AnimationView;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.dirlist.Model.Update;
 import com.android.documentsui.files.ActionHandler.Addons;
@@ -87,6 +88,7 @@
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors,
@@ -96,7 +98,7 @@
             DocumentClipper clipper,
             ClipStore clipStore) {
 
-        super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+        super(activity, state, roots, docs, focusHandler, selectionMgr, searchMgr, executors);
 
         mActionModeAddons = actionModeAddons;
         mDialogs = dialogs;
@@ -190,14 +192,57 @@
         return previewDocument(doc);
     }
 
+    private Selection getSelectedOrFocused() {
+        final Selection selection = this.getStableSelection();
+        if (selection.isEmpty()) {
+            String focusModelId = mFocusHandler.getFocusModelId();
+            if (focusModelId != null) {
+                selection.add(focusModelId);
+            }
+        }
+
+        return selection;
+    }
+
+    @Override
+    public void cutToClipboard() {
+        Metrics.logUserAction(mActivity, Metrics.USER_ACTION_CUT_CLIPBOARD);
+        Selection selection = getSelectedOrFocused();
+
+        if (selection.isEmpty()) {
+            return;
+        }
+        mSelectionMgr.clearSelection();
+
+        mClipper.clipDocumentsForCut(mScope.model::getItemUri, selection, mState.stack.peek());
+
+        mDialogs.showDocumentsClipped(selection.size());
+    }
+
+    @Override
+    public void copyToClipboard() {
+        Metrics.logUserAction(mActivity, Metrics.USER_ACTION_COPY_CLIPBOARD);
+        Selection selection = getSelectedOrFocused();
+
+        if (selection.isEmpty()) {
+            return;
+        }
+        mSelectionMgr.clearSelection();
+
+        mClipper.clipDocumentsForCopy(mScope.model::getItemUri, selection);
+
+        mDialogs.showDocumentsClipped(selection.size());
+    }
+
+
     @Override
     public void deleteSelectedDocuments() {
-        assert(mSelectionMgr.hasSelection());
-
         Metrics.logUserAction(mActivity, Metrics.USER_ACTION_DELETE);
+        Selection selection = getSelectedOrFocused();
 
-        Selection selection = getStableSelection();
-        assert(!selection.isEmpty());
+        if (selection.isEmpty()) {
+            return;
+        }
 
         final DocumentInfo srcParent = mState.stack.peek();
         assert(srcParent != null);
diff --git a/src/com/android/documentsui/files/ActivityInputHandler.java b/src/com/android/documentsui/files/ActivityInputHandler.java
index a7b0e2c..35653d2 100644
--- a/src/com/android/documentsui/files/ActivityInputHandler.java
+++ b/src/com/android/documentsui/files/ActivityInputHandler.java
@@ -18,31 +18,22 @@
 
 import android.view.KeyEvent;
 
-import com.android.documentsui.selection.SelectionManager;
-import com.android.documentsui.ActionHandler;
-
 /**
  * Used by {@link FilesActivity} to manage global keyboard shortcuts tied to file actions
  */
 final class ActivityInputHandler {
 
-    private final SelectionManager mSelectionMgr;
-    private final ActionHandler mActions;
+    private final Runnable mDeleteHandler;
 
-    ActivityInputHandler(SelectionManager selectionMgr, ActionHandler actionHandler) {
-        mSelectionMgr = selectionMgr;
-        mActions = actionHandler;
+    ActivityInputHandler(Runnable deleteHandler) {
+        mDeleteHandler = deleteHandler;
     }
 
     boolean onKeyDown(int keyCode, KeyEvent event) {
         if ((keyCode == KeyEvent.KEYCODE_DEL && event.isAltPressed())
                 || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
-            if (mSelectionMgr.hasSelection()) {
-                mActions.deleteSelectedDocuments();
-                return true;
-            } else {
-                return false;
-            }
+            mDeleteHandler.run();
+            return true;
         }
         return false;
     }
diff --git a/src/com/android/documentsui/files/FilesActivity.java b/src/com/android/documentsui/files/FilesActivity.java
index 42afa7f..7a2284f 100644
--- a/src/com/android/documentsui/files/FilesActivity.java
+++ b/src/com/android/documentsui/files/FilesActivity.java
@@ -56,6 +56,7 @@
 import com.android.documentsui.dirlist.DirectoryFragment;
 import com.android.documentsui.dirlist.DocumentsAdapter;
 import com.android.documentsui.dirlist.Model;
+import com.android.documentsui.selection.Selection;
 import com.android.documentsui.selection.SelectionManager;
 import com.android.documentsui.selection.SelectionManager.SelectionPredicate;
 import com.android.documentsui.services.FileOperationService;
@@ -124,6 +125,7 @@
                 mState,
                 mRoots,
                 mDocs,
+                mFocusManager,
                 mSelectionMgr,
                 mSearchManager,
                 ProviderExecutor::forAuthority,
@@ -133,7 +135,7 @@
                 mClipper,
                 DocumentsApplication.getClipStore(this));
 
-        mActivityInputHandler = new ActivityInputHandler(mSelectionMgr, mActions);
+        mActivityInputHandler = new ActivityInputHandler(mActions::deleteSelectedDocuments);
 
         RootsFragment.show(getFragmentManager(), null);
 
@@ -324,16 +326,10 @@
                 }
                 return true;
             case KeyEvent.KEYCODE_X:
-                dir = getDirectoryFragment();
-                if (dir != null) {
-                    dir.cutSelectedToClipboard();
-                }
+                mActions.cutToClipboard();
                 return true;
             case KeyEvent.KEYCODE_C:
-                dir = getDirectoryFragment();
-                if (dir != null) {
-                    dir.copySelectedToClipboard();
-                }
+                mActions.copyToClipboard();
                 return true;
             case KeyEvent.KEYCODE_V:
                 dir = getDirectoryFragment();
diff --git a/src/com/android/documentsui/picker/ActionHandler.java b/src/com/android/documentsui/picker/ActionHandler.java
index 6e1b9d4..1999381 100644
--- a/src/com/android/documentsui/picker/ActionHandler.java
+++ b/src/com/android/documentsui/picker/ActionHandler.java
@@ -40,6 +40,7 @@
 import com.android.documentsui.base.Shared;
 import com.android.documentsui.base.State;
 import com.android.documentsui.dirlist.DocumentDetails;
+import com.android.documentsui.dirlist.FocusHandler;
 import com.android.documentsui.dirlist.Model;
 import com.android.documentsui.dirlist.Model.Update;
 import com.android.documentsui.picker.ActionHandler.Addons;
@@ -66,12 +67,13 @@
             State state,
             RootsAccess roots,
             DocumentsAccess docs,
+            FocusHandler focusHandler,
             SelectionManager selectionMgr,
             SearchViewManager searchMgr,
             Lookup<String, Executor> executors,
             ActivityConfig activityConfig) {
 
-        super(activity, state, roots, docs, selectionMgr, searchMgr, executors);
+        super(activity, state, roots, docs, focusHandler, selectionMgr, searchMgr, executors);
 
         mConfig = activityConfig;
         mScope = new ContentScope(this::onModelLoaded);
diff --git a/src/com/android/documentsui/picker/PickActivity.java b/src/com/android/documentsui/picker/PickActivity.java
index 268d801..eee92e5 100644
--- a/src/com/android/documentsui/picker/PickActivity.java
+++ b/src/com/android/documentsui/picker/PickActivity.java
@@ -110,6 +110,7 @@
                 mState,
                 mRoots,
                 mDocs,
+                mFocusManager,
                 mSelectionMgr,
                 mSearchManager,
                 ProviderExecutor::forAuthority,
diff --git a/src/com/android/documentsui/sidebar/RootsFragment.java b/src/com/android/documentsui/sidebar/RootsFragment.java
index 8890fb3..4e9287e 100644
--- a/src/com/android/documentsui/sidebar/RootsFragment.java
+++ b/src/com/android/documentsui/sidebar/RootsFragment.java
@@ -357,8 +357,8 @@
     /**
      * Attempts to shift focus back to the navigation drawer.
      */
-    public void requestFocus() {
-        mList.requestFocus();
+    public boolean requestFocus() {
+        return mList.requestFocus();
     }
 
     private BaseActivity<?> getBaseActivity() {
diff --git a/src/com/android/documentsui/ui/DialogController.java b/src/com/android/documentsui/ui/DialogController.java
index 6c648a2..39bebfe 100644
--- a/src/com/android/documentsui/ui/DialogController.java
+++ b/src/com/android/documentsui/ui/DialogController.java
@@ -50,11 +50,17 @@
         public void showNoApplicationFound() {
             throw new UnsupportedOperationException();
         }
+
+        @Override
+        public void showDocumentsClipped(int size) {
+            throw new UnsupportedOperationException();
+        }
     };
 
     void confirmDelete(List<DocumentInfo> docs, ConfirmationCallback callback);
     void showFileOperationFailures(int status, int opType, int docCount);
     void showNoApplicationFound();
+    void showDocumentsClipped(int size);
 
     // Should be private, but Java doesn't like me treating an interface like a mini-package.
     public static final class RuntimeDialogController implements DialogController {
@@ -137,6 +143,11 @@
             Snackbars.makeSnackbar(
                     mActivity, R.string.toast_no_application, Snackbar.LENGTH_SHORT).show();
         }
+
+        @Override
+        public void showDocumentsClipped(int size) {
+            Snackbars.showDocumentsClipped(mActivity, size);
+        }
     }
 
     static DialogController create(Activity activity, MessageBuilder messages) {
diff --git a/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java b/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
index 45e0655..76db4e1 100644
--- a/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
+++ b/tests/common/com/android/documentsui/dirlist/TestFocusHandler.java
@@ -45,6 +45,11 @@
     }
 
     @Override
+    public String getFocusModelId() {
+        return null;
+    }
+
+    @Override
     public void focusDocument(String modelId) {
     }
 
diff --git a/tests/common/com/android/documentsui/testing/TestActionHandler.java b/tests/common/com/android/documentsui/testing/TestActionHandler.java
index 69f716c..bd8adac 100644
--- a/tests/common/com/android/documentsui/testing/TestActionHandler.java
+++ b/tests/common/com/android/documentsui/testing/TestActionHandler.java
@@ -40,6 +40,7 @@
                 env.state,
                 env.roots,
                 env.docs,
+                env.focusHandler,
                 env.selectionMgr,
                 env.searchViewManager,
                 (String authority) -> null);
diff --git a/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java b/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
index 6ffdf70..f94f33f 100644
--- a/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
+++ b/tests/common/com/android/documentsui/testing/TestConfirmationCallback.java
@@ -48,4 +48,8 @@
     public void assertCalled() {
         Assert.assertTrue(mCalled);
     }
+
+    public void assertNeverCalled() {
+        Assert.assertFalse(mCalled);
+    }
 }
diff --git a/tests/common/com/android/documentsui/testing/TestEnv.java b/tests/common/com/android/documentsui/testing/TestEnv.java
index e0af394..076e621 100644
--- a/tests/common/com/android/documentsui/testing/TestEnv.java
+++ b/tests/common/com/android/documentsui/testing/TestEnv.java
@@ -21,6 +21,7 @@
 
 import com.android.documentsui.base.DocumentInfo;
 import com.android.documentsui.base.State;
+import com.android.documentsui.dirlist.TestFocusHandler;
 import com.android.documentsui.dirlist.TestModel;
 import com.android.documentsui.selection.SelectionManager;
 
@@ -50,6 +51,7 @@
     public final State state = new State();
     public final TestRootsAccess roots = new TestRootsAccess();
     public final TestDocumentsAccess docs = new TestDocumentsAccess();
+    public final TestFocusHandler focusHandler = new TestFocusHandler();
     public final TestModel model;
     public final SelectionManager selectionMgr;
     public final TestSearchViewManager searchViewManager;
diff --git a/tests/common/com/android/documentsui/ui/TestDialogController.java b/tests/common/com/android/documentsui/ui/TestDialogController.java
index dbac0f5..e3135be 100644
--- a/tests/common/com/android/documentsui/ui/TestDialogController.java
+++ b/tests/common/com/android/documentsui/ui/TestDialogController.java
@@ -28,6 +28,7 @@
     public int mNextConfirmationCode;
     private boolean mFileOpFailed;
     private boolean mNoApplicationFound;
+    private boolean mDocumentsClipped;
 
     public TestDialogController() {
         // by default, always confirm
@@ -51,6 +52,11 @@
         mNoApplicationFound = true;
     }
 
+    @Override
+    public void showDocumentsClipped(int size) {
+        mDocumentsClipped = true;
+    }
+
     public void assertNoFileFailures() {
         Assert.assertFalse(mFileOpFailed);
     }
@@ -59,6 +65,10 @@
         Assert.assertFalse(mNoApplicationFound);
     }
 
+    public void assertDocumentsClippedNotShown() {
+        Assert.assertFalse(mDocumentsClipped);
+    }
+
     public void confirmNext() {
         mNextConfirmationCode = ConfirmationCallback.CONFIRM;
     }
diff --git a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
index 5ac2741..b3379bf 100644
--- a/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/AbstractActionHandlerTest.java
@@ -59,6 +59,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor) {
diff --git a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
index 271639b..c332bfd 100644
--- a/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActionHandlerTest.java
@@ -36,6 +36,7 @@
 import com.android.documentsui.base.DocumentStack;
 import com.android.documentsui.base.RootInfo;
 import com.android.documentsui.base.Shared;
+import com.android.documentsui.selection.Selection;
 import com.android.documentsui.testing.Roots;
 import com.android.documentsui.testing.TestConfirmationCallback;
 import com.android.documentsui.testing.TestEnv;
@@ -71,6 +72,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor,
@@ -112,13 +114,32 @@
     }
 
     @Test
-    public void testDeleteSelectedDocuments() {
+    public void testCutSelectedDocuments_NoGivenSelection() {
         mEnv.populateStack();
 
+        mEnv.selectionMgr.clearSelection();
+        mHandler.cutToClipboard();
+        mDialogs.assertDocumentsClippedNotShown();
+    }
+
+    @Test
+    public void testCopySelectedDocuments_NoGivenSelection() {
+        mEnv.populateStack();
+
+        mEnv.selectionMgr.clearSelection();
+        mHandler.copyToClipboard();
+        mDialogs.assertDocumentsClippedNotShown();
+    }
+
+    @Test
+    public void testDeleteSelectedDocuments_NoSelection() {
+        mEnv.populateStack();
+
+        mEnv.selectionMgr.clearSelection();
         mHandler.deleteSelectedDocuments();
         mDialogs.assertNoFileFailures();
-        mActivity.startService.assertCalled();
-        mActionModeAddons.finishOnConfirmed.assertConfirmed();
+        mActivity.startService.assertNotCalled();
+        mActionModeAddons.finishOnConfirmed.assertNeverCalled();
     }
 
     @Test
diff --git a/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java b/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
index 37a696f..b579dbe 100644
--- a/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
+++ b/tests/unit/com/android/documentsui/files/ActivityInputHandlerTest.java
@@ -16,7 +16,6 @@
 
 package com.android.documentsui.files;
 
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import android.support.test.filters.MediumTest;
@@ -24,50 +23,30 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
-import com.android.documentsui.dirlist.TestData;
-import com.android.documentsui.selection.SelectionManager;
-import com.android.documentsui.selection.SelectionProbe;
-import com.android.documentsui.testing.SelectionManagers;
-import com.android.documentsui.testing.TestActionHandler;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-
 @RunWith(AndroidJUnit4.class)
 @MediumTest
 public class ActivityInputHandlerTest {
 
-    private static final List<String> ITEMS = TestData.create(100);
-
-    private SelectionProbe mSelection;
-    private TestActionHandler mActionHandler;
     private ActivityInputHandler mActivityInputHandler;
+    private boolean mDeleteHappened;
 
     @Before
     public void setUp() {
-        SelectionManager selectionMgr = SelectionManagers.createTestInstance(ITEMS);
-        mSelection = new SelectionProbe(selectionMgr);
-        mActionHandler = new TestActionHandler();
-        mActivityInputHandler = new ActivityInputHandler(selectionMgr, mActionHandler);
+        mDeleteHappened = false;
+        mActivityInputHandler = new ActivityInputHandler(() -> {
+            mDeleteHappened = true;
+        });
     }
 
     @Test
-    public void testDelete_noSelection() {
-        KeyEvent event = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0,
-                KeyEvent.META_ALT_ON);
-        assertFalse(mActivityInputHandler.onKeyDown(event.getKeyCode(), event));
-        assertFalse(mActionHandler.mDeleteHappened);
-    }
-
-    @Test
-    public void testDelete_hasSelection() {
-        mSelection.select(1);
+    public void testDelete() {
         KeyEvent event = new KeyEvent(0, 0, MotionEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL, 0,
                 KeyEvent.META_ALT_ON);
         assertTrue(mActivityInputHandler.onKeyDown(event.getKeyCode(), event));
-        assertTrue(mActionHandler.mDeleteHappened);
+        assertTrue(mDeleteHappened);
     }
 }
diff --git a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
index 73cb123..5e26cd8 100644
--- a/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
+++ b/tests/unit/com/android/documentsui/picker/ActionHandlerTest.java
@@ -65,6 +65,7 @@
                 mEnv.state,
                 mEnv.roots,
                 mEnv.docs,
+                mEnv.focusHandler,
                 mEnv.selectionMgr,
                 mEnv.searchViewManager,
                 mEnv::lookupExecutor,