blob: a9eb6bf92c349d9c04d92d2a1cd30b9600018e84 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.providers.media.photopicker;
import static android.provider.MediaStore.VOLUME_EXTERNAL;
import static com.android.providers.media.photopicker.data.PickerDbFacade.PROP_DEFAULT_SYNC_DELAY_MS;
import static com.android.providers.media.util.MimeUtils.isImageMimeType;
import static com.android.providers.media.util.MimeUtils.isVideoMimeType;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
import android.Manifest;
import android.app.UiAutomation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.provider.MediaStore;
import androidx.test.InstrumentationRegistry;
import com.android.providers.media.photopicker.data.ItemsProvider;
import com.android.providers.media.photopicker.data.model.Category;
import com.android.providers.media.photopicker.data.model.Item;
import com.android.providers.media.photopicker.data.model.UserId;
import com.android.providers.media.scan.MediaScannerTest.IsolatedContext;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class ItemsProviderTest {
/**
* To help avoid flaky tests, give ourselves a unique nonce to be used for
* all filesystem paths, so that we don't risk conflicting with previous
* test runs.
*/
private static final String NONCE = String.valueOf(System.nanoTime());
private static final String TAG = "ItemsProviderTest";
private static final String VIDEO_FILE_NAME = TAG + "_file_" + NONCE + ".mp4";
private static final String IMAGE_FILE_NAME = TAG + "_file_" + NONCE + ".jpg";
private static final String HIDDEN_DIR_NAME = TAG + "_hidden_dir_" + NONCE;
private ContentResolver mIsolatedResolver;
private ItemsProvider mItemsProvider;
@Before
public void setUp() {
final UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation()
.getUiAutomation();
uiAutomation.adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
Manifest.permission.READ_DEVICE_CONFIG,
Manifest.permission.INTERACT_ACROSS_USERS);
// Remove sync delay to avoid flaky tests
final String setSyncDelayCommand = "setprop " + PROP_DEFAULT_SYNC_DELAY_MS + " 0";
uiAutomation.executeShellCommand(setSyncDelayCommand);
final Context context = InstrumentationRegistry.getTargetContext();
final Context isolatedContext
= new IsolatedContext(context, "databases", /*asFuseThread*/ false);
mIsolatedResolver = isolatedContext.getContentResolver();
mItemsProvider = new ItemsProvider(isolatedContext);
// Wait for MediaStore to be Idle to reduce flakes caused by database updates
MediaStore.waitForIdle(mIsolatedResolver);
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_CAMERA}.
*/
@Test
public void testGetCategories_camera() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 image file in Camera dir to test
// {@link ItemsProvider#getCategories(String, UserId)}.
final File cameraDir = getCameraDir();
File imageFile = assertCreateNewImage(cameraDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_CAMERA, /* numberOfItems */ 1);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_CAMERA}.
*/
@Test
public void testGetCategories_not_camera() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// negative test case: image file which should not be returned in Camera category
final File picturesDir = getPicturesDir();
File nonCameraImageFile = assertCreateNewImage(picturesDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_CAMERA, /* numberOfItems */ 0);
} finally {
nonCameraImageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_VIDEOS}.
*/
@Test
public void testGetCategories_videos() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 video file in Movies dir to test
// {@link ItemsProvider#getCategories(String, UserId)}.
final File moviesDir = getMoviesDir();
File videoFile = assertCreateNewVideo(moviesDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_VIDEOS, /* numberOfItems */ 1);
} finally {
videoFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_VIDEOS}.
*/
@Test
public void testGetCategories_not_videos() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// negative test case: image file which should not be returned in Videos category
final File picturesDir = getPicturesDir();
File imageFile = assertCreateNewImage(picturesDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_VIDEOS, /* numberOfItems */ 0);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_SCREENSHOTS}.
*/
@Test
public void testGetCategories_screenshots() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 image file in Screenshots dir to test
// {@link ItemsProvider#getCategories(String, UserId)}
final File screenshotsDir = getScreenshotsDir();
File imageFile = assertCreateNewImage(screenshotsDir);
// Create 1 image file in Screenshots dir of Downloads dir
// {@link ItemsProvider#getCategories(String, UserId)}
final File screenshotsDirInDownloadsDir = getScreenshotsDirFromDownloadsDir();
File imageFileInScreenshotDirInDownloads =
assertCreateNewImage(screenshotsDirInDownloadsDir);
try {
assertGetCategoriesMatchMultiple(Category.CATEGORY_SCREENSHOTS,
Category.CATEGORY_DOWNLOADS, /* numberOfItemsInScreenshots */ 2,
/* numberOfItemsInDownloads */ 1);
} finally {
imageFile.delete();
imageFileInScreenshotDirInDownloads.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_SCREENSHOTS}.
*/
@Test
public void testGetCategories_not_screenshots() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// negative test case: image file which should not be returned in Screenshots category
final File cameraDir = getCameraDir();
File imageFile = assertCreateNewImage(cameraDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_SCREENSHOTS, /* numberOfItems */ 0);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_FAVORITES}.
*/
@Test
public void testGetCategories_favorites() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// positive test case: image file which should be returned in favorites category
final File picturesDir = getPicturesDir();
final File imageFile = assertCreateNewImage(picturesDir);
setIsFavorite(imageFile);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_FAVORITES, /* numberOfItems */1);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_FAVORITES}.
*/
@Test
public void testGetCategories_not_favorites() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// negative test case: image file which should not be returned in favorites category
final File picturesDir = getPicturesDir();
final File nonFavImageFile = assertCreateNewImage(picturesDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_FAVORITES, /* numberOfItems */ 0);
} finally {
nonFavImageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_DOWNLOADS}.
*/
@Test
public void testGetCategories_downloads() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 image file in Downloads dir to test
// {@link ItemsProvider#getCategories(String, UserId)}.
final File downloadsDir = getDownloadsDir();
final File imageFile = assertCreateNewImage(downloadsDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_DOWNLOADS, /* numberOfItems */ 1);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_DOWNLOADS}.
*/
@Test
public void testGetCategories_not_downloads() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// negative test case: image file which should not be returned in Downloads category
final File picturesDir = getPicturesDir();
final File nonDownloadsImageFile = assertCreateNewImage(picturesDir);
try {
assertGetCategoriesMatchSingle(Category.CATEGORY_DOWNLOADS, /* numberOfItems */ 0);
} finally {
nonDownloadsImageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_CAMERA} and {@link Category#CATEGORY_VIDEOS}.
*/
@Test
public void testGetCategories_camera_and_videos() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 video file in Camera dir to test
// {@link ItemsProvider#getCategories(String, UserId)}.
final File cameraDir = getCameraDir();
File videoFile = assertCreateNewVideo(cameraDir);
try {
assertGetCategoriesMatchMultiple(Category.CATEGORY_CAMERA, Category.CATEGORY_VIDEOS,
/* numberOfItemsInCamera */ 1,
/* numberOfItemsInVideos */ 1);
} finally {
videoFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_SCREENSHOTS} and {@link Category#CATEGORY_FAVORITES}.
*/
@Test
public void testGetCategories_screenshots_and_favorites() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 image file in Screenshots dir to test
// {@link ItemsProvider#getCategories(String, UserId)}
final File screenshotsDir = getScreenshotsDir();
File imageFile = assertCreateNewImage(screenshotsDir);
setIsFavorite(imageFile);
try {
assertGetCategoriesMatchMultiple(Category.CATEGORY_SCREENSHOTS,
Category.CATEGORY_FAVORITES,
/* numberOfItemsInScreenshots */ 1,
/* numberOfItemsInFavorites */ 1);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getCategories(String, UserId)} to return correct info about
* {@link Category#CATEGORY_DOWNLOADS} and {@link Category#CATEGORY_FAVORITES}.
*/
@Test
public void testGetCategories_downloads_and_favorites() throws Exception {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c.getCount()).isEqualTo(0);
// Create 1 image file in Screenshots dir to test
// {@link ItemsProvider#getCategories(String, UserId)}
final File downloadsDir = getDownloadsDir();
File imageFile = assertCreateNewImage(downloadsDir);
setIsFavorite(imageFile);
try {
assertGetCategoriesMatchMultiple(Category.CATEGORY_DOWNLOADS,
Category.CATEGORY_FAVORITES,
/* numberOfItemsInScreenshots */ 1,
/* numberOfItemsInFavorites */ 1);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
* images and videos.
*/
@Test
public void testGetItems() throws Exception {
// Create 1 image and 1 video file to test
// {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
// Both files should be returned.
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ null, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(2);
assertThatOnlyImagesVideos(res);
// Reset the cursor back. Cursor#moveToPosition(-1) will reset the position to -1,
// but since there is no such valid cursor position, it returns false.
assertThat(res.moveToPosition(-1)).isFalse();
assertThatAllImagesVideos(res.getCount());
} finally {
imageFile.delete();
videoFile.delete();
}
}
@Test
public void testGetItems_sortOrder() throws Exception {
try {
final long timeNow = System.nanoTime() / 1000;
final Uri imageFileDateNowPlus1Uri = prepareFileAndGetUri(
new File(getDownloadsDir(), "latest_" + IMAGE_FILE_NAME), timeNow + 1000);
final Uri imageFileDateNowUri
= prepareFileAndGetUri(new File(getDcimDir(), IMAGE_FILE_NAME), timeNow);
final Uri videoFileDateNowUri
= prepareFileAndGetUri(new File(getCameraDir(), VIDEO_FILE_NAME), timeNow);
// This is the list of uris based on the expected sort order of items returned by
// ItemsProvider#getItems
List<Uri> uris = new ArrayList<>();
// This is the latest image file
uris.add(imageFileDateNowPlus1Uri);
// Video file was scanned after image file, hence has higher _id than image file
uris.add(videoFileDateNowUri);
uris.add(imageFileDateNowUri);
try (Cursor cursor = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ null, /* userId */ null)) {
assertThat(cursor).isNotNull();
final int expectedCount = uris.size();
assertThat(cursor.getCount()).isEqualTo(expectedCount);
int rowNum = 0;
assertThat(cursor.moveToFirst()).isTrue();
final int idColumnIndex = cursor.getColumnIndexOrThrow(Item.ItemColumns.ID);
while (rowNum < expectedCount) {
assertWithMessage("id at row:" + rowNum + " is expected to be"
+ " same as id in " + uris.get(rowNum))
.that(String.valueOf(cursor.getLong(idColumnIndex)))
.isEqualTo(uris.get(rowNum).getLastPathSegment());
cursor.moveToNext();
rowNum++;
}
}
} finally {
deleteAllFilesNoThrow();
}
}
/**
* Tests {@link {@link ItemsProvider#getItems(String, int, int, String, UserId)}} does not
* return hidden images/videos.
*/
@Test
public void testGetItems_nonMedia() throws Exception {
// Create 1 image and 1 video file in a hidden dir to test
// {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
// Both should not be returned.
File hiddenDir = createHiddenDir();
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ null, /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
imageFileHidden.delete();
videoFileHidden.delete();
hiddenDir.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
* images and videos based on the mimeType. Image mimeType should only return images.
*/
@Test
public void testGetItemsImages() throws Exception {
// Create 1 image and 1 video file to test
// {@link ItemsProvider#getItems(String, int, int, String, UserId)}.
// Only 1 should be returned.
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
assertThatOnlyImages(res);
assertThatAllImages(res.getCount());
} finally {
imageFile.delete();
videoFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
* images and videos based on the mimeType. Image mimeType should only return images.
*/
@Test
public void testGetItemsImages_png() throws Exception {
// Create a jpg file image. Tests negative use case, this should not be returned below.
File imageFile = assertCreateNewImage();
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "image/png", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
imageFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} does not return
* hidden images/videos.
*/
@Test
public void testGetItemsImages_nonMedia() throws Exception {
// Create 1 image and 1 video file in a hidden dir to test
// {@link ItemsProvider#getItems(String, int, int, String)}.
// Both should not be returned.
File hiddenDir = createHiddenDir();
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "image/*", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
imageFileHidden.delete();
videoFileHidden.delete();
hiddenDir.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
* images and videos based on the mimeType. Video mimeType should only return videos.
*/
@Test
public void testGetItemsVideos() throws Exception {
// Create 1 image and 1 video file to test
// {@link ItemsProvider#getItems(String, int, int, String)}.
// Only 1 should be returned.
File imageFile = assertCreateNewImage();
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
assertThatOnlyVideos(res);
assertThatAllVideos(res.getCount());
} finally {
imageFile.delete();
videoFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} to return all
* images and videos based on the mimeType. Image mimeType should only return images.
*/
@Test
public void testGetItemsVideos_mp4() throws Exception {
// Create a mp4 video file. Tests positive use case, this should be returned below.
File videoFile = assertCreateNewVideo();
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "video/mp4", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(1);
} finally {
videoFile.delete();
}
}
/**
* Tests {@link ItemsProvider#getItems(String, int, int, String, UserId)} does not return
* hidden images/videos.
*/
@Test
public void testGetItemsVideos_nonMedia() throws Exception {
// Create 1 image and 1 video file in a hidden dir to test the API.
// Both should not be returned.
File hiddenDir = createHiddenDir();
File imageFileHidden = assertCreateNewImage(hiddenDir);
File videoFileHidden = assertCreateNewVideo(hiddenDir);
try {
final Cursor res = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
/* limit */ -1, /* mimeType */ "video/*", /* userId */ null);
assertThat(res).isNotNull();
assertThat(res.getCount()).isEqualTo(0);
} finally {
imageFileHidden.delete();
videoFileHidden.delete();
hiddenDir.delete();
}
}
private void assertGetCategoriesMatchSingle(String expectedCategoryName,
int expectedNumberOfItems) throws Exception {
if (expectedNumberOfItems == 0) {
assertCategoriesNoMatch(expectedCategoryName);
return;
}
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c).isNotNull();
assertThat(c.getCount()).isEqualTo(1);
// Assert that only expected category is returned and has expectedNumberOfItems items in it
assertThat(c.moveToFirst()).isTrue();
final int nameColumnIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.NAME);
final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(
Category.CategoryColumns.NUMBER_OF_ITEMS);
final int coverIdIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.COVER_ID);
final String categoryName = c.getString(nameColumnIndex);
final int numOfItems = c.getInt(numOfItemsColumnIndex);
final Uri coverUri = ItemsProvider.getItemsUri(c.getString(coverIdIndex),
/* authority */ null, UserId.CURRENT_USER);
assertThat(categoryName).isEqualTo(expectedCategoryName);
assertThat(numOfItems).isEqualTo(expectedNumberOfItems);
assertCategoryUriIsValid(coverUri);
}
private void assertCategoryUriIsValid(Uri uri) throws Exception {
final AssetFileDescriptor fd1 = mIsolatedResolver.openTypedAssetFile(uri, "image/*", null,
null);
assertThat(fd1).isNotNull();
final ParcelFileDescriptor fd2 = mIsolatedResolver.openFileDescriptor(uri, "r");
assertThat(fd2).isNotNull();
}
private void assertCategoriesNoMatch(String expectedCategoryName) {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
while (c != null && c.moveToNext()) {
final int nameColumnIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.NAME);
final String categoryName = c.getString(nameColumnIndex);
assertThat(categoryName).isNotEqualTo(expectedCategoryName);
}
}
private void assertGetCategoriesMatchMultiple(String category1, String category2,
int numberOfItems1, int numberOfItems2) {
Cursor c = mItemsProvider.getCategories(/* mimeType */ null, /* userId */ null);
assertThat(c).isNotNull();
assertThat(c.getCount()).isEqualTo(2);
// Assert that category1 and category2 is returned and has numberOfItems1 and
// numberOfItems2 items in them respectively.
boolean isCategory1Returned = false;
boolean isCategory2Returned = false;
while (c.moveToNext()) {
final int nameColumnIndex = c.getColumnIndexOrThrow(Category.CategoryColumns.NAME);
final int numOfItemsColumnIndex = c.getColumnIndexOrThrow(
Category.CategoryColumns.NUMBER_OF_ITEMS);
final String categoryName = c.getString(nameColumnIndex);
final int numOfItems = c.getInt(numOfItemsColumnIndex);
if (categoryName.equals(category1)) {
isCategory1Returned = true;
assertThat(numOfItems).isEqualTo(numberOfItems1);
} else if (categoryName.equals(category2)) {
isCategory2Returned = true;
assertThat(numOfItems).isEqualTo(numberOfItems2);
}
}
assertThat(isCategory1Returned).isTrue();
assertThat(isCategory2Returned).isTrue();
}
private void setIsFavorite(File file) {
final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
final ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.IS_FAVORITE, 1);
// Assert that 1 row corresponding to this file is updated.
assertThat(mIsolatedResolver.update(uri, values, null)).isEqualTo(1);
// Wait for MediaStore to be Idle to reduce flakes caused by database updates
MediaStore.waitForIdle(mIsolatedResolver);
}
private void assertThatOnlyImagesVideos(Cursor c) throws Exception {
while (c.moveToNext()) {
int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
String mimeType = c.getString(mimeTypeColumn);
assertThat(isImageMimeType(mimeType) || isVideoMimeType(mimeType)).isTrue();
}
}
private void assertThatOnlyImages(Cursor c) throws Exception {
while (c.moveToNext()) {
int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
String mimeType = c.getString(mimeTypeColumn);
assertThat(isImageMimeType(mimeType)).isTrue();
}
}
private void assertThatOnlyVideos(Cursor c) throws Exception {
while (c.moveToNext()) {
int mimeTypeColumn = c.getColumnIndexOrThrow(MediaStore.MediaColumns.MIME_TYPE);
String mimeType = c.getString(mimeTypeColumn);
assertThat(isVideoMimeType(mimeType)).isTrue();
}
}
private void assertThatAllImagesVideos(int count) {
int countOfImages = getCountOfMediaStoreImages();
int countOfVideos = getCountOfMediaStoreVideos();
assertThat(count).isEqualTo(countOfImages + countOfVideos);
}
private void assertThatAllImages(int count) {
int countOfImages = getCountOfMediaStoreImages();
assertThat(count).isEqualTo(countOfImages);
}
private void assertThatAllVideos(int count) {
int countOfVideos = getCountOfMediaStoreVideos();
assertThat(count).isEqualTo(countOfVideos);
}
private int getCountOfMediaStoreImages() {
try (Cursor c = mIsolatedResolver.query(
MediaStore.Images.Media.getContentUri(VOLUME_EXTERNAL), null, null, null)) {
assertThat(c.moveToFirst()).isTrue();
return c.getCount();
}
}
private int getCountOfMediaStoreVideos() {
try (Cursor c = mIsolatedResolver.query(
MediaStore.Video.Media.getContentUri(VOLUME_EXTERNAL), null, null, null)) {
assertThat(c.moveToFirst()).isTrue();
return c.getCount();
}
}
private File assertCreateNewVideo(File dir) throws Exception {
return assertCreateNewFile(dir, VIDEO_FILE_NAME);
}
private File assertCreateNewImage(File dir) throws Exception {
return assertCreateNewFile(dir, IMAGE_FILE_NAME);
}
private File assertCreateNewVideo() throws Exception {
return assertCreateNewFile(getDownloadsDir(), VIDEO_FILE_NAME);
}
private File assertCreateNewImage() throws Exception {
return assertCreateNewFile(getDownloadsDir(), IMAGE_FILE_NAME);
}
private File assertCreateNewFile(File parentDir, String fileName) throws Exception {
final File file = new File(parentDir, fileName);
prepareFileAndGetUri(file, /* lastModifiedTime */ -1);
return file;
}
private Uri prepareFileAndGetUri(File file, long lastModifiedTime) throws IOException {
ensureParentExists(file.getParentFile());
assertThat(file.createNewFile()).isTrue();
// Write 1 byte because 0byte files are not valid in the picker db
try (FileOutputStream fos = new FileOutputStream(file)) {
fos.write(1);
}
if (lastModifiedTime != -1) {
file.setLastModified(lastModifiedTime);
}
final Uri uri = MediaStore.scanFile(mIsolatedResolver, file);
assertWithMessage("Uri obtained by scanning file " + file)
.that(uri).isNotNull();
// Wait for picker db sync
MediaStore.waitForIdle(mIsolatedResolver);
return uri;
}
private void ensureParentExists(File parent) {
if (!parent.exists()) {
parent.mkdirs();
}
assertThat(parent.exists()).isTrue();
}
private File getDownloadsDir() {
return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS);
}
private File getDcimDir() {
return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM);
}
private File getPicturesDir() {
return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
}
private File getMoviesDir() {
return new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_MOVIES);
}
private File getCameraDir() {
return new File(getDcimDir(), "Camera");
}
private File getScreenshotsDir() {
return new File(getPicturesDir(), Environment.DIRECTORY_SCREENSHOTS);
}
private File getScreenshotsDirFromDownloadsDir() {
return new File(getDownloadsDir(), Environment.DIRECTORY_SCREENSHOTS);
}
private File createHiddenDir() throws Exception {
File parentDir = new File(Environment.getExternalStorageDirectory(),
Environment.DIRECTORY_DOWNLOADS);
File dir = new File(parentDir, HIDDEN_DIR_NAME);
dir.mkdirs();
File nomedia = new File(dir, ".nomedia");
nomedia.createNewFile();
MediaStore.scanFile(mIsolatedResolver, nomedia);
return dir;
}
private void deleteAllFilesNoThrow() {
try (Cursor c = mIsolatedResolver.query(
MediaStore.Files.getContentUri(VOLUME_EXTERNAL),
new String[] {MediaStore.MediaColumns.DATA}, null, null)) {
while(c.moveToNext()) {
(new File(c.getString(
c.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA)))).delete();
}
}
}
}