1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui.files;
18 
19 import static com.android.documentsui.testing.IntentAsserts.assertHasAction;
20 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraIntent;
21 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraList;
22 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraUri;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertSame;
28 import static org.junit.Assert.assertTrue;
29 
30 import android.app.Activity;
31 import android.app.PendingIntent;
32 import android.content.ClipData;
33 import android.content.Intent;
34 import android.net.Uri;
35 import android.os.Parcelable;
36 import android.provider.DocumentsContract;
37 import android.provider.DocumentsContract.Path;
38 import android.support.test.filters.MediumTest;
39 import android.support.test.InstrumentationRegistry;
40 import android.support.test.runner.AndroidJUnit4;
41 import android.view.DragEvent;
42 
43 import com.android.documentsui.AbstractActionHandler;
44 import com.android.documentsui.R;
45 import com.android.documentsui.TestActionModeAddons;
46 import com.android.documentsui.archives.ArchivesProvider;
47 import com.android.documentsui.base.DocumentInfo;
48 import com.android.documentsui.base.DocumentStack;
49 import com.android.documentsui.base.RootInfo;
50 import com.android.documentsui.base.Shared;
51 import com.android.documentsui.testing.ClipDatas;
52 import com.android.documentsui.testing.DocumentStackAsserts;
53 import com.android.documentsui.testing.Roots;
54 import com.android.documentsui.testing.TestActivityConfig;
55 import com.android.documentsui.testing.TestConfirmationCallback;
56 import com.android.documentsui.testing.TestDocumentClipper;
57 import com.android.documentsui.testing.TestEnv;
58 import com.android.documentsui.testing.TestProvidersAccess;
59 import com.android.documentsui.ui.TestDialogController;
60 
61 import org.junit.Before;
62 import org.junit.Test;
63 import org.junit.runner.RunWith;
64 
65 import java.util.ArrayList;
66 import java.util.Arrays;
67 import java.util.List;
68 
69 @RunWith(AndroidJUnit4.class)
70 @MediumTest
71 public class ActionHandlerTest {
72 
73     private TestEnv mEnv;
74     private TestActivity mActivity;
75     private TestActionModeAddons mActionModeAddons;
76     private TestDialogController mDialogs;
77     private TestConfirmationCallback mCallback;
78     private ActionHandler<TestActivity> mHandler;
79     private TestDocumentClipper mClipper;
80     private boolean refreshAnswer = false;
81 
82     @Before
setUp()83     public void setUp() {
84         mEnv = TestEnv.create();
85         mActivity = TestActivity.create(mEnv);
86         mActionModeAddons = new TestActionModeAddons();
87         mDialogs = new TestDialogController();
88         mCallback = new TestConfirmationCallback();
89         mEnv.roots.configurePm(mActivity.packageMgr);
90         ((TestActivityConfig) mEnv.injector.config).nextDocumentEnabled = true;
91         mEnv.injector.dialogs = mDialogs;
92         mClipper = new TestDocumentClipper();
93 
94         mHandler = createHandler();
95 
96         mDialogs.confirmNext();
97 
98         mEnv.selectDocument(TestEnv.FILE_GIF);
99     }
100 
101     @Test
testOpenSelectedInNewWindow()102     public void testOpenSelectedInNewWindow() {
103         mHandler.openSelectedInNewWindow();
104 
105         DocumentStack path = new DocumentStack(Roots.create("123"), mEnv.model.getDocument("1"));
106 
107         Intent expected = LauncherActivity.createLaunchIntent(mActivity);
108         expected.putExtra(Shared.EXTRA_STACK, (Parcelable) path);
109 
110         Intent actual = mActivity.startActivity.getLastValue();
111         assertEquals(expected.toString(), actual.toString());
112     }
113 
114     @Test
testSpringOpenDirectory()115     public void testSpringOpenDirectory() {
116         mHandler.springOpenDirectory(TestEnv.FOLDER_0);
117         assertTrue(mActionModeAddons.finishActionModeCalled);
118         assertEquals(TestEnv.FOLDER_0, mEnv.state.stack.peek());
119     }
120 
121     @Test
testCutSelectedDocuments_NoGivenSelection()122     public void testCutSelectedDocuments_NoGivenSelection() {
123         mEnv.populateStack();
124 
125         mEnv.selectionMgr.clearSelection();
126         mHandler.cutToClipboard();
127         mDialogs.assertDocumentsClippedNotShown();
128     }
129 
130     @Test
testCopySelectedDocuments_NoGivenSelection()131     public void testCopySelectedDocuments_NoGivenSelection() {
132         mEnv.populateStack();
133 
134         mEnv.selectionMgr.clearSelection();
135         mHandler.copyToClipboard();
136         mDialogs.assertDocumentsClippedNotShown();
137     }
138 
139     @Test
testDeleteSelectedDocuments_NoSelection()140     public void testDeleteSelectedDocuments_NoSelection() {
141         mEnv.populateStack();
142 
143         mEnv.selectionMgr.clearSelection();
144         mHandler.deleteSelectedDocuments();
145         mDialogs.assertNoFileFailures();
146         mActivity.startService.assertNotCalled();
147         mActionModeAddons.finishOnConfirmed.assertNeverCalled();
148     }
149 
150     @Test
testDeleteSelectedDocuments_Cancelable()151     public void testDeleteSelectedDocuments_Cancelable() {
152         mEnv.populateStack();
153 
154         mDialogs.rejectNext();
155         mHandler.deleteSelectedDocuments();
156         mDialogs.assertNoFileFailures();
157         mActivity.startService.assertNotCalled();
158         mActionModeAddons.finishOnConfirmed.assertRejected();
159     }
160 
161     // Recents root means when deleting the srcParent will be null.
162     @Test
testDeleteSelectedDocuments_RecentsRoot()163     public void testDeleteSelectedDocuments_RecentsRoot() {
164         mEnv.state.stack.changeRoot(TestProvidersAccess.RECENTS);
165 
166         mHandler.deleteSelectedDocuments();
167         mDialogs.assertNoFileFailures();
168         mActivity.startService.assertCalled();
169         mActionModeAddons.finishOnConfirmed.assertCalled();
170     }
171 
172     @Test
testShareSelectedDocuments_ShowsChooser()173     public void testShareSelectedDocuments_ShowsChooser() {
174         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
175         mHandler.shareSelectedDocuments();
176 
177         mActivity.assertActivityStarted(Intent.ACTION_CHOOSER);
178     }
179 
180     @Test
testShareSelectedDocuments_Single()181     public void testShareSelectedDocuments_Single() {
182         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
183         mHandler.shareSelectedDocuments();
184 
185         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
186         assertHasAction(intent, Intent.ACTION_SEND);
187         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
188         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
189         assertHasExtraUri(intent, Intent.EXTRA_STREAM);
190     }
191 
192     @Test
testShareSelectedDocuments_ArchivedFile()193     public void testShareSelectedDocuments_ArchivedFile() {
194         mEnv = TestEnv.create(ArchivesProvider.AUTHORITY);
195         mHandler = createHandler();
196 
197         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
198         mEnv.selectionMgr.clearSelection();
199         mEnv.selectDocument(TestEnv.FILE_PDF);
200         mHandler.shareSelectedDocuments();
201 
202         Intent intent = mActivity.startActivity.getLastValue();
203         assertNull(intent);
204     }
205 
206     @Test
testShareSelectedDocuments_Multiple()207     public void testShareSelectedDocuments_Multiple() {
208         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
209         mEnv.selectDocument(TestEnv.FILE_PDF);
210         mHandler.shareSelectedDocuments();
211 
212         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
213         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
214         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
215         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
216         assertHasExtraList(intent, Intent.EXTRA_STREAM, 2);
217     }
218 
219     @Test
testShareSelectedDocuments_VirtualFiles()220     public void testShareSelectedDocuments_VirtualFiles() {
221         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
222         mEnv.selectionMgr.clearSelection();
223         mEnv.selectDocument(TestEnv.FILE_VIRTUAL);
224         mHandler.shareSelectedDocuments();
225 
226         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
227         assertHasAction(intent, Intent.ACTION_SEND);
228         assertTrue(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
229         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
230         assertHasExtraUri(intent, Intent.EXTRA_STREAM);
231     }
232 
233     @Test
testShareSelectedDocuments_RegularAndVirtualFiles()234     public void testShareSelectedDocuments_RegularAndVirtualFiles() {
235         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
236         mEnv.selectDocument(TestEnv.FILE_PNG);
237         mEnv.selectDocument(TestEnv.FILE_VIRTUAL);
238         mHandler.shareSelectedDocuments();
239 
240         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
241         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
242         assertTrue(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
243         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
244         assertHasExtraList(intent, Intent.EXTRA_STREAM, 3);
245     }
246 
247     @Test
testShareSelectedDocuments_OmitsPartialFiles()248     public void testShareSelectedDocuments_OmitsPartialFiles() {
249         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
250         mEnv.selectDocument(TestEnv.FILE_PARTIAL);
251         mEnv.selectDocument(TestEnv.FILE_PNG);
252         mHandler.shareSelectedDocuments();
253 
254         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
255         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
256         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
257         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
258         assertHasExtraList(intent, Intent.EXTRA_STREAM, 2);
259     }
260 
261     @Test
testDocumentPicked_DefaultsToView()262     public void testDocumentPicked_DefaultsToView() throws Exception {
263         mActivity.currentRoot = TestProvidersAccess.HOME;
264 
265         mHandler.openDocument(TestEnv.FILE_GIF, ActionHandler.VIEW_TYPE_PREVIEW,
266                 ActionHandler.VIEW_TYPE_REGULAR);
267         mActivity.assertActivityStarted(Intent.ACTION_VIEW);
268     }
269 
270     @Test
testDocumentPicked_InArchive_QuickViewable()271     public void testDocumentPicked_InArchive_QuickViewable() throws Exception {
272         mActivity.resources.setQuickViewerPackage("corptropolis.viewer");
273         mActivity.currentRoot = TestProvidersAccess.HOME;
274 
275         mHandler.openDocument(TestEnv.FILE_IN_ARCHIVE, ActionHandler.VIEW_TYPE_PREVIEW,
276                 ActionHandler.VIEW_TYPE_REGULAR);
277         mActivity.assertActivityStarted(Intent.ACTION_QUICK_VIEW);
278     }
279 
280     @Test
testDocumentPicked_InArchive_Unopenable()281     public void testDocumentPicked_InArchive_Unopenable() throws Exception {
282         mActivity.currentRoot = TestProvidersAccess.HOME;
283 
284         mHandler.openDocument(TestEnv.FILE_IN_ARCHIVE, ActionHandler.VIEW_TYPE_PREVIEW,
285                 ActionHandler.VIEW_TYPE_REGULAR);
286         mDialogs.assertViewInArchivesShownUnsupported();
287     }
288 
289     @Test
testDocumentPicked_PreviewsWhenResourceSet()290     public void testDocumentPicked_PreviewsWhenResourceSet() throws Exception {
291         mActivity.resources.setQuickViewerPackage("corptropolis.viewer");
292         mActivity.currentRoot = TestProvidersAccess.HOME;
293 
294         mHandler.openDocument(TestEnv.FILE_GIF, ActionHandler.VIEW_TYPE_PREVIEW,
295                 ActionHandler.VIEW_TYPE_REGULAR);
296         mActivity.assertActivityStarted(Intent.ACTION_QUICK_VIEW);
297     }
298 
299     @Test
testDocumentPicked_Downloads_ManagesApks()300     public void testDocumentPicked_Downloads_ManagesApks() throws Exception {
301         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
302 
303         mHandler.openDocument(TestEnv.FILE_APK, ActionHandler.VIEW_TYPE_PREVIEW,
304                 ActionHandler.VIEW_TYPE_REGULAR);
305         mActivity.assertActivityStarted(DocumentsContract.ACTION_MANAGE_DOCUMENT);
306     }
307 
308     @Test
testDocumentPicked_Downloads_ManagesPartialFiles()309     public void testDocumentPicked_Downloads_ManagesPartialFiles() throws Exception {
310         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
311 
312         mHandler.openDocument(TestEnv.FILE_PARTIAL, ActionHandler.VIEW_TYPE_PREVIEW,
313                 ActionHandler.VIEW_TYPE_REGULAR);
314         mActivity.assertActivityStarted(DocumentsContract.ACTION_MANAGE_DOCUMENT);
315     }
316 
317     @Test
testDocumentPicked_OpensArchives()318     public void testDocumentPicked_OpensArchives() throws Exception {
319         mActivity.currentRoot = TestProvidersAccess.HOME;
320         mEnv.docs.nextDocument = TestEnv.FILE_ARCHIVE;
321 
322         mHandler.openDocument(TestEnv.FILE_ARCHIVE, ActionHandler.VIEW_TYPE_PREVIEW,
323                 ActionHandler.VIEW_TYPE_REGULAR);
324         assertEquals(TestEnv.FILE_ARCHIVE, mEnv.state.stack.peek());
325     }
326 
327     @Test
testDocumentPicked_OpensDirectories()328     public void testDocumentPicked_OpensDirectories() throws Exception {
329         mActivity.currentRoot = TestProvidersAccess.HOME;
330 
331         mHandler.openDocument(TestEnv.FOLDER_1, ActionHandler.VIEW_TYPE_PREVIEW,
332                 ActionHandler.VIEW_TYPE_REGULAR);
333         assertEquals(TestEnv.FOLDER_1, mEnv.state.stack.peek());
334     }
335 
336     @Test
testShowChooser()337     public void testShowChooser() throws Exception {
338         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
339 
340         mHandler.showChooserForDoc(TestEnv.FILE_PDF);
341         mActivity.assertActivityStarted(Intent.ACTION_CHOOSER);
342     }
343 
344     @Test
testInitLocation_DefaultsToDownloads()345     public void testInitLocation_DefaultsToDownloads() throws Exception {
346         mActivity.resources.bools.put(R.bool.show_documents_root, false);
347 
348         mHandler.initLocation(mActivity.getIntent());
349         assertRootPicked(TestProvidersAccess.DOWNLOADS.getUri());
350     }
351 
352     @Test
testInitLocation_DocumentsRootEnabled()353     public void testInitLocation_DocumentsRootEnabled() throws Exception {
354         mActivity.resources.bools.put(R.bool.show_documents_root, true);
355 
356         mHandler.initLocation(mActivity.getIntent());
357         assertRootPicked(TestProvidersAccess.HOME.getUri());
358     }
359 
360     @Test
testInitLocation_BrowseRoot()361     public void testInitLocation_BrowseRoot() throws Exception {
362         Intent intent = mActivity.getIntent();
363         intent.setAction(Intent.ACTION_VIEW);
364         intent.setData(TestProvidersAccess.PICKLES.getUri());
365 
366         mHandler.initLocation(intent);
367         assertRootPicked(TestProvidersAccess.PICKLES.getUri());
368     }
369 
370     @Test
testInitLocation_LaunchToDocuments()371     public void testInitLocation_LaunchToDocuments() throws Exception {
372         mEnv.docs.nextIsDocumentsUri = true;
373         mEnv.docs.nextPath = new Path(
374                 TestProvidersAccess.HOME.rootId,
375                 Arrays.asList(
376                         TestEnv.FOLDER_0.documentId,
377                         TestEnv.FOLDER_1.documentId));
378         mEnv.docs.nextDocuments =
379                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1);
380 
381         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
382         Intent intent = mActivity.getIntent();
383         intent.setAction(Intent.ACTION_VIEW);
384         intent.setData(TestEnv.FOLDER_1.derivedUri);
385         mHandler.initLocation(intent);
386 
387         mEnv.beforeAsserts();
388 
389         DocumentStackAsserts.assertEqualsTo(mEnv.state.stack, TestProvidersAccess.HOME,
390                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
391         mActivity.refreshCurrentRootAndDirectory.assertCalled();
392     }
393 
394     @Test
testDragAndDrop_OnReadOnlyRoot()395     public void testDragAndDrop_OnReadOnlyRoot() throws Exception {
396         RootInfo root = new RootInfo(); // root by default has no SUPPORT_CREATE flag
397         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, null, null, null,
398                 null, true);
399         assertFalse(mHandler.dropOn(event, root));
400     }
401 
402     @Test
testDragAndDrop_OnLibraryRoot()403     public void testDragAndDrop_OnLibraryRoot() throws Exception {
404         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, null, null, null,
405                 null, true);
406         assertFalse(mHandler.dropOn(event, TestProvidersAccess.RECENTS));
407     }
408 
409     @Test
testClipper_suppliedCorrectClipData()410     public void testClipper_suppliedCorrectClipData() throws Exception {
411         // DragEvent gets recycled in Android, so it is possible that by the time the callback is
412         // called, event.getLocalState() and event.getClipData() returns null. This tests to ensure
413         // our Clipper is getting the original CipData passed in.
414         mEnv.docs.nextRootDocument = TestEnv.FOLDER_0;
415         mHandler = new ActionHandler<>(
416                 mActivity,
417                 mEnv.state,
418                 mEnv.roots,
419                 mEnv.docs,
420                 mEnv.searchViewManager,
421                 mEnv::lookupExecutor,
422                 mActionModeAddons,
423                 mClipper,
424                 null,
425                 mEnv.injector
426         );
427         Object localState = new Object();
428         ClipData clipData = ClipDatas.createTestClipData();
429         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, localState, null, clipData,
430                 null, true);
431         assertSame(localState, event.getLocalState());
432         assertSame(clipData, event.getClipData());
433 
434         mHandler.dropOn(event, TestProvidersAccess.DOWNLOADS);
435         event.recycle();
436 
437         mEnv.beforeAsserts();
438 
439         mClipper.assertSameClipData(clipData);
440     }
441 
442     @Test
testClipper_notCalledIfDestInSelection()443     public void testClipper_notCalledIfDestInSelection() throws Exception {
444         mHandler = new ActionHandler<>(
445                 mActivity,
446                 mEnv.state,
447                 mEnv.roots,
448                 mEnv.docs,
449                 mEnv.searchViewManager,
450                 mEnv::lookupExecutor,
451                 mActionModeAddons,
452                 mClipper,
453                 null,
454                 mEnv.injector
455         );
456         List<DocumentInfo> localState = new ArrayList<>();
457         localState.add(mEnv.docs.getRootDocument(TestProvidersAccess.DOWNLOADS));
458         ClipData clipData = ClipDatas.createTestClipData();
459         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, localState, null, clipData,
460                 null, true);
461 
462         mHandler.dropOn(event, TestProvidersAccess.DOWNLOADS);
463 
464         mEnv.beforeAsserts();
465 
466         mClipper.assertNoClipData();
467     }
468 
469     @Test
testRefresh_nullUri()470     public void testRefresh_nullUri() throws Exception {
471         refreshAnswer = true;
472         mHandler.refreshDocument(null, (boolean answer) -> {
473             refreshAnswer = answer;
474         });
475 
476         mEnv.beforeAsserts();
477         assertFalse(refreshAnswer);
478     }
479 
480     @Test
testRefresh_emptyStack()481     public void testRefresh_emptyStack() throws Exception {
482         refreshAnswer = true;
483         assertTrue(mEnv.state.stack.isEmpty());
484         mHandler.refreshDocument(new DocumentInfo(), (boolean answer) -> {
485             refreshAnswer = answer;
486         });
487 
488         mEnv.beforeAsserts();
489         assertFalse(refreshAnswer);
490     }
491 
492     @Test
testRefresh()493     public void testRefresh() throws Exception {
494         refreshAnswer = false;
495         mEnv.populateStack();
496         mHandler.refreshDocument(mEnv.model.getDocument("1"), (boolean answer) -> {
497             refreshAnswer = answer;
498         });
499 
500         mEnv.beforeAsserts();
501         assertTrue(refreshAnswer);
502     }
503 
504     @Test
testAuthentication()505     public void testAuthentication() throws Exception {
506         PendingIntent intent = PendingIntent.getActivity(
507                 InstrumentationRegistry.getInstrumentation().getTargetContext(), 0, new Intent(),
508                 0);
509 
510         mHandler.startAuthentication(intent);
511         assertEquals(intent.getIntentSender(), mActivity.startIntentSender.getLastValue().first);
512         assertEquals(AbstractActionHandler.CODE_AUTHENTICATION,
513                 mActivity.startIntentSender.getLastValue().second.intValue());
514     }
515 
516     @Test
testOnActivityResult_onOK()517     public void testOnActivityResult_onOK() throws Exception {
518         mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION, Activity.RESULT_OK,
519                 null);
520         mActivity.refreshCurrentRootAndDirectory.assertCalled();
521     }
522 
523     @Test
testOnActivityResult_onNotOK()524     public void testOnActivityResult_onNotOK() throws Exception {
525         mHandler.onActivityResult(0, Activity.RESULT_OK, null);
526         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
527 
528         mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION,
529                 Activity.RESULT_CANCELED, null);
530         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
531     }
532 
assertRootPicked(Uri expectedUri)533     private void assertRootPicked(Uri expectedUri) throws Exception {
534         mEnv.beforeAsserts();
535 
536         mActivity.rootPicked.assertCalled();
537         RootInfo root = mActivity.rootPicked.getLastValue();
538         assertNotNull(root);
539         assertEquals(expectedUri, root.getUri());
540     }
541 
createHandler()542     private ActionHandler<TestActivity> createHandler() {
543         return new ActionHandler<>(
544                 mActivity,
545                 mEnv.state,
546                 mEnv.roots,
547                 mEnv.docs,
548                 mEnv.searchViewManager,
549                 mEnv::lookupExecutor,
550                 mActionModeAddons,
551                 new TestDocumentClipper(),
552                 null,  // clip storage, not utilized unless we venture into *jumbo* clip territory.
553                 mEnv.injector
554         );
555     }
556 }
557