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