1 /* 2 * Copyright (C) 2021 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.providers.media.photopicker.viewmodel; 18 19 import static android.provider.CloudMediaProviderContract.AlbumColumns; 20 import static android.provider.CloudMediaProviderContract.MediaColumns.AUTHORITY; 21 import static android.provider.CloudMediaProviderContract.MediaColumns.DATA; 22 import static android.provider.CloudMediaProviderContract.MediaColumns.DATE_TAKEN_MILLIS; 23 import static android.provider.CloudMediaProviderContract.MediaColumns.DURATION_MILLIS; 24 import static android.provider.CloudMediaProviderContract.MediaColumns.HEIGHT; 25 import static android.provider.CloudMediaProviderContract.MediaColumns.ID; 26 import static android.provider.CloudMediaProviderContract.MediaColumns.IS_FAVORITE; 27 import static android.provider.CloudMediaProviderContract.MediaColumns.MEDIA_STORE_URI; 28 import static android.provider.CloudMediaProviderContract.MediaColumns.MIME_TYPE; 29 import static android.provider.CloudMediaProviderContract.MediaColumns.ORIENTATION; 30 import static android.provider.CloudMediaProviderContract.MediaColumns.SIZE_BYTES; 31 import static android.provider.CloudMediaProviderContract.MediaColumns.STANDARD_MIME_TYPE_EXTENSION; 32 import static android.provider.CloudMediaProviderContract.MediaColumns.SYNC_GENERATION; 33 import static android.provider.CloudMediaProviderContract.MediaColumns.WIDTH; 34 35 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 36 37 import static com.android.providers.media.PickerUriResolver.REFRESH_UI_PICKER_INTERNAL_OBSERVABLE_URI; 38 import static com.android.providers.media.photopicker.data.model.Item.ROW_ID; 39 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_CLEAR_AND_UPDATE_LIST; 40 import static com.android.providers.media.photopicker.ui.ItemsAction.ACTION_VIEW_CREATED; 41 42 import static com.google.common.truth.Truth.assertThat; 43 import static com.google.common.truth.Truth.assertWithMessage; 44 45 import static org.junit.Assert.assertEquals; 46 import static org.junit.Assert.assertFalse; 47 import static org.junit.Assert.assertNotNull; 48 import static org.junit.Assert.assertNull; 49 import static org.junit.Assert.assertTrue; 50 import static org.junit.Assert.fail; 51 import static org.mockito.ArgumentMatchers.any; 52 import static org.mockito.ArgumentMatchers.eq; 53 import static org.mockito.Mockito.mock; 54 import static org.mockito.Mockito.spy; 55 import static org.mockito.Mockito.times; 56 import static org.mockito.Mockito.verify; 57 import static org.mockito.Mockito.when; 58 59 import android.app.Application; 60 import android.content.ContentResolver; 61 import android.content.Context; 62 import android.content.Intent; 63 import android.database.Cursor; 64 import android.database.MatrixCursor; 65 import android.graphics.Color; 66 import android.net.Uri; 67 import android.os.Bundle; 68 import android.os.CancellationSignal; 69 import android.provider.MediaStore; 70 import android.text.format.DateUtils; 71 72 import androidx.annotation.NonNull; 73 import androidx.annotation.Nullable; 74 import androidx.lifecycle.LiveData; 75 import androidx.test.filters.SdkSuppress; 76 77 import com.android.modules.utils.build.SdkLevel; 78 import com.android.providers.media.TestConfigStore; 79 import com.android.providers.media.photopicker.DataLoaderThread; 80 import com.android.providers.media.photopicker.PickerSyncController; 81 import com.android.providers.media.photopicker.data.ItemsProvider; 82 import com.android.providers.media.photopicker.data.PaginationParameters; 83 import com.android.providers.media.photopicker.data.PickerResult; 84 import com.android.providers.media.photopicker.data.Selection; 85 import com.android.providers.media.photopicker.data.UserIdManager; 86 import com.android.providers.media.photopicker.data.UserManagerState; 87 import com.android.providers.media.photopicker.data.model.Category; 88 import com.android.providers.media.photopicker.data.model.Item; 89 import com.android.providers.media.photopicker.data.model.ModelTestUtils; 90 import com.android.providers.media.photopicker.data.model.RefreshRequest; 91 import com.android.providers.media.photopicker.data.model.UserId; 92 import com.android.providers.media.photopicker.espresso.PhotoPickerBaseTest; 93 94 import com.google.android.collect.Lists; 95 96 import org.junit.Before; 97 import org.junit.Rule; 98 import org.junit.Test; 99 import org.junit.runner.RunWith; 100 import org.junit.runners.Parameterized; 101 import org.mockito.Mock; 102 import org.mockito.MockitoAnnotations; 103 104 import java.util.ArrayList; 105 import java.util.List; 106 import java.util.Objects; 107 import java.util.Set; 108 import java.util.concurrent.TimeUnit; 109 import java.util.stream.Collectors; 110 111 @RunWith(Parameterized.class) 112 public class PickerViewModelTest { 113 private static final String FAKE_CATEGORY_NAME = "testCategoryName"; 114 private static final String FAKE_ID = "5"; 115 private static final Context sTargetContext = getInstrumentation().getTargetContext(); 116 private static final String TEST_PACKAGE_NAME = "com.android.providers.media.tests"; 117 private static final String CMP_AUTHORITY = "authority"; 118 private static final String CMP_ACCOUNT_NAME = "account_name"; 119 120 @Rule 121 public InstantTaskExecutorRule instantTaskExecutorRule = new InstantTaskExecutorRule(); 122 123 @Mock 124 private Application mApplication; 125 126 private PickerViewModel mPickerViewModel; 127 private TestItemsProvider mItemsProvider; 128 private TestConfigStore mConfigStore; 129 private BannerManager mBannerManager; 130 private BannerController mBannerController; 131 @Parameterized.Parameter(0) 132 public boolean isPrivateSpaceEnabled; 133 134 /** 135 * Parametrize values for {@code isPrivateSpaceEnabled} to run all the tests twice once with 136 * private space flag enabled and once with it disabled. 137 */ 138 @Parameterized.Parameters(name = "privateSpaceEnabled={0}") data()139 public static Iterable<?> data() { 140 return Lists.newArrayList(true, false); 141 } 142 PickerViewModelTest()143 public PickerViewModelTest() { 144 } 145 146 @Before setUp()147 public void setUp() { 148 MockitoAnnotations.initMocks(this); 149 150 when(mApplication.getApplicationContext()).thenReturn(sTargetContext); 151 mConfigStore = new TestConfigStore(); 152 mConfigStore.enableCloudMediaFeatureAndSetAllowedCloudProviderPackages(TEST_PACKAGE_NAME); 153 mConfigStore.enablePickerChoiceManagedSelectionEnabled(); 154 if (isPrivateSpaceEnabled) { 155 mConfigStore.enablePrivateSpaceInPhotoPicker(); 156 } else { 157 mConfigStore.disablePrivateSpaceInPhotoPicker(); 158 } 159 160 getInstrumentation().runOnMainSync(() -> { 161 mPickerViewModel = new PickerViewModel(mApplication) { 162 @Override 163 protected void initConfigStore() { 164 setConfigStore(mConfigStore); 165 } 166 }; 167 }); 168 mItemsProvider = new TestItemsProvider(sTargetContext); 169 mPickerViewModel.setItemsProvider(mItemsProvider); 170 171 // set current user profile and banner manager 172 if (mConfigStore.isPrivateSpaceInPhotoPickerEnabled() && SdkLevel.isAtLeastS()) { 173 final UserManagerState userManagerState = mock(UserManagerState.class); 174 when(userManagerState.getCurrentUserProfileId()).thenReturn(UserId.CURRENT_USER); 175 mPickerViewModel.setUserManagerState(userManagerState); 176 mBannerManager = BannerTestUtils.getTestCloudBannerManager( 177 sTargetContext, userManagerState, mConfigStore); 178 } else { 179 final UserIdManager userIdManager = mock(UserIdManager.class); 180 when(userIdManager.getCurrentUserProfileId()).thenReturn(UserId.CURRENT_USER); 181 mPickerViewModel.setUserIdManager(userIdManager); 182 mBannerManager = BannerTestUtils.getTestCloudBannerManager( 183 sTargetContext, userIdManager, mConfigStore); 184 } 185 186 mPickerViewModel.setBannerManager(mBannerManager); 187 188 // Set default banner manager values 189 mBannerController = mBannerManager.getBannerControllersPerUser().get( 190 UserId.CURRENT_USER.getIdentifier()); 191 assertNotNull(mBannerController); 192 mBannerController.onChangeCloudMediaInfo( 193 /* cmpAuthority= */ null, /* cmpAccountName= */ null); 194 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 195 } 196 197 @Test testGetItems_noItems()198 public void testGetItems_noItems() { 199 final int itemCount = 0; 200 mItemsProvider.setItems(generateFakeImageItemList(itemCount)); 201 mPickerViewModel.getPaginatedItemsForAction( 202 ACTION_CLEAR_AND_UPDATE_LIST, null); 203 // We use DataLoader thread to execute the loadItems in updateItems(), wait for the thread 204 // idle 205 DataLoaderThread.waitForIdle(); 206 207 final List<Item> itemList = Objects.requireNonNull( 208 mPickerViewModel.getPaginatedItemsForAction( 209 ACTION_VIEW_CREATED, 210 new PaginationParameters()).getValue()).getItems(); 211 212 // No date headers, the size should be 0 213 assertThat(itemList.size()).isEqualTo(itemCount); 214 } 215 216 @Test testGetCategories()217 public void testGetCategories() throws Exception { 218 final int categoryCount = 2; 219 try (final Cursor fakeCursor = generateCursorForFakeCategories(categoryCount)) { 220 fakeCursor.moveToFirst(); 221 final Category fakeFirstCategory = Category.fromCursor(fakeCursor, UserId.CURRENT_USER); 222 fakeCursor.moveToNext(); 223 final Category fakeSecondCategory = Category.fromCursor(fakeCursor, 224 UserId.CURRENT_USER); 225 mItemsProvider.setCategoriesCursor(fakeCursor); 226 // move the cursor to original position 227 fakeCursor.moveToPosition(-1); 228 mPickerViewModel.updateCategories(); 229 // We use DataLoaderThread to execute the loadCategories in updateCategories(), wait for 230 // the thread idle 231 DataLoaderThread.waitForIdle(); 232 233 final List<Category> categoryList = mPickerViewModel.getCategories().getValue(); 234 235 assertThat(categoryList.size()).isEqualTo(categoryCount); 236 // Verify the first category 237 final Category firstCategory = categoryList.get(0); 238 assertThat(firstCategory.getDisplayName(sTargetContext)).isEqualTo( 239 fakeFirstCategory.getDisplayName(sTargetContext)); 240 assertThat(firstCategory.getItemCount()).isEqualTo(fakeFirstCategory.getItemCount()); 241 assertThat(firstCategory.getCoverUri()).isEqualTo(fakeFirstCategory.getCoverUri()); 242 // Verify the second category 243 final Category secondCategory = categoryList.get(1); 244 assertThat(secondCategory.getDisplayName(sTargetContext)).isEqualTo( 245 fakeSecondCategory.getDisplayName(sTargetContext)); 246 assertThat(secondCategory.getItemCount()).isEqualTo(fakeSecondCategory.getItemCount()); 247 assertThat(secondCategory.getCoverUri()).isEqualTo(fakeSecondCategory.getCoverUri()); 248 } 249 } 250 251 @Test test_getItems_correctItemsReturned()252 public void test_getItems_correctItemsReturned() { 253 final int numberOfTestItems = 4; 254 final List<Item> expectedItems = generateFakeImageItemList(numberOfTestItems); 255 mItemsProvider.setItems(expectedItems); 256 257 LiveData<PickerViewModel.PaginatedItemsResult> testItems = 258 mPickerViewModel.getPaginatedItemsForAction( 259 ACTION_VIEW_CREATED, 260 new PaginationParameters()); 261 DataLoaderThread.waitForIdle(); 262 263 assertThat(testItems).isNotNull(); 264 assertThat(testItems.getValue()).isNotNull(); 265 assertThat(testItems.getValue().getItems().size()).isEqualTo(numberOfTestItems); 266 267 for (int itr = 0; itr < numberOfTestItems; itr++) { 268 // Assert that all test and expected items are equal. 269 assertThat(testItems.getValue().getItems().get(itr).compareTo( 270 expectedItems.get(itr))).isEqualTo(0); 271 } 272 } 273 274 @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake") 275 @Test test_getRemainingPreGrantedItems_correctItemsLoaded()276 public void test_getRemainingPreGrantedItems_correctItemsLoaded() { 277 // Enable managed selection for this test. 278 Intent intent = new Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP); 279 intent.putExtra(Intent.EXTRA_UID, 0); 280 mPickerViewModel.parseValuesFromIntent(intent); 281 282 final int numberOfTestItems = 4; 283 final List<Item> expectedItems = generateFakeImageItemList(numberOfTestItems); 284 for (Item item : expectedItems) { 285 item.setPreGranted(); 286 } 287 mItemsProvider.setItems(expectedItems); 288 List<Uri> preGrantedItems = List.of(expectedItems.get(0).getContentUri(), 289 expectedItems.get(1).getContentUri(), 290 expectedItems.get(2).getContentUri()); 291 Selection selection = mPickerViewModel.getSelection(); 292 // Add 3 item ids is preGranted set. 293 selection.setPreGrantedItems(preGrantedItems); 294 295 // adding 1 item in selection item set. 296 selection.addSelectedItem(expectedItems.get(1)); 297 298 // revoking grant for 1 id. 299 selection.removeSelectedItem(expectedItems.get(0)); 300 301 // since only one item is added in selection set, the size should be one. 302 assertThat(selection.getSelectedItems().size()).isEqualTo(1); 303 304 // Since out of 3 one grant was removed, so there would be one item loaded when remaining 305 // grants are loaded. 306 mPickerViewModel.getRemainingPreGrantedItems(); 307 DataLoaderThread.waitForIdle(); 308 309 // Now the selection set should have 2 items. 310 assertThat(selection.getSelectedItems().size()).isEqualTo(2); 311 } 312 313 @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake") 314 @Test test_deselectPreGrantedItem_correctRevokeMapMaintained()315 public void test_deselectPreGrantedItem_correctRevokeMapMaintained() { 316 // Enable managed selection for this test. 317 Intent intent = new Intent(MediaStore.ACTION_USER_SELECT_IMAGES_FOR_APP); 318 intent.putExtra(Intent.EXTRA_UID, 0); 319 mPickerViewModel.parseValuesFromIntent(intent); 320 321 final int numberOfTestItems = 4; 322 final List<Item> expectedItems = generateFakeImageItemList(numberOfTestItems); 323 for (Item item : expectedItems) { 324 item.setPreGranted(); 325 } 326 mItemsProvider.setItems(expectedItems); 327 328 List<Uri> preGrantedItems = List.of( 329 expectedItems.get(0).getContentUri(), 330 expectedItems.get(1).getContentUri(), 331 expectedItems.get(2).getContentUri()); 332 333 334 Selection selection = mPickerViewModel.getSelection(); 335 // Add 3 item ids is preGranted set. 336 selection.setPreGrantedItems(preGrantedItems); 337 338 // adding 2 items in selection item set. 339 selection.addSelectedItem(expectedItems.get(0)); 340 selection.addSelectedItem(expectedItems.get(1)); 341 342 // revoking grant for the 0th item id. 343 selection.removeSelectedItem(expectedItems.get(0)); 344 345 // since only one item is added in selection set, the size should be one. 346 assertThat(selection.getSelectedItems().size()).isEqualTo(1); 347 348 // verify revoked item is present in the items to be revoked set. 349 Set<Item> itemsToBeRevoked = selection.getDeselectedItemsToBeRevoked(); 350 assertThat(itemsToBeRevoked.size()).isEqualTo(1); 351 assertThat(itemsToBeRevoked.contains(expectedItems.get(0))).isTrue(); 352 353 Set<Uri> itemUrisToBeRevoked = selection.getDeselectedUrisToBeRevoked(); 354 assertThat(itemUrisToBeRevoked.size()).isEqualTo(1); 355 assertThat(itemUrisToBeRevoked.contains(expectedItems.get(0).getContentUri())).isTrue(); 356 } 357 358 @Test test_initialisePreSelectionItems_correctItemsLoaded()359 public void test_initialisePreSelectionItems_correctItemsLoaded() { 360 // Set the intent action as PICK_IMAGES 361 Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 362 Bundle extras = new Bundle(); 363 extras.putInt(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); 364 intent.putExtras(extras); 365 366 mPickerViewModel.parseValuesFromIntent(intent); 367 368 // generate test items 369 final int numberOfTestItems = 4; 370 final List<Item> expectedItems = generateFakeImageItemList(numberOfTestItems); 371 372 373 // Mock the test items to return the required URI and id when used. 374 final List<Item> mockedExpectedItems = new ArrayList<>(); 375 for (int i = 0; i < expectedItems.size(); i++) { 376 Item item = mock(Item.class); 377 when(item.getContentUri()).thenReturn(ItemsProvider.getItemsUri( 378 expectedItems.get(i).getId(), 379 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY, 380 UserId.CURRENT_USER)); 381 when(item.getId()).thenReturn(expectedItems.get(i).getId()); 382 mockedExpectedItems.add(item); 383 } 384 mItemsProvider.setItems(mockedExpectedItems); 385 386 // generate a list of input pre-selected picker URI and add them to test intent extras. 387 ArrayList<Uri> preGrantedPickerUris = new ArrayList<>(); 388 for (int i = 0; i < expectedItems.size(); i++) { 389 preGrantedPickerUris.add( 390 PickerResult.getPickerUrisForItems(MediaStore.ACTION_PICK_IMAGES, 391 List.of(mockedExpectedItems.get(i))).get(0)); 392 } 393 Bundle intentExtras = new Bundle(); 394 intentExtras.putParcelableArrayList(MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS, 395 preGrantedPickerUris); 396 397 Selection selection = mPickerViewModel.getSelection(); 398 // Since no item has been selected and no pre-granted URIs have been loaded, thus the size 399 // of selection should be 0. 400 assertThat(selection.getSelectedItems().size()).isEqualTo(0); 401 402 DataLoaderThread.waitForIdle(); 403 404 // Initialise pre-granted items for selection. 405 mPickerViewModel.initialisePreGrantsIfNecessary(selection, intentExtras, 406 /* mimeTypeFilters */ null); 407 DataLoaderThread.waitForIdle(); 408 409 // after initialization the items should have been added to selection. 410 assertThat(selection.getPreGrantedUris()).isNotNull(); 411 assertThat(selection.getPreGrantedUris().size()).isEqualTo(4); 412 assertThat(mPickerViewModel.getSelection().getSelectedItems().size()).isEqualTo(4); 413 } 414 415 416 @Test test_preSelectionItemsExceedMaxLimit_initialisationOfItemsFails()417 public void test_preSelectionItemsExceedMaxLimit_initialisationOfItemsFails() { 418 // Generate a list of test uris, the size being 2 uris more than the max number of URIs 419 // accepted. 420 String testUriPrefix = "content://media/picker/0/com.test.package/media/"; 421 int numberOfPreselectedUris = MediaStore.getPickImagesMaxLimit() + 2; 422 ArrayList<Uri> testUrisAsString = new ArrayList<>(); 423 for (int i = 0; i < numberOfPreselectedUris; i++) { 424 testUrisAsString.add(Uri.parse(testUriPrefix + String.valueOf(i))); 425 } 426 427 // set up the intent extras to contain the test uris. Also, parse a test PICK_IMAGES intent 428 // to ensure that PickerViewModel works in PICK_IMAGES action mode. 429 Bundle intentExtras = new Bundle(); 430 intentExtras.putInt(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); 431 intentExtras.putParcelableArrayList(MediaStore.EXTRA_PICKER_PRE_SELECTION_URIS, 432 testUrisAsString); 433 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 434 intent.putExtras(intentExtras); 435 mPickerViewModel.parseValuesFromIntent(intent); 436 437 try { 438 mPickerViewModel.initialisePreGrantsIfNecessary(null, intentExtras, null); 439 fail("The initialisation of items should have failed since the number of pre-selected" 440 + "items exceeds the max limit"); 441 } catch (IllegalArgumentException illegalArgumentException) { 442 assertThat(illegalArgumentException.getMessage()).isEqualTo( 443 "The number of URIs exceed the maximum allowed limit: " 444 + MediaStore.getPickImagesMaxLimit()); 445 } 446 } 447 generateFakeImageItem(String id)448 private static Item generateFakeImageItem(String id) { 449 final long dateTakenMs = System.currentTimeMillis() 450 + Long.parseLong(id) * DateUtils.DAY_IN_MILLIS; 451 return ModelTestUtils.generateJpegItem(id, dateTakenMs, /* generationModified */ 1L); 452 } 453 generateFakeImageItemList(int num)454 private static List<Item> generateFakeImageItemList(int num) { 455 final List<Item> itemList = new ArrayList<>(); 456 for (int i = 0; i < num; i++) { 457 itemList.add(generateFakeImageItem(String.valueOf(i))); 458 } 459 return itemList; 460 } 461 generateCursorForFakeCategories(int num)462 private static Cursor generateCursorForFakeCategories(int num) { 463 final MatrixCursor cursor = new MatrixCursor(AlbumColumns.ALL_PROJECTION); 464 final int itemCount = 5; 465 for (int i = 0; i < num; i++) { 466 cursor.addRow(new Object[]{ 467 FAKE_ID + String.valueOf(i), 468 System.currentTimeMillis(), 469 FAKE_CATEGORY_NAME + i, 470 FAKE_ID + String.valueOf(i), 471 itemCount + i, 472 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY 473 }); 474 } 475 return cursor; 476 } 477 478 private static class TestItemsProvider extends ItemsProvider { 479 480 private List<Item> mItemList = new ArrayList<>(); 481 private Cursor mCategoriesCursor; 482 TestItemsProvider(Context context)483 public TestItemsProvider(Context context) { 484 super(context); 485 } 486 487 @Override getAllItems(Category category, PaginationParameters paginationParameters, @Nullable String[] mimeType, @Nullable UserId userId, @Nullable CancellationSignal cancellationSignal)488 public Cursor getAllItems(Category category, 489 PaginationParameters paginationParameters, @Nullable String[] mimeType, 490 @Nullable UserId userId, 491 @Nullable CancellationSignal cancellationSignal) throws 492 IllegalArgumentException, IllegalStateException { 493 final String[] all_projection = new String[]{ 494 ID, 495 // This field is unique to the cursor received by the pickerVIewModel. 496 // It is not a part of cloud provider contract. 497 ROW_ID, 498 DATE_TAKEN_MILLIS, 499 SYNC_GENERATION, 500 MIME_TYPE, 501 STANDARD_MIME_TYPE_EXTENSION, 502 SIZE_BYTES, 503 MEDIA_STORE_URI, 504 DURATION_MILLIS, 505 IS_FAVORITE, 506 WIDTH, 507 HEIGHT, 508 ORIENTATION, 509 DATA, 510 AUTHORITY, 511 }; 512 final MatrixCursor c = new MatrixCursor(all_projection); 513 514 int itr = 1; 515 for (Item item : mItemList) { 516 c.addRow(new String[]{ 517 item.getId(), 518 String.valueOf(itr), 519 String.valueOf(item.getDateTaken()), 520 String.valueOf(item.getGenerationModified()), 521 item.getMimeType(), 522 String.valueOf(item.getSpecialFormat()), 523 "1", // size_bytes 524 null, // media_store_uri 525 String.valueOf(item.getDuration()), 526 "0", // is_favorite 527 String.valueOf(800), // width 528 String.valueOf(500), // height 529 String.valueOf(0), // orientation 530 "/storage/emulated/0/foo", 531 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY 532 }); 533 itr++; 534 } 535 536 return c; 537 } 538 539 @Override getLocalItems(Category category, PaginationParameters paginationParameters, @Nullable String[] mimeType, @Nullable UserId userId, @Nullable CancellationSignal cancellationSignal)540 public Cursor getLocalItems(Category category, 541 PaginationParameters paginationParameters, @Nullable String[] mimeType, 542 @Nullable UserId userId, 543 @Nullable CancellationSignal cancellationSignal) throws 544 IllegalArgumentException, IllegalStateException { 545 final String[] all_projection = new String[]{ 546 ID, 547 // This field is unique to the cursor received by the pickerVIewModel. 548 // It is not a part of cloud provider contract. 549 ROW_ID, 550 DATE_TAKEN_MILLIS, 551 SYNC_GENERATION, 552 MIME_TYPE, 553 STANDARD_MIME_TYPE_EXTENSION, 554 SIZE_BYTES, 555 MEDIA_STORE_URI, 556 DURATION_MILLIS, 557 IS_FAVORITE, 558 WIDTH, 559 HEIGHT, 560 ORIENTATION, 561 DATA, 562 AUTHORITY, 563 }; 564 final MatrixCursor c = new MatrixCursor(all_projection); 565 566 int itr = 1; 567 for (Item item : mItemList) { 568 c.addRow(new String[]{ 569 item.getId(), 570 String.valueOf(itr), 571 String.valueOf(item.getDateTaken()), 572 String.valueOf(item.getGenerationModified()), 573 item.getMimeType(), 574 String.valueOf(item.getSpecialFormat()), 575 "1", // size_bytes 576 null, // media_store_uri 577 String.valueOf(item.getDuration()), 578 "0", // is_favorite 579 String.valueOf(800), // width 580 String.valueOf(500), // height 581 String.valueOf(0), // orientation 582 "/storage/emulated/0/foo", 583 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY 584 }); 585 itr++; 586 } 587 588 return c; 589 } 590 591 @Override getItemsForPreselectedMedia(Category category, @NonNull List<Uri> preselectedUris, @Nullable String[] mimeTypes, @Nullable UserId userId, boolean isLocalOnly, int callingPackageUid, boolean shouldScreenSelectionUris, @Nullable CancellationSignal cancellationSignal)592 public Cursor getItemsForPreselectedMedia(Category category, @NonNull List<Uri> 593 preselectedUris, @Nullable String[] mimeTypes, @Nullable UserId userId, 594 boolean isLocalOnly, int callingPackageUid, boolean shouldScreenSelectionUris, 595 @Nullable CancellationSignal cancellationSignal) throws IllegalArgumentException { 596 final String[] all_projection = new String[]{ 597 ID, 598 // This field is unique to the cursor received by the pickerVIewModel. 599 // It is not a part of cloud provider contract. 600 ROW_ID, 601 DATE_TAKEN_MILLIS, 602 SYNC_GENERATION, 603 MIME_TYPE, 604 STANDARD_MIME_TYPE_EXTENSION, 605 SIZE_BYTES, 606 MEDIA_STORE_URI, 607 DURATION_MILLIS, 608 IS_FAVORITE, 609 WIDTH, 610 HEIGHT, 611 ORIENTATION, 612 DATA, 613 AUTHORITY, 614 }; 615 final MatrixCursor c = new MatrixCursor(all_projection); 616 List<String> preSelectedIds = preselectedUris.stream().map( 617 Uri::getLastPathSegment).collect(Collectors.toList()); 618 int itr = 1; 619 for (Item item : mItemList) { 620 if (preSelectedIds.contains(item.getId())) { 621 c.addRow(new String[]{ 622 item.getId(), 623 String.valueOf(itr), 624 String.valueOf(item.getDateTaken()), 625 String.valueOf(item.getGenerationModified()), 626 item.getMimeType(), 627 String.valueOf(item.getSpecialFormat()), 628 "1", // size_bytes 629 null, // media_store_uri 630 String.valueOf(item.getDuration()), 631 "0", // is_favorite 632 String.valueOf(800), // width 633 String.valueOf(500), // height 634 String.valueOf(0), // orientation 635 "/storage/emulated/0/foo", 636 PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY 637 }); 638 itr++; 639 } 640 } 641 return c; 642 643 } 644 645 @Nullable getAllCategories(@ullable String[] mimeType, @Nullable UserId userId, @Nullable CancellationSignal cancellationSignal)646 public Cursor getAllCategories(@Nullable String[] mimeType, @Nullable UserId userId, 647 @Nullable CancellationSignal cancellationSignal) { 648 if (mCategoriesCursor != null) { 649 return mCategoriesCursor; 650 } 651 652 final MatrixCursor c = new MatrixCursor(AlbumColumns.ALL_PROJECTION); 653 return c; 654 } 655 setItems(@onNull List<Item> itemList)656 public void setItems(@NonNull List<Item> itemList) { 657 mItemList = itemList; 658 } 659 setCategoriesCursor(@onNull Cursor cursor)660 public void setCategoriesCursor(@NonNull Cursor cursor) { 661 mCategoriesCursor = cursor; 662 } 663 } 664 665 @Test testParseValuesFromPickImagesIntent_noMimeType_defaultFalse()666 public void testParseValuesFromPickImagesIntent_noMimeType_defaultFalse() { 667 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 668 669 mPickerViewModel.parseValuesFromIntent(intent); 670 671 assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse(); 672 } 673 674 @Test testParseValuesFromGetContentIntent_noMimeType_defaultFalse()675 public void testParseValuesFromGetContentIntent_noMimeType_defaultFalse() { 676 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 677 intent.setType("*/*"); 678 679 mPickerViewModel.parseValuesFromIntent(intent); 680 681 assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse(); 682 } 683 684 @Test testParseValuesFromIntent_validMimeType()685 public void testParseValuesFromIntent_validMimeType() { 686 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 687 intent.setType("image/png"); 688 689 mPickerViewModel.parseValuesFromIntent(intent); 690 691 assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue(); 692 } 693 694 @Test testParseValuesFromPickImagesIntent_validExtraMimeType()695 public void testParseValuesFromPickImagesIntent_validExtraMimeType() { 696 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 697 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/gif", "video/*"}); 698 699 mPickerViewModel.parseValuesFromIntent(intent); 700 701 assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue(); 702 } 703 704 @Test testParseValuesFromPickImagesIntent_invalidExtraMimeType()705 public void testParseValuesFromPickImagesIntent_invalidExtraMimeType() { 706 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 707 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"audio/*", "video/*"}); 708 709 try { 710 mPickerViewModel.parseValuesFromIntent(intent); 711 fail("Photo Picker does not support non-media mime type filters"); 712 } catch (IllegalArgumentException expected) { 713 // Expected 714 } 715 } 716 717 @Test testParseValuesFromPickImagesIntent_localOnlyTrue()718 public void testParseValuesFromPickImagesIntent_localOnlyTrue() { 719 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 720 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 721 722 mPickerViewModel.parseValuesFromIntent(intent); 723 724 assertThat(mPickerViewModel.isLocalOnly()).isTrue(); 725 } 726 727 @Test testParseValuesFromPickImagesIntent_localOnlyFalse()728 public void testParseValuesFromPickImagesIntent_localOnlyFalse() { 729 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 730 731 mPickerViewModel.parseValuesFromIntent(intent); 732 733 assertThat(mPickerViewModel.isLocalOnly()).isFalse(); 734 } 735 736 @Test testParseValuesFromGetContentIntent_validExtraMimeType()737 public void testParseValuesFromGetContentIntent_validExtraMimeType() { 738 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 739 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/gif", "video/*"}); 740 741 mPickerViewModel.parseValuesFromIntent(intent); 742 743 assertThat(mPickerViewModel.hasMimeTypeFilters()).isTrue(); 744 } 745 746 @Test testParseValuesFromGetContentIntent_invalidExtraMimeType()747 public void testParseValuesFromGetContentIntent_invalidExtraMimeType() { 748 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 749 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"audio/*", "video/*"}); 750 751 mPickerViewModel.parseValuesFromIntent(intent); 752 753 // non-media filters for GET_CONTENT show all images and videos 754 assertThat(mPickerViewModel.hasMimeTypeFilters()).isFalse(); 755 } 756 757 @Test testParseValuesFromGetContentIntent_localOnlyTrue()758 public void testParseValuesFromGetContentIntent_localOnlyTrue() { 759 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 760 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*"}); 761 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 762 763 mPickerViewModel.parseValuesFromIntent(intent); 764 765 assertThat(mPickerViewModel.isLocalOnly()).isTrue(); 766 } 767 768 @Test testParseValuesFromGetContentIntent_localOnlyFalse()769 public void testParseValuesFromGetContentIntent_localOnlyFalse() { 770 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 771 intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*"}); 772 773 mPickerViewModel.parseValuesFromIntent(intent); 774 775 assertThat(mPickerViewModel.isLocalOnly()).isFalse(); 776 } 777 778 @Test testParseValuesFromPickImagesIntent_launchPickerInPhotosTab()779 public void testParseValuesFromPickImagesIntent_launchPickerInPhotosTab() { 780 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 781 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, MediaStore.PICK_IMAGES_TAB_IMAGES); 782 783 mPickerViewModel.parseValuesFromIntent(intent); 784 785 assertThat(mPickerViewModel.getPickerLaunchTab()).isEqualTo( 786 MediaStore.PICK_IMAGES_TAB_IMAGES); 787 } 788 789 @Test testParseValuesFromPickImagesIntent_launchPickerInAlbumsTab()790 public void testParseValuesFromPickImagesIntent_launchPickerInAlbumsTab() { 791 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 792 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, MediaStore.PICK_IMAGES_TAB_ALBUMS); 793 794 mPickerViewModel.parseValuesFromIntent(intent); 795 796 assertThat(mPickerViewModel.getPickerLaunchTab()).isEqualTo( 797 MediaStore.PICK_IMAGES_TAB_ALBUMS); 798 } 799 800 @Test testParseValuesFromPickImagesIntent_launchPickerWithIncorrectTabOption()801 public void testParseValuesFromPickImagesIntent_launchPickerWithIncorrectTabOption() { 802 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 803 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, 2); 804 805 try { 806 mPickerViewModel.parseValuesFromIntent(intent); 807 fail("Incorrect value passed for the picker launch tab option in the intent"); 808 } catch (IllegalArgumentException expected) { 809 // Expected 810 } 811 } 812 813 @Test testParseValuesFromPickImagesIntent_validAccentColor()814 public void testParseValuesFromPickImagesIntent_validAccentColor() { 815 long accentColor = 0xFFFF0000; 816 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 817 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, accentColor); 818 819 mPickerViewModel.parseValuesFromIntent(intent); 820 821 assertThat( 822 mPickerViewModel.getPickerAccentColorParameters().getPickerAccentColor()).isEqualTo( 823 Color.parseColor(String.format("#%06X", (0xFFFFFF & accentColor)))); 824 assertThat( 825 mPickerViewModel.getPickerAccentColorParameters().isCustomPickerColorSet()) 826 .isTrue(); 827 } 828 829 @Test testParseValuesFromPickImagesIntent_invalidAccentColor()830 public void testParseValuesFromPickImagesIntent_invalidAccentColor() { 831 long accentColor = 0xFF6; 832 833 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 834 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, accentColor); 835 836 try { 837 mPickerViewModel.parseValuesFromIntent(intent); 838 } catch (IllegalArgumentException e) { 839 // Expected 840 } 841 } 842 843 @Test testParseValuesFromGetContentIntent_extraPickerLaunchTab()844 public void testParseValuesFromGetContentIntent_extraPickerLaunchTab() { 845 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 846 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_LAUNCH_TAB, MediaStore.PICK_IMAGES_TAB_ALBUMS); 847 848 mPickerViewModel.parseValuesFromIntent(intent); 849 850 // GET_CONTENT doesn't support this option. Launch tab will always default to photos 851 assertThat(mPickerViewModel.getPickerLaunchTab()).isEqualTo( 852 MediaStore.PICK_IMAGES_TAB_IMAGES); 853 } 854 855 @Test testParseValuesFromPickImagesIntent_accentColorsWithUnacceptedBrightness()856 public void testParseValuesFromPickImagesIntent_accentColorsWithUnacceptedBrightness() { 857 // Accent color brightness is less than the accepted brightness 858 long[] accentColors = new long[] { 859 0xFF000000, // black 860 0xFFFFFFFF, // white 861 0xFFFFFFF0 // variant of white 862 }; 863 864 for (long accentColor: accentColors) { 865 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 866 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, accentColor); 867 868 mPickerViewModel.parseValuesFromIntent(intent); 869 870 // Fall back to the android theme 871 assertWithMessage("Input accent color " + accentColor 872 + " does not fall within accepted luminance range.") 873 .that(mPickerViewModel.getPickerAccentColorParameters().getPickerAccentColor()) 874 .isEqualTo(-1); 875 assertWithMessage("Custom picker color flag for input color " 876 + accentColor + " should be false but was true.") 877 .that(mPickerViewModel.getPickerAccentColorParameters() 878 .isCustomPickerColorSet()).isFalse(); 879 } 880 } 881 882 @Test testParseValuesFromGetContentIntent_accentColor()883 public void testParseValuesFromGetContentIntent_accentColor() { 884 long accentColor = 0xFFFF0000; 885 final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); 886 intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_ACCENT_COLOR, accentColor); 887 888 mPickerViewModel.parseValuesFromIntent(intent); 889 890 // GET_CONTENT doesn't support this option. Accent color parameter object is not 891 // created. 892 assertThat(mPickerViewModel.getPickerAccentColorParameters().getPickerAccentColor()) 893 .isEqualTo(-1); 894 assertThat(mPickerViewModel.getPickerAccentColorParameters().isCustomPickerColorSet()) 895 .isFalse(); 896 } 897 898 @Test testShouldShowOnlyLocalFeatures()899 public void testShouldShowOnlyLocalFeatures() { 900 mConfigStore.enableCloudMediaFeature(); 901 902 Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 903 intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); 904 mPickerViewModel.parseValuesFromIntent(intent); 905 assertThat(mPickerViewModel.isLocalOnly()).isTrue(); 906 assertThat(mPickerViewModel.shouldShowOnlyLocalFeatures()).isTrue(); 907 908 intent.removeExtra(Intent.EXTRA_LOCAL_ONLY); 909 mPickerViewModel.parseValuesFromIntent(intent); 910 assertThat(mPickerViewModel.isLocalOnly()).isFalse(); 911 assertThat(mPickerViewModel.shouldShowOnlyLocalFeatures()).isFalse(); 912 913 mConfigStore.disableCloudMediaFeature(); 914 assertThat(mPickerViewModel.shouldShowOnlyLocalFeatures()).isTrue(); 915 } 916 917 @Test testRefreshUiNotifications()918 public void testRefreshUiNotifications() throws InterruptedException { 919 final LiveData<RefreshRequest> shouldRefreshUi = mPickerViewModel.refreshUiLiveData(); 920 assertFalse(shouldRefreshUi.getValue().shouldRefreshPicker()); 921 922 final ContentResolver contentResolver = sTargetContext.getContentResolver(); 923 contentResolver.notifyChange(REFRESH_UI_PICKER_INTERNAL_OBSERVABLE_URI, null); 924 925 TimeUnit.MILLISECONDS.sleep(100); 926 assertTrue(shouldRefreshUi.getValue().shouldRefreshPicker()); 927 928 mPickerViewModel.resetAllContentInCurrentProfile(false); 929 assertFalse(shouldRefreshUi.getValue().shouldRefreshPicker()); 930 } 931 932 @Test testDismissChooseAppBanner()933 public void testDismissChooseAppBanner() { 934 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, CMP_ACCOUNT_NAME); 935 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 936 937 mBannerController.onChangeCloudMediaInfo( 938 /* cmpAuthority= */ null, /* cmpAccountName= */ null); 939 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 940 assertTrue(mBannerController.shouldShowChooseAppBanner()); 941 assertTrue(mPickerViewModel.shouldShowChooseAppBannerLiveData().getValue()); 942 943 getInstrumentation().runOnMainSync(() -> mPickerViewModel.onUserDismissedChooseAppBanner()); 944 assertFalse(mBannerController.shouldShowChooseAppBanner()); 945 assertFalse(mPickerViewModel.shouldShowChooseAppBannerLiveData().getValue()); 946 947 // Assert no change on dismiss when the banner is already hidden 948 getInstrumentation().runOnMainSync(() -> mPickerViewModel.onUserDismissedChooseAppBanner()); 949 assertFalse(mBannerController.shouldShowChooseAppBanner()); 950 assertFalse(mPickerViewModel.shouldShowChooseAppBannerLiveData().getValue()); 951 } 952 953 @Test testDismissCloudMediaAvailableBanner()954 public void testDismissCloudMediaAvailableBanner() { 955 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, CMP_ACCOUNT_NAME); 956 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 957 assertTrue(mBannerController.shouldShowCloudMediaAvailableBanner()); 958 assertTrue(mPickerViewModel.shouldShowCloudMediaAvailableBannerLiveData().getValue()); 959 960 getInstrumentation().runOnMainSync(() -> 961 mPickerViewModel.onUserDismissedCloudMediaAvailableBanner()); 962 assertFalse(mBannerController.shouldShowCloudMediaAvailableBanner()); 963 assertFalse(mPickerViewModel.shouldShowCloudMediaAvailableBannerLiveData().getValue()); 964 965 // Assert no change on dismiss when the banner is already hidden 966 getInstrumentation().runOnMainSync(() -> 967 mPickerViewModel.onUserDismissedCloudMediaAvailableBanner()); 968 assertFalse(mBannerController.shouldShowCloudMediaAvailableBanner()); 969 assertFalse(mPickerViewModel.shouldShowCloudMediaAvailableBannerLiveData().getValue()); 970 } 971 972 @Test testDismissAccountUpdatedBanner()973 public void testDismissAccountUpdatedBanner() { 974 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, /* cmpAccountName= */ null); 975 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 976 977 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, CMP_ACCOUNT_NAME); 978 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 979 assertTrue(mBannerController.shouldShowAccountUpdatedBanner()); 980 assertTrue(mPickerViewModel.shouldShowAccountUpdatedBannerLiveData().getValue()); 981 982 getInstrumentation().runOnMainSync(() -> 983 mPickerViewModel.onUserDismissedAccountUpdatedBanner()); 984 assertFalse(mBannerController.shouldShowAccountUpdatedBanner()); 985 assertFalse(mPickerViewModel.shouldShowAccountUpdatedBannerLiveData().getValue()); 986 987 // Assert no change on dismiss when the banner is already hidden 988 getInstrumentation().runOnMainSync(() -> 989 mPickerViewModel.onUserDismissedAccountUpdatedBanner()); 990 assertFalse(mBannerController.shouldShowAccountUpdatedBanner()); 991 assertFalse(mPickerViewModel.shouldShowAccountUpdatedBannerLiveData().getValue()); 992 } 993 994 @Test testDismissChooseAccountBanner()995 public void testDismissChooseAccountBanner() { 996 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, /* cmpAccountName= */ null); 997 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 998 assertTrue(mBannerController.shouldShowChooseAccountBanner()); 999 assertTrue(mPickerViewModel.shouldShowChooseAccountBannerLiveData().getValue()); 1000 1001 getInstrumentation().runOnMainSync(() -> 1002 mPickerViewModel.onUserDismissedChooseAccountBanner()); 1003 assertFalse(mBannerController.shouldShowChooseAccountBanner()); 1004 assertFalse(mPickerViewModel.shouldShowChooseAccountBannerLiveData().getValue()); 1005 1006 // Assert no change on dismiss when the banner is already hidden 1007 getInstrumentation().runOnMainSync(() -> 1008 mPickerViewModel.onUserDismissedChooseAccountBanner()); 1009 assertFalse(mBannerController.shouldShowChooseAccountBanner()); 1010 assertFalse(mPickerViewModel.shouldShowChooseAccountBannerLiveData().getValue()); 1011 } 1012 1013 @Test testGetCloudMediaProviderAuthorityLiveData()1014 public void testGetCloudMediaProviderAuthorityLiveData() { 1015 assertNull(mPickerViewModel.getCloudMediaProviderAuthorityLiveData().getValue()); 1016 1017 mBannerController.onChangeCloudMediaInfo(CMP_AUTHORITY, /* cmpAccountName= */ null); 1018 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 1019 1020 assertEquals(CMP_AUTHORITY, 1021 mPickerViewModel.getCloudMediaProviderAuthorityLiveData().getValue()); 1022 } 1023 1024 @Test testGetChooseCloudMediaAccountActivityIntent()1025 public void testGetChooseCloudMediaAccountActivityIntent() { 1026 assertNull(mPickerViewModel.getChooseCloudMediaAccountActivityIntent()); 1027 1028 final Intent testIntent = new Intent(); 1029 mBannerController.setChooseCloudMediaAccountActivityIntent(testIntent); 1030 mBannerManager.maybeInitialiseAndSetBannersForCurrentUser(); 1031 1032 assertEquals(testIntent, 1033 mPickerViewModel.getChooseCloudMediaAccountActivityIntent()); 1034 } 1035 1036 @Test testMainGridInitRequest()1037 public void testMainGridInitRequest() { 1038 ItemsProvider mockItemsProvider = spy(ItemsProvider.class); 1039 mPickerViewModel.setItemsProvider(mockItemsProvider); 1040 1041 // Parse values from intent 1042 final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 1043 mPickerViewModel.parseValuesFromIntent(intent); 1044 1045 // Send an init request 1046 mPickerViewModel.maybeInitPhotoPickerData(); 1047 1048 // Check that the request was sent 1049 DataLoaderThread.waitForIdle(); 1050 verify(mockItemsProvider, times(1)).initPhotoPickerData(any(), any(), eq(false), any()); 1051 1052 // Send an init request again 1053 mPickerViewModel.maybeInitPhotoPickerData(); 1054 1055 // Check that init request was NOT sent again 1056 DataLoaderThread.waitForIdle(); 1057 verify(mockItemsProvider, times(1)).initPhotoPickerData(any(), any(), eq(false), any()); 1058 } 1059 1060 @SdkSuppress(minSdkVersion = 34, codeName = "UpsideDownCake") 1061 @Test testMainGridPickerChoiceInitRequest()1062 public void testMainGridPickerChoiceInitRequest() { 1063 ItemsProvider mockItemsProvider = spy(ItemsProvider.class); 1064 mPickerViewModel.setItemsProvider(mockItemsProvider); 1065 1066 // Parse values from intent 1067 Intent intent = PhotoPickerBaseTest.getUserSelectImagesForAppIntent(); 1068 mPickerViewModel.parseValuesFromIntent(intent); 1069 1070 // Send an init request 1071 mPickerViewModel.maybeInitPhotoPickerData(); 1072 1073 // Check that a local-only init request was sent 1074 DataLoaderThread.waitForIdle(); 1075 verify(mockItemsProvider, times(1)).initPhotoPickerData(any(), any(), eq(true), any()); 1076 1077 // Send an init request again 1078 mPickerViewModel.maybeInitPhotoPickerData(); 1079 1080 // Check that init request was NOT sent again 1081 DataLoaderThread.waitForIdle(); 1082 verify(mockItemsProvider, times(1)).initPhotoPickerData(any(), any(), eq(true), any()); 1083 } 1084 1085 @Test testMainGridInitOnResetRequest()1086 public void testMainGridInitOnResetRequest() { 1087 ItemsProvider mockItemsProvider = spy(ItemsProvider.class); 1088 mPickerViewModel.setItemsProvider(mockItemsProvider); 1089 1090 // Parse values from intent 1091 Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); 1092 mPickerViewModel.parseValuesFromIntent(intent); 1093 1094 // Send an init request 1095 mPickerViewModel.maybeInitPhotoPickerData(); 1096 1097 // Check that a local-only init request was sent 1098 DataLoaderThread.waitForIdle(); 1099 verify(mockItemsProvider, times(1)).initPhotoPickerData(any(), any(), eq(false), any()); 1100 1101 // Send an init request again 1102 mPickerViewModel.resetAllContentInCurrentProfile(true); 1103 1104 // Check that init request was sent again 1105 DataLoaderThread.waitForIdle(); 1106 verify(mockItemsProvider, times(2)).initPhotoPickerData(any(), any(), eq(false), any()); 1107 } 1108 } 1109