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