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.assertHasData;
21 import static com.android.documentsui.testing.IntentAsserts.assertHasExtra;
22 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraIntent;
23 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraList;
24 import static com.android.documentsui.testing.IntentAsserts.assertHasExtraUri;
25 import static com.android.documentsui.testing.IntentAsserts.assertTargetsComponent;
26 
27 import static org.junit.Assert.assertEquals;
28 import static org.junit.Assert.assertFalse;
29 import static org.junit.Assert.assertNotNull;
30 import static org.junit.Assert.assertNull;
31 import static org.junit.Assert.assertSame;
32 import static org.junit.Assert.assertTrue;
33 import static org.junit.Assume.assumeTrue;
34 
35 import android.app.Activity;
36 import android.app.DownloadManager;
37 import android.app.PendingIntent;
38 import android.content.ClipData;
39 import android.content.Intent;
40 import android.net.Uri;
41 import android.os.Parcelable;
42 import android.provider.DocumentsContract;
43 import android.provider.DocumentsContract.Path;
44 import android.util.Pair;
45 import android.view.DragEvent;
46 
47 import androidx.core.util.Preconditions;
48 import androidx.test.InstrumentationRegistry;
49 import androidx.test.filters.MediumTest;
50 
51 import com.android.documentsui.AbstractActionHandler;
52 import com.android.documentsui.ModelId;
53 import com.android.documentsui.R;
54 import com.android.documentsui.TestActionModeAddons;
55 import com.android.documentsui.TestConfigStore;
56 import com.android.documentsui.archives.ArchivesProvider;
57 import com.android.documentsui.base.DebugFlags;
58 import com.android.documentsui.base.DocumentInfo;
59 import com.android.documentsui.base.DocumentStack;
60 import com.android.documentsui.base.RootInfo;
61 import com.android.documentsui.base.Shared;
62 import com.android.documentsui.inspector.InspectorActivity;
63 import com.android.documentsui.testing.ClipDatas;
64 import com.android.documentsui.testing.DocumentStackAsserts;
65 import com.android.documentsui.testing.Roots;
66 import com.android.documentsui.testing.TestActivityConfig;
67 import com.android.documentsui.testing.TestDocumentClipper;
68 import com.android.documentsui.testing.TestDragAndDropManager;
69 import com.android.documentsui.testing.TestEnv;
70 import com.android.documentsui.testing.TestFeatures;
71 import com.android.documentsui.testing.TestProvidersAccess;
72 import com.android.documentsui.testing.UserManagers;
73 import com.android.documentsui.ui.TestDialogController;
74 import com.android.documentsui.util.VersionUtils;
75 import com.android.modules.utils.build.SdkLevel;
76 
77 import com.google.common.collect.Lists;
78 
79 import org.junit.Before;
80 import org.junit.Ignore;
81 import org.junit.Test;
82 import org.junit.runner.RunWith;
83 import org.junit.runners.Parameterized;
84 import org.junit.runners.Parameterized.Parameter;
85 import org.junit.runners.Parameterized.Parameters;
86 
87 import java.util.ArrayList;
88 import java.util.Arrays;
89 import java.util.List;
90 
91 @RunWith(Parameterized.class)
92 @MediumTest
93 public class ActionHandlerTest {
94 
95     private TestEnv mEnv;
96     private TestActivity mActivity;
97     private TestActionModeAddons mActionModeAddons;
98     private TestDialogController mDialogs;
99     private ActionHandler<TestActivity> mHandler;
100     private TestDocumentClipper mClipper;
101     private TestDragAndDropManager mDragAndDropManager;
102     private TestFeatures mFeatures;
103     private TestConfigStore mTestConfigStore;
104     private boolean refreshAnswer = false;
105 
106     @Parameter(0)
107     public boolean isPrivateSpaceEnabled;
108 
109     /**
110      * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with
111      * private space flag enabled and once with it disabled.
112      */
113     @Parameters(name = "privateSpaceEnabled={0}")
data()114     public static Iterable<?> data() {
115         return Lists.newArrayList(true, false);
116     }
117 
118     @Before
setUp()119     public void setUp() {
120         mFeatures = new TestFeatures();
121         mEnv = TestEnv.create(mFeatures);
122         mActivity = TestActivity.create(mEnv);
123         mActivity.userManager = UserManagers.create();
124         mActionModeAddons = new TestActionModeAddons();
125         mDialogs = new TestDialogController();
126         mClipper = new TestDocumentClipper();
127         mDragAndDropManager = new TestDragAndDropManager();
128         mTestConfigStore = new TestConfigStore();
129         mEnv.state.configStore = mTestConfigStore;
130 
131         isPrivateSpaceEnabled &= SdkLevel.isAtLeastS();
132         if (isPrivateSpaceEnabled) {
133             mTestConfigStore.enablePrivateSpaceInPhotoPicker();
134             mEnv.state.canForwardToProfileIdMap.put(TestProvidersAccess.USER_ID, true);
135         }
136 
137         mEnv.providers.configurePm(mActivity.packageMgr);
138         ((TestActivityConfig) mEnv.injector.config).nextDocumentEnabled = true;
139         mEnv.injector.dialogs = mDialogs;
140 
141         mHandler = createHandler();
142 
143         mEnv.selectDocument(TestEnv.FILE_GIF);
144     }
145 
146     @Test
testOpenSelectedInNewWindow()147     public void testOpenSelectedInNewWindow() {
148         mHandler.openSelectedInNewWindow();
149 
150         DocumentStack path = new DocumentStack(Roots.create("123"), mEnv.model.getDocument("1"));
151 
152         Intent expected = LauncherActivity.createLaunchIntent(mActivity);
153         expected.putExtra(Shared.EXTRA_STACK, (Parcelable) path);
154 
155         Intent actual = mActivity.startActivity.getLastValue();
156         assertEquals(expected.toString(), actual.toString());
157     }
158 
159     @Test
testSpringOpenDirectory()160     public void testSpringOpenDirectory() {
161         mHandler.springOpenDirectory(TestEnv.FOLDER_0);
162         assertTrue(mActionModeAddons.finishActionModeCalled);
163         assertEquals(TestEnv.FOLDER_0, mEnv.state.stack.peek());
164     }
165 
166     @Test
testCutSelectedDocuments_NoGivenSelection()167     public void testCutSelectedDocuments_NoGivenSelection() {
168         mEnv.populateStack();
169 
170         mEnv.selectionMgr.clearSelection();
171         mHandler.cutToClipboard();
172         mDialogs.assertDocumentsClippedNotShown();
173     }
174 
175     @Test
testCutSelectedDocuments_ContainsNonMovableItem()176     public void testCutSelectedDocuments_ContainsNonMovableItem() {
177         mEnv.populateStack();
178         mEnv.selectDocument(TestEnv.FILE_READ_ONLY);
179 
180         mHandler.cutToClipboard();
181         mDialogs.assertDocumentsClippedNotShown();
182         mDialogs.assertShowOperationUnsupported();
183         mClipper.clipForCut.assertNotCalled();
184     }
185 
186     @Test
testCopySelectedDocuments_NoGivenSelection()187     public void testCopySelectedDocuments_NoGivenSelection() {
188         mEnv.populateStack();
189 
190         mEnv.selectionMgr.clearSelection();
191         mHandler.copyToClipboard();
192         mDialogs.assertDocumentsClippedNotShown();
193     }
194 
195     @Test
testShowDeleteDialog_NoSelection()196     public void testShowDeleteDialog_NoSelection() {
197         mEnv.populateStack();
198 
199         mEnv.selectionMgr.clearSelection();
200         mHandler.showDeleteDialog();
201         mActivity.startService.assertNotCalled();
202         assertFalse(mActionModeAddons.finishActionModeCalled);
203     }
204 
205     @Test
testDeleteSelectedDocuments()206     public void testDeleteSelectedDocuments() {
207         mEnv.populateStack();
208 
209         mEnv.selectionMgr.clearSelection();
210         mEnv.selectDocument(TestEnv.FILE_PNG);
211 
212         List<DocumentInfo> docs = new ArrayList<>();
213         docs.add(TestEnv.FILE_PNG);
214         mHandler.deleteSelectedDocuments(docs, mEnv.state.stack.peek());
215 
216         mActivity.startService.assertCalled();
217         assertTrue(mActionModeAddons.finishActionModeCalled);
218     }
219 
220     @Test
testShareSelectedDocuments_ShowsChooser()221     public void testShareSelectedDocuments_ShowsChooser() {
222         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
223         mHandler.shareSelectedDocuments();
224 
225         mActivity.assertActivityStarted(Intent.ACTION_CHOOSER);
226     }
227 
228     @Test
testShareSelectedDocuments_Single()229     public void testShareSelectedDocuments_Single() {
230         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
231         mHandler.shareSelectedDocuments();
232 
233         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
234         assertHasAction(intent, Intent.ACTION_SEND);
235         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
236         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
237         assertHasExtraUri(intent, Intent.EXTRA_STREAM);
238     }
239 
240     @Test
testShareSelectedDocuments_ArchivedFile()241     public void testShareSelectedDocuments_ArchivedFile() {
242         mEnv = TestEnv.create(ArchivesProvider.AUTHORITY);
243         mHandler = createHandler();
244 
245         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
246         mEnv.selectionMgr.clearSelection();
247         mEnv.selectDocument(TestEnv.FILE_PDF);
248         mHandler.shareSelectedDocuments();
249 
250         Intent intent = mActivity.startActivity.getLastValue();
251         assertNull(intent);
252     }
253 
254     @Test
testShareSelectedDocuments_Multiple()255     public void testShareSelectedDocuments_Multiple() {
256         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
257         mEnv.selectDocument(TestEnv.FILE_PDF);
258         mHandler.shareSelectedDocuments();
259 
260         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
261         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
262         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
263         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
264         assertHasExtraList(intent, Intent.EXTRA_STREAM, 2);
265     }
266 
267     @Test
testShareSelectedDocuments_overShareLimit()268     public void testShareSelectedDocuments_overShareLimit() {
269         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
270         mEnv.selectMultipleFiles(500);
271         mHandler.shareSelectedDocuments();
272 
273         Intent intent = mActivity.startActivity.getLastValue();
274         assertNull(intent);
275         mDialogs.assertShareOverLimitShown();
276     }
277 
278     @Test
testShareSelectedDocuments_VirtualFiles()279     public void testShareSelectedDocuments_VirtualFiles() {
280         if (!mEnv.features.isVirtualFilesSharingEnabled()) {
281             return;
282         }
283 
284         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
285         mEnv.selectionMgr.clearSelection();
286         mEnv.selectDocument(TestEnv.FILE_VIRTUAL);
287         mHandler.shareSelectedDocuments();
288 
289         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
290         assertHasAction(intent, Intent.ACTION_SEND);
291         assertTrue(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
292         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
293         assertHasExtraUri(intent, Intent.EXTRA_STREAM);
294     }
295 
296     @Test
testShareSelectedDocuments_RegularAndVirtualFiles()297     public void testShareSelectedDocuments_RegularAndVirtualFiles() {
298         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
299         mEnv.selectDocument(TestEnv.FILE_PNG);
300         mEnv.selectDocument(TestEnv.FILE_VIRTUAL);
301         mHandler.shareSelectedDocuments();
302 
303         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
304         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
305 
306         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
307         if (mEnv.features.isVirtualFilesSharingEnabled()) {
308             assertTrue(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
309             assertHasExtraList(intent, Intent.EXTRA_STREAM, 3);
310         }else {
311             assertHasExtraList(intent, Intent.EXTRA_STREAM, 2);
312         }
313     }
314 
315     @Test
testShareSelectedDocuments_OmitsPartialFiles()316     public void testShareSelectedDocuments_OmitsPartialFiles() {
317         mActivity.resources.strings.put(R.string.share_via, "Sharezilla!");
318         mEnv.selectDocument(TestEnv.FILE_PARTIAL);
319         mEnv.selectDocument(TestEnv.FILE_PNG);
320         mHandler.shareSelectedDocuments();
321 
322         Intent intent = assertHasExtraIntent(mActivity.startActivity.getLastValue());
323         assertHasAction(intent, Intent.ACTION_SEND_MULTIPLE);
324         assertFalse(intent.hasCategory(Intent.CATEGORY_TYPED_OPENABLE));
325         assertFalse(intent.hasCategory(Intent.CATEGORY_OPENABLE));
326         assertHasExtraList(intent, Intent.EXTRA_STREAM, 2);
327     }
328 
329     @Test
testDocumentPicked_DefaultsToView()330     public void testDocumentPicked_DefaultsToView() throws Exception {
331         mActivity.currentRoot = TestProvidersAccess.HOME;
332 
333         mHandler.openDocument(TestEnv.FILE_GIF, ActionHandler.VIEW_TYPE_PREVIEW,
334                 ActionHandler.VIEW_TYPE_REGULAR);
335         mActivity.assertActivityStarted(Intent.ACTION_VIEW);
336     }
337 
338     @Test
testDocumentPicked_InArchive_QuickViewable()339     public void testDocumentPicked_InArchive_QuickViewable() throws Exception {
340         mActivity.resources.setQuickViewerPackage("corptropolis.viewer");
341         mActivity.currentRoot = TestProvidersAccess.HOME;
342 
343         mHandler.openDocument(TestEnv.FILE_IN_ARCHIVE, ActionHandler.VIEW_TYPE_PREVIEW,
344                 ActionHandler.VIEW_TYPE_REGULAR);
345         mActivity.assertActivityStarted(Intent.ACTION_QUICK_VIEW);
346     }
347 
348     @Test
testDocumentPicked_InArchive_Unopenable()349     public void testDocumentPicked_InArchive_Unopenable() throws Exception {
350         mActivity.currentRoot = TestProvidersAccess.HOME;
351 
352         mHandler.openDocument(TestEnv.FILE_IN_ARCHIVE, ActionHandler.VIEW_TYPE_PREVIEW,
353                 ActionHandler.VIEW_TYPE_REGULAR);
354         mDialogs.assertViewInArchivesShownUnsupported();
355     }
356 
357     @Test
testDocumentPicked_PreviewsWhenResourceSet()358     public void testDocumentPicked_PreviewsWhenResourceSet() throws Exception {
359         mActivity.resources.setQuickViewerPackage("corptropolis.viewer");
360         mActivity.currentRoot = TestProvidersAccess.HOME;
361 
362         mHandler.openDocument(TestEnv.FILE_GIF, ActionHandler.VIEW_TYPE_PREVIEW,
363                 ActionHandler.VIEW_TYPE_REGULAR);
364         mActivity.assertActivityStarted(Intent.ACTION_QUICK_VIEW);
365     }
366 
367     @Test
testDocumentPicked_Downloads_ManagesApks()368     public void testDocumentPicked_Downloads_ManagesApks() throws Exception {
369         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
370         TestEnv.FILE_APK.authority = TestProvidersAccess.DOWNLOADS.authority;
371 
372         mHandler.openDocument(TestEnv.FILE_APK, ActionHandler.VIEW_TYPE_PREVIEW,
373                 ActionHandler.VIEW_TYPE_REGULAR);
374         mActivity.assertActivityStarted(DocumentsContract.ACTION_MANAGE_DOCUMENT);
375     }
376 
377     @Test
testDocumentPicked_Downloads_ManagesPartialFiles()378     public void testDocumentPicked_Downloads_ManagesPartialFiles() throws Exception {
379         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
380         TestEnv.FILE_PARTIAL.authority = TestProvidersAccess.DOWNLOADS.authority;
381 
382         mHandler.openDocument(TestEnv.FILE_PARTIAL, ActionHandler.VIEW_TYPE_PREVIEW,
383                 ActionHandler.VIEW_TYPE_REGULAR);
384         mActivity.assertActivityStarted(DocumentsContract.ACTION_MANAGE_DOCUMENT);
385     }
386 
387     @Test
testDocumentPicked_Recent_ManagesApks()388     public void testDocumentPicked_Recent_ManagesApks() throws Exception {
389         mActivity.currentRoot = TestProvidersAccess.RECENTS;
390         TestEnv.FILE_APK.authority = TestProvidersAccess.DOWNLOADS.authority;
391 
392         mHandler.openDocument(TestEnv.FILE_APK, ActionHandler.VIEW_TYPE_PREVIEW,
393                 ActionHandler.VIEW_TYPE_REGULAR);
394         mActivity.assertActivityStarted(DocumentsContract.ACTION_MANAGE_DOCUMENT);
395     }
396 
397     @Test
testDocumentPicked_Home_SendsActionViewForApks()398     public void testDocumentPicked_Home_SendsActionViewForApks() throws Exception {
399         mActivity.currentRoot = TestProvidersAccess.HOME;
400 
401         mHandler.openDocument(TestEnv.FILE_APK, ActionHandler.VIEW_TYPE_PREVIEW,
402                 ActionHandler.VIEW_TYPE_REGULAR);
403         mActivity.assertActivityStarted(Intent.ACTION_VIEW);
404     }
405 
406     @Test
testDocumentPicked_OpensArchives()407     public void testDocumentPicked_OpensArchives() throws Exception {
408         mActivity.currentRoot = TestProvidersAccess.HOME;
409         mEnv.docs.nextDocument = TestEnv.FILE_ARCHIVE;
410 
411         final boolean result = mHandler.openDocument(TestEnv.FILE_ARCHIVE,
412                 ActionHandler.VIEW_TYPE_PREVIEW, ActionHandler.VIEW_TYPE_REGULAR);
413         assertEquals(TestEnv.FILE_ARCHIVE, mEnv.state.stack.peek());
414         assertEquals(false, result);
415     }
416 
417     @Test
testDocumentPicked_OpensDirectories()418     public void testDocumentPicked_OpensDirectories() throws Exception {
419         mActivity.currentRoot = TestProvidersAccess.HOME;
420 
421         final boolean result = mHandler.openDocument(TestEnv.FOLDER_1,
422                 ActionHandler.VIEW_TYPE_PREVIEW, ActionHandler.VIEW_TYPE_REGULAR);
423         assertEquals(TestEnv.FOLDER_1, mEnv.state.stack.peek());
424         assertEquals(false, result);
425     }
426 
427     @Test
testShowChooser()428     public void testShowChooser() throws Exception {
429         mActivity.currentRoot = TestProvidersAccess.DOWNLOADS;
430 
431         mHandler.showChooserForDoc(TestEnv.FILE_PDF);
432         mActivity.assertActivityStarted(Intent.ACTION_CHOOSER);
433     }
434 
435     @Test
testInitLocation_LaunchToStackLocation()436     public void testInitLocation_LaunchToStackLocation() {
437         DocumentStack path = new DocumentStack(Roots.create("123"), mEnv.model.getDocument("1"));
438 
439         Intent intent = LauncherActivity.createLaunchIntent(mActivity);
440         intent.putExtra(Shared.EXTRA_STACK, (Parcelable) path);
441 
442         mHandler.initLocation(intent);
443         mActivity.refreshCurrentRootAndDirectory.assertCalled();
444     }
445 
446     @Test
testInitLocation_RestoresIfStackIsLoaded()447     public void testInitLocation_RestoresIfStackIsLoaded() throws Exception {
448         mEnv.state.stack.changeRoot(TestProvidersAccess.DOWNLOADS);
449         mEnv.state.stack.push(TestEnv.FOLDER_0);
450 
451         mHandler.initLocation(mActivity.getIntent());
452         mActivity.restoreRootAndDirectory.assertCalled();
453     }
454 
455     @Test
testInitLocation_LoadsRootDocIfStackOnlyHasRoot()456     public void testInitLocation_LoadsRootDocIfStackOnlyHasRoot() throws Exception {
457         mEnv.state.stack.changeRoot(TestProvidersAccess.HAMMY);
458 
459         mHandler.initLocation(mActivity.getIntent());
460         assertRootPicked(TestProvidersAccess.HAMMY.getUri());
461     }
462 
463     @Test
testInitLocation_forceDefaultsToRoot()464     public void testInitLocation_forceDefaultsToRoot() throws Exception {
465         mActivity.resources.strings.put(R.string.default_root_uri,
466                 TestProvidersAccess.DOWNLOADS.getUri().toString());
467 
468         mHandler.initLocation(mActivity.getIntent());
469         assertRootPicked(TestProvidersAccess.DOWNLOADS.getUri());
470     }
471 
472     @Test
testInitLocation_BrowseRootWithoutRootId()473     public void testInitLocation_BrowseRootWithoutRootId() throws Exception {
474         Intent intent = mActivity.getIntent();
475         intent.setAction(Intent.ACTION_VIEW);
476         intent.setData(DocumentsContract.buildRootsUri(TestProvidersAccess.HAMMY.authority));
477 
478         mHandler.initLocation(intent);
479         assertRootPicked(TestProvidersAccess.HAMMY.getUri());
480     }
481 
482     @Test
testInitLocation_BrowseRootWrongAuthority_ShowDefault()483     public void testInitLocation_BrowseRootWrongAuthority_ShowDefault() throws Exception {
484         Intent intent = mActivity.getIntent();
485         intent.setAction(Intent.ACTION_VIEW);
486         intent.setData(DocumentsContract.buildRootsUri("com.test.wrongauthority"));
487         mActivity.resources.strings.put(R.string.default_root_uri,
488                 TestProvidersAccess.HOME.getUri().toString());
489 
490         mHandler.initLocation(intent);
491         assertRootPicked(TestProvidersAccess.HOME.getUri());
492     }
493 
494     @Test
testInitLocation_BrowseRoot()495     public void testInitLocation_BrowseRoot() throws Exception {
496         Intent intent = mActivity.getIntent();
497         intent.setAction(Intent.ACTION_VIEW);
498         intent.setData(TestProvidersAccess.PICKLES.getUri());
499 
500         mHandler.initLocation(intent);
501         assertRootPicked(TestProvidersAccess.PICKLES.getUri());
502     }
503 
504     @Test
testInitLocation_LaunchToDocuments()505     public void testInitLocation_LaunchToDocuments() throws Exception {
506         if (!mEnv.features.isLaunchToDocumentEnabled()) {
507             return;
508         }
509 
510         mEnv.docs.nextIsDocumentsUri = true;
511         mEnv.docs.nextPath = new Path(
512                 TestProvidersAccess.HOME.rootId,
513                 Arrays.asList(
514                         TestEnv.FOLDER_0.documentId,
515                         TestEnv.FOLDER_1.documentId));
516         mEnv.docs.nextDocuments =
517                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1);
518 
519         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
520         Intent intent = mActivity.getIntent();
521         intent.setAction(Intent.ACTION_VIEW);
522         intent.setData(TestEnv.FOLDER_1.derivedUri);
523         mHandler.initLocation(intent);
524 
525         mEnv.beforeAsserts();
526 
527         DocumentStackAsserts.assertEqualsTo(mEnv.state.stack, TestProvidersAccess.HOME,
528                 Arrays.asList(TestEnv.FOLDER_0, TestEnv.FOLDER_1));
529         mActivity.refreshCurrentRootAndDirectory.assertCalled();
530     }
531 
532     @Test
testInitLocation_LaunchToDownloads()533     public void testInitLocation_LaunchToDownloads() throws Exception {
534         Intent intent = mActivity.getIntent();
535         intent.setAction(DownloadManager.ACTION_VIEW_DOWNLOADS);
536 
537         mHandler.initLocation(intent);
538         assertRootPicked(TestProvidersAccess.DOWNLOADS.getUri());
539     }
540 
541     // Ignoring the test because it uses hidden api DragEvent#obtain() and changes to the api is
542     // causing failure on older base builds
543     // TODO: b/343206763 remove dependence on hidden api
544     @Ignore
545     @Test
testDragAndDrop_OnReadOnlyRoot()546     public void testDragAndDrop_OnReadOnlyRoot() throws Exception {
547         assumeTrue(VersionUtils.isAtLeastS());
548         RootInfo root = new RootInfo(); // root by default has no SUPPORT_CREATE flag
549         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, 0, 0, 0, null, null, null,
550                 null, null, true);
551         assertFalse(mHandler.dropOn(event, root));
552     }
553 
554     // Ignoring the test because it uses hidden api DragEvent#obtain() and changes to the api is
555     // causing failure on older base builds
556     // TODO: b/343206763 remove dependence on hidden api
557     @Ignore
558     @Test
testDragAndDrop_OnLibraryRoot()559     public void testDragAndDrop_OnLibraryRoot() throws Exception {
560         assumeTrue(VersionUtils.isAtLeastS());
561         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, 0, 0, 0, null, null, null,
562                 null, null, true);
563         assertFalse(mHandler.dropOn(event, TestProvidersAccess.RECENTS));
564     }
565 
566     // Ignoring the test because it uses hidden api DragEvent#obtain() and changes to the api is
567     // causing failure on older base builds
568     // TODO: b/343206763 remove dependence on hidden api
569     @Ignore
570     @Test
testDragAndDrop_DropsOnWritableRoot()571     public void testDragAndDrop_DropsOnWritableRoot() throws Exception {
572         assumeTrue(VersionUtils.isAtLeastS());
573         // DragEvent gets recycled in Android, so it is possible that by the time the callback is
574         // called, event.getLocalState() and event.getClipData() returns null. This tests to ensure
575         // our Clipper is getting the original CipData passed in.
576         Object localState = new Object();
577         ClipData clipData = ClipDatas.createTestClipData();
578         DragEvent event = DragEvent.obtain(DragEvent.ACTION_DROP, 1, 1, 0, 0, 0, localState, null,
579                 clipData, null, null, true);
580 
581         mHandler.dropOn(event, TestProvidersAccess.DOWNLOADS);
582         event.recycle();
583 
584         Pair<ClipData, RootInfo> actual = mDragAndDropManager.dropOnRootHandler.getLastValue();
585         assertSame(clipData, actual.first);
586         assertSame(TestProvidersAccess.DOWNLOADS, actual.second);
587     }
588 
589     @Test
testRefresh_nullUri()590     public void testRefresh_nullUri() throws Exception {
591         refreshAnswer = true;
592         mHandler.refreshDocument(null, (boolean answer) -> {
593             refreshAnswer = answer;
594         });
595 
596         mEnv.beforeAsserts();
597         assertFalse(refreshAnswer);
598     }
599 
600     @Test
testRefresh_emptyStack()601     public void testRefresh_emptyStack() throws Exception {
602         refreshAnswer = true;
603         assertTrue(mEnv.state.stack.isEmpty());
604         mHandler.refreshDocument(new DocumentInfo(), (boolean answer) -> {
605             refreshAnswer = answer;
606         });
607 
608         mEnv.beforeAsserts();
609         assertFalse(refreshAnswer);
610     }
611 
612     @Test
testRefresh()613     public void testRefresh() throws Exception {
614         refreshAnswer = false;
615         mEnv.populateStack();
616         mHandler.refreshDocument(mEnv.model.getDocument(
617                 ModelId.build(mEnv.model.mUserId, TestProvidersAccess.HOME.authority, "1")),
618                 (boolean answer) -> {
619                     refreshAnswer = answer;
620                 });
621 
622         mEnv.beforeAsserts();
623         if (mEnv.features.isContentRefreshEnabled()) {
624             assertTrue(refreshAnswer);
625         } else {
626             assertFalse(refreshAnswer);
627         }
628     }
629 
630     @Test
testAuthentication()631     public void testAuthentication() throws Exception {
632         PendingIntent intent = PendingIntent.getActivity(
633                 InstrumentationRegistry.getInstrumentation().getTargetContext(), 0, new Intent(),
634                 PendingIntent.FLAG_IMMUTABLE);
635 
636         mHandler.startAuthentication(intent);
637         assertEquals(intent.getIntentSender(), mActivity.startIntentSender.getLastValue().first);
638         assertEquals(AbstractActionHandler.CODE_AUTHENTICATION,
639                 mActivity.startIntentSender.getLastValue().second.intValue());
640     }
641 
642     @Test
testOnActivityResult_onOK()643     public void testOnActivityResult_onOK() throws Exception {
644         mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION, Activity.RESULT_OK,
645                 null);
646         mActivity.refreshCurrentRootAndDirectory.assertCalled();
647     }
648 
649     @Test
testOnActivityResult_onNotOK()650     public void testOnActivityResult_onNotOK() throws Exception {
651         mHandler.onActivityResult(0, Activity.RESULT_OK, null);
652         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
653 
654         mHandler.onActivityResult(AbstractActionHandler.CODE_AUTHENTICATION,
655                 Activity.RESULT_CANCELED, null);
656         mActivity.refreshCurrentRootAndDirectory.assertNotCalled();
657     }
658 
659     @Test
testShowInspector()660     public void testShowInspector() throws Exception {
661         mHandler.showInspector(TestEnv.FILE_GIF);
662 
663         mActivity.startActivity.assertCalled();
664         Intent intent = mActivity.startActivity.getLastValue();
665         assertTargetsComponent(intent, InspectorActivity.class);
666         assertHasData(intent, TestEnv.FILE_GIF.derivedUri);
667 
668         // should only send this under especial circumstances. See test below.
669         assertFalse(intent.getExtras().containsKey(Intent.EXTRA_TITLE));
670     }
671 
672     @Test
testShowInspector_DebugDisabled()673     public void testShowInspector_DebugDisabled() throws Exception {
674         mFeatures.debugSupport = false;
675 
676         mHandler.showInspector(TestEnv.FILE_GIF);
677         Intent intent = mActivity.startActivity.getLastValue();
678 
679         assertHasExtra(intent, Shared.EXTRA_SHOW_DEBUG);
680         assertFalse(intent.getExtras().getBoolean(Shared.EXTRA_SHOW_DEBUG));
681     }
682 
683     @Test
testShowInspector_DebugEnabled()684     public void testShowInspector_DebugEnabled() throws Exception {
685         mFeatures.debugSupport = true;
686         DebugFlags.setDocumentDetailsEnabled(true);
687 
688         mHandler.showInspector(TestEnv.FILE_GIF);
689         Intent intent = mActivity.startActivity.getLastValue();
690 
691         assertHasExtra(intent, Shared.EXTRA_SHOW_DEBUG);
692         assertTrue(intent.getExtras().getBoolean(Shared.EXTRA_SHOW_DEBUG));
693         DebugFlags.setDocumentDetailsEnabled(false);
694     }
695 
696     @Test
testShowInspector_OverridesRootDocumentName()697     public void testShowInspector_OverridesRootDocumentName() throws Exception {
698         mActivity.currentRoot = TestProvidersAccess.PICKLES;
699         mEnv.populateStack();
700 
701         // Verify test setup is correct, but not an assert related to the logic of our test.
702         Preconditions.checkState(mEnv.state.stack.size() == 1);
703         Preconditions.checkNotNull(mEnv.state.stack.peek());
704 
705         DocumentInfo rootDoc = mEnv.state.stack.peek();
706         rootDoc.displayName = "poodles";
707 
708         mHandler.showInspector(rootDoc);
709         Intent intent = mActivity.startActivity.getLastValue();
710         assertEquals(
711                 TestProvidersAccess.PICKLES.title,
712                 intent.getExtras().getString(Intent.EXTRA_TITLE));
713     }
714 
715     @Test
testShowInspector_OverridesRootDocumentNameX()716     public void testShowInspector_OverridesRootDocumentNameX() throws Exception {
717         mActivity.currentRoot = TestProvidersAccess.PICKLES;
718         mEnv.populateStack();
719         mEnv.state.stack.push(TestEnv.FOLDER_2);
720 
721         // Verify test setup is correct, but not an assert related to the logic of our test.
722         Preconditions.checkState(mEnv.state.stack.size() == 2);
723         Preconditions.checkNotNull(mEnv.state.stack.peek());
724 
725         DocumentInfo rootDoc = mEnv.state.stack.peek();
726         rootDoc.displayName = "poodles";
727 
728         mHandler.showInspector(rootDoc);
729         Intent intent = mActivity.startActivity.getLastValue();
730         assertFalse(intent.getExtras().containsKey(Intent.EXTRA_TITLE));
731     }
732 
assertRootPicked(Uri expectedUri)733     private void assertRootPicked(Uri expectedUri) throws Exception {
734         mEnv.beforeAsserts();
735 
736         mActivity.rootPicked.assertCalled();
737         RootInfo root = mActivity.rootPicked.getLastValue();
738         assertNotNull(root);
739         assertEquals(expectedUri, root.getUri());
740     }
741 
createHandler()742     private ActionHandler<TestActivity> createHandler() {
743         return new ActionHandler<>(
744                 mActivity,
745                 mEnv.state,
746                 mEnv.providers,
747                 mEnv.docs,
748                 mEnv.searchViewManager,
749                 mEnv::lookupExecutor,
750                 mActionModeAddons,
751                 mClipper,
752                 null,  // clip storage, not utilized unless we venture into *jumbo* clip territory.
753                 mDragAndDropManager,
754                 mEnv.injector
755         );
756     }
757 }
758