Add some category methods
- Extract CursorUtils for Category and Item
- Add related methods of getCategories and getCategoryItems in
PickerViewModdel
- Fix getItem with categogry issue
- Support localizations for default categories.
Test: atest CategoryTest
Bug: 185801192
Change-Id: I5eb39f94e28c779d20176063e12e300a4e5957a1
Merged-In: I5eb39f94e28c779d20176063e12e300a4e5957a1
(cherry picked from commit b6cf17bb072a6983bbdc0816d97489b7765420ad)
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fb6a708..1b93546 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -115,6 +115,17 @@
<!-- Text shown on the add button for multi-select in PhotoPicker. [CHAR LIMIT=30] -->
<string name="picker_add_button_multi_select">Add (<xliff:g id="count" example="42">^1</xliff:g>)</string>
+ <!-- Title for the category in the picker that offers items in Camera folder. [CHAR LIMIT=24] -->
+ <string name="picker_category_camera">Camera</string>
+ <!-- Title for the category in the picker that offers downloaded items. [CHAR LIMIT=24] -->
+ <string name="picker_category_downloads">Downloads</string>
+ <!-- Title for the category in the picker that offers favorite items. [CHAR LIMIT=24] -->
+ <string name="picker_category_favorites">Favorites</string>
+ <!-- Title for the category in the picker that offers screenshots. [CHAR LIMIT=24] -->
+ <string name="picker_category_screenshots">Screenshots</string>
+ <!-- Title for the category in the picker that offers videos. [CHAR LIMIT=24] -->
+ <string name="picker_category_videos">@string/root_videos</string>
+
<!-- ========================= BEGIN AUTO-GENERATED BY gen_strings.py ========================= -->
<!-- ========================= WRITE STRINGS ========================= -->
diff --git a/src/com/android/providers/media/photopicker/data/ItemsProvider.java b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
index 9499d42..bf1a769 100644
--- a/src/com/android/providers/media/photopicker/data/ItemsProvider.java
+++ b/src/com/android/providers/media/photopicker/data/ItemsProvider.java
@@ -94,7 +94,7 @@
int offset, int limit, @Nullable String mimeType, @NonNull UserId userId) throws
IllegalArgumentException, IllegalStateException {
// Validate incoming params
- if (category != null && Category.isValidCategory(category)) {
+ if (category != null && !Category.isValidCategory(category)) {
throw new IllegalArgumentException("ItemsProvider does not support the given "
+ "category: " + category);
}
@@ -160,9 +160,10 @@
}
return new String[] {
- category,
- String.valueOf(getMediaStoreUriForItem(c.getLong(0))),
- String.valueOf(c.getCount())
+ category, // category name
+ String.valueOf(getMediaStoreUriForItem(c.getLong(0))), // coverUri
+ String.valueOf(c.getCount()), // item count
+ category // category type
};
}
diff --git a/src/com/android/providers/media/photopicker/data/model/Category.java b/src/com/android/providers/media/photopicker/data/model/Category.java
index e1a063e..5a17ede 100644
--- a/src/com/android/providers/media/photopicker/data/model/Category.java
+++ b/src/com/android/providers/media/photopicker/data/model/Category.java
@@ -16,16 +16,26 @@
package com.android.providers.media.photopicker.data.model;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorInt;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+
import android.annotation.StringDef;
+import android.content.Context;
import android.database.Cursor;
+import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.provider.MediaStore.Files.FileColumns;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.providers.media.R;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -123,11 +133,11 @@
}
private static String[] CATEGORIES = {
- CATEGORY_SCREENSHOTS,
+ CATEGORY_FAVORITES,
CATEGORY_CAMERA,
CATEGORY_VIDEOS,
- CATEGORY_FAVORITES,
- CATEGORY_DOWNLOADS
+ CATEGORY_SCREENSHOTS,
+ CATEGORY_DOWNLOADS,
};
public static List<String> CATEGORIES_LIST = Collections.unmodifiableList(
@@ -137,6 +147,19 @@
return CATEGORIES_LIST.contains(category);
}
+ @CategoryType
+ private String mCategoryType;
+ private String mCategoryName;
+ private Uri mCoverUri;
+ private int mItemCount;
+
+ private Category() {}
+
+ @VisibleForTesting
+ Category(@NonNull Cursor cursor) {
+ updateFromCursor(cursor);
+ }
+
/**
* Defines category columns for each category
*/
@@ -144,13 +167,95 @@
public static String NAME = "name";
public static String COVER_URI = "cover_uri";
public static String NUMBER_OF_ITEMS = "number_of_items";
+ public static String CATEGORY_TYPE = "category_type";
public static String[] getAllColumns() {
return new String[] {
NAME,
COVER_URI,
- NUMBER_OF_ITEMS
+ NUMBER_OF_ITEMS,
+ CATEGORY_TYPE,
};
}
}
+
+ /**
+ * @return localized category name if {@code context} is not null and {@link #mCategoryType} is
+ * in {@link #CATEGORIES}, {@link #mCategoryName} otherwise.
+ */
+ public String getCategoryName(@Nullable Context context) {
+ if (context != null) {
+ final String categoryName = getCategoryName(context, mCategoryType);
+ if (categoryName != null) {
+ return categoryName;
+ }
+ }
+ return mCategoryName;
+ }
+
+ /**
+ * @return localized category name if {@link #mCategoryType} is in {@link #CATEGORIES},
+ * {@code null} otherwise.
+ */
+ public static String getCategoryName(@NonNull Context context,
+ @NonNull @CategoryType String categoryType) {
+ switch (categoryType) {
+ case CATEGORY_FAVORITES:
+ return context.getString(R.string.picker_category_favorites);
+ case CATEGORY_VIDEOS:
+ return context.getString(R.string.picker_category_videos);
+ case CATEGORY_CAMERA:
+ return context.getString(R.string.picker_category_camera);
+ case CATEGORY_SCREENSHOTS:
+ return context.getString(R.string.picker_category_screenshots);
+ case CATEGORY_DOWNLOADS:
+ return context.getString(R.string.picker_category_downloads);
+ default:
+ return null;
+ }
+ }
+
+ @CategoryType
+ public String getCategoryType() {
+ return mCategoryType;
+ }
+
+ public Uri getCoverUri() {
+ return mCoverUri;
+ }
+
+ public int getItemCount() {
+ return mItemCount;
+ }
+
+ /**
+ * Get the category instance with the {@link #mCategoryType} is {@link #CATEGORY_DEFAULT}
+ *
+ * @return the default category
+ */
+ public static Category getDefaultCategory() {
+ final Category category = new Category();
+ category.mCategoryType = CATEGORY_DEFAULT;
+ return category;
+ }
+
+ /**
+ * @return {@link Category} from the given {@code cursor}
+ */
+ public static Category fromCursor(@NonNull Cursor cursor) {
+ final Category category = new Category(cursor);
+ return category;
+ }
+
+ /**
+ * Update the category based on the {@code cursor}
+ *
+ * @param cursor the cursor to update the data
+ */
+ public void updateFromCursor(@NonNull Cursor cursor) {
+ mCategoryName = getCursorString(cursor, CategoryColumns.NAME);
+ mCoverUri = Uri.parse(getCursorString(cursor, CategoryColumns.COVER_URI));
+ mItemCount = getCursorInt(cursor, CategoryColumns.NUMBER_OF_ITEMS);
+ mCategoryType = getCursorString(cursor, CategoryColumns.CATEGORY_TYPE);
+ }
}
diff --git a/src/com/android/providers/media/photopicker/data/model/Item.java b/src/com/android/providers/media/photopicker/data/model/Item.java
index da282af..4ae9643 100644
--- a/src/com/android/providers/media/photopicker/data/model/Item.java
+++ b/src/com/android/providers/media/photopicker/data/model/Item.java
@@ -16,6 +16,9 @@
package com.android.providers.media.photopicker.data.model;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorLong;
+import static com.android.providers.media.photopicker.util.CursorUtils.getCursorString;
+
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
@@ -200,38 +203,4 @@
mIsVideo = true;
}
}
-
- @Nullable
- private static String getCursorString(Cursor cursor, String columnName) {
- if (cursor == null) {
- return null;
- }
- final int index = cursor.getColumnIndex(columnName);
- return (index != -1) ? cursor.getString(index) : null;
- }
-
- /**
- * Missing or null values are returned as -1.
- */
- private static long getCursorLong(Cursor cursor, String columnName) {
- if (cursor == null) {
- return -1;
- }
-
- final int index = cursor.getColumnIndex(columnName);
- if (index == -1) {
- return -1;
- }
-
- final String value = cursor.getString(index);
- if (value == null) {
- return -1;
- }
-
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException e) {
- return -1;
- }
- }
}
diff --git a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
index 1a8f2ee..7876fae 100644
--- a/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
+++ b/src/com/android/providers/media/photopicker/ui/PhotosTabFragment.java
@@ -86,4 +86,4 @@
PreviewFragment.show(getActivity().getSupportFragmentManager());
}
}
-}
\ No newline at end of file
+}
diff --git a/src/com/android/providers/media/photopicker/util/CursorUtils.java b/src/com/android/providers/media/photopicker/util/CursorUtils.java
new file mode 100644
index 0000000..a992f4e
--- /dev/null
+++ b/src/com/android/providers/media/photopicker/util/CursorUtils.java
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import android.database.Cursor;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provide the utility methods to handle cursor.
+ */
+public class CursorUtils {
+
+ /**
+ * Get the string from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the string value from the {@code cursor}, or {@code null} when {@code cursor} doesn't
+ * contain {@code columnName}
+ */
+ @Nullable
+ public static String getCursorString(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return (index != -1) ? cursor.getString(index) : null;
+ }
+
+ /**
+ * Get the long value from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the long value from the {@code cursor}, or -1 when {@code cursor} doesn't contain
+ * {@code columnName}
+ */
+ public static long getCursorLong(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ if (index == -1) {
+ return -1;
+ }
+
+ final String value = cursor.getString(index);
+ if (value == null) {
+ return -1;
+ }
+
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return -1;
+ }
+ }
+
+ /**
+ * Get the int value from the {@code cursor} with the {@code columnName}.
+ *
+ * @param cursor the cursor to be parsed
+ * @param columnName the column name of the value
+ * @return the int value from the {@code cursor}, or 0 when {@code cursor} doesn't contain
+ * {@code columnName}
+ */
+ public static int getCursorInt(@NonNull Cursor cursor, @NonNull String columnName) {
+ final int index = cursor.getColumnIndex(columnName);
+ return (index != -1) ? cursor.getInt(index) : 0;
+ }
+}
diff --git a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
index 16aa8f9..80d7c6e 100644
--- a/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
+++ b/src/com/android/providers/media/photopicker/viewmodel/PickerViewModel.java
@@ -28,17 +28,20 @@
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
+import android.text.TextUtils;
import android.util.Log;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
-import com.android.providers.media.photopicker.util.DateTimeUtils;
import com.android.providers.media.photopicker.data.ItemsProvider;
import com.android.providers.media.photopicker.data.UserIdManager;
+import com.android.providers.media.photopicker.data.model.Category;
+import com.android.providers.media.photopicker.data.model.Category.CategoryType;
import com.android.providers.media.photopicker.data.model.Item;
import com.android.providers.media.photopicker.data.model.UserId;
+import com.android.providers.media.photopicker.util.DateTimeUtils;
import com.android.providers.media.util.ForegroundThread;
import java.util.ArrayList;
@@ -55,8 +58,14 @@
private static final int RECENT_MINIMUM_COUNT = 12;
public static final int DEFAULT_MAX_SELECTION_LIMIT = 100;
+ // TODO(b/193857982): we keep these four data sets now, we may find a way to reduce the data
+ // set to reduce memories.
+ // the list of Items with all photos and videos
private MutableLiveData<List<Item>> mItemList;
+ // the list of Items with all photos and videos in category
+ private MutableLiveData<List<Item>> mCategoryItemList;
private MutableLiveData<Map<Uri, Item>> mSelectedItemList = new MutableLiveData<>();
+ private MutableLiveData<List<Category>> mCategoryList;
private final ItemsProvider mItemsProvider;
private final UserIdManager mUserIdManager;
private boolean mSelectMultiple = false;
@@ -67,6 +76,8 @@
// Show max label text view if and only if caller sets acceptable value for
// {@link MediaStore#EXTRA_PICK_IMAGES_MAX}
private boolean mShowMaxLabel = false;
+ @CategoryType
+ private String mCurrentCategoryType;
public PickerViewModel(@NonNull Application application) {
super(application);
@@ -76,7 +87,7 @@
}
/**
- * @return the Map of selected Item.
+ * @return the {@link LiveData} of selected items {@link #mSelectedItemList}.
*/
public LiveData<Map<Uri, Item>> getSelectedItems() {
if (mSelectedItemList.getValue() == null) {
@@ -87,7 +98,7 @@
}
/**
- * Add the selected Item.
+ * Add the selected {@code item} into {@link #mSelectedItemList}.
*/
public void addSelectedItem(Item item) {
if (mSelectedItemList.getValue() == null) {
@@ -101,7 +112,7 @@
}
/**
- * Clear the selected Item list.
+ * Clear the selected Item list {@link #mSelectedItemList}.
*/
public void clearSelectedItems() {
if (mSelectedItemList.getValue() == null) {
@@ -112,7 +123,9 @@
}
/**
- * Delete the selected Item.
+ * Delete the selected {@code item} from the selected item list {@link #mSelectedItemList}.
+ *
+ * @param item the item to be deleted from the selected item list
*/
public void deleteSelectedItem(Item item) {
if (mSelectedItemList.getValue() == null) {
@@ -149,7 +162,7 @@
}
/**
- * @return the list of Items with all photos and videos on the device.
+ * @return the list of Items with all photos and videos {@link #mItemList} on the device.
*/
public LiveData<List<Item>> getItems() {
if (mItemList == null) {
@@ -158,31 +171,39 @@
return mItemList;
}
- private List<Item> loadItems() {
+ private List<Item> loadItems(@Nullable @CategoryType String category) {
final List<Item> items = new ArrayList<>();
final UserId userId = mUserIdManager.getCurrentUserProfileId();
- try (Cursor cursor = mItemsProvider.getItems(/* category */ null, /* offset */ 0,
+ try (Cursor cursor = mItemsProvider.getItems(category, /* offset */ 0,
/* limit */ -1, mMimeTypeFilter, userId)) {
if (cursor == null) {
return items;
}
+ // We only add the RECENT header on the PhotosTabFragment with CATEGORY_DEFAULT. In this
+ // case, we call this method {loadItems} with null category. When the category is not
+ // empty, we don't show the RECENT header.
+ final boolean showRecent = TextUtils.isEmpty(category);
+
int recentSize = 0;
long currentDateTaken = 0;
// add max label message header item
if (mShowMaxLabel) {
items.add(Item.createMessageItem());
}
- // add Recent date header
- items.add(Item.createDateItem(0));
+
+ if (showRecent) {
+ // add Recent date header
+ items.add(Item.createDateItem(0));
+ }
while (cursor.moveToNext()) {
// TODO(b/188394433): Return userId in the cursor so that we do not need to pass it
- // here again.
+ // here again.
final Item item = Item.fromCursor(cursor, userId);
final long dateTaken = item.getDateTaken();
// the minimum count of items in recent is not reached
- if (recentSize < RECENT_MINIMUM_COUNT) {
+ if (showRecent && recentSize < RECENT_MINIMUM_COUNT) {
recentSize++;
currentDateTaken = dateTaken;
}
@@ -197,18 +218,23 @@
}
}
- Log.d(TAG, "Loaded " + items.size() + " items for user " + userId.toString());
+ if (TextUtils.isEmpty(category)) {
+ Log.d(TAG, "Loaded " + items.size() + " items for user " + userId.toString());
+ } else {
+ Log.d(TAG, "Loaded " + items.size() + " items in " + category + " for user "
+ + userId.toString());
+ }
return items;
}
private void loadItemsAsync() {
ForegroundThread.getExecutor().execute(() -> {
- mItemList.postValue(loadItems());
+ mItemList.postValue(loadItems(/* category= */ null));
});
}
/**
- * Update the item List
+ * Update the item List {@link #mItemList}
*/
public void updateItems() {
if (mItemList == null) {
@@ -218,14 +244,95 @@
}
/**
- * Return whether supports multiple select or not
+ * Get the list of all photos and videos with the specific {@code category} on the device.
+ *
+ * @param category the category we want to be queried
+ * @return the list of all photos and videos with the specific {@code category}
+ * {@link #mCategoryItemList}
+ */
+ public LiveData<List<Item>> getCategoryItems(@NonNull @CategoryType String category) {
+ if (mCategoryItemList == null || !TextUtils.equals(category, mCurrentCategoryType)) {
+ mCurrentCategoryType = category;
+ updateCategoryItems(category);
+ }
+ return mCategoryItemList;
+ }
+
+ private void loadCategoryItemsAsync(@NonNull @CategoryType String category) {
+ ForegroundThread.getExecutor().execute(() -> {
+ mCategoryItemList.postValue(loadItems(category));
+ });
+ }
+
+ /**
+ * Update the item List with the {@code category} {@link #mCategoryItemList}
+ */
+ public void updateCategoryItems(@NonNull @CategoryType String category) {
+ if (mCategoryItemList == null) {
+ mCategoryItemList = new MutableLiveData<>();
+ }
+ loadCategoryItemsAsync(category);
+ }
+
+ /**
+ * @return the list of Categories {@link #mCategoryList}
+ */
+ public LiveData<List<Category>> getCategories() {
+ if (mCategoryList == null) {
+ updateCategories();
+ }
+ return mCategoryList;
+ }
+
+ private List<Category> loadCategories() {
+ final List<Category> categoryList = new ArrayList<>();
+ final UserId userId = mUserIdManager.getCurrentUserProfileId();
+ final Cursor cursor = mItemsProvider.getCategories(mMimeTypeFilter, userId);
+ if (cursor == null) {
+ return categoryList;
+ }
+
+ while (cursor.moveToNext()) {
+ final Category category = Category.fromCursor(cursor);
+ categoryList.add(category);
+ }
+
+ Log.d(TAG, "Loaded " + categoryList.size() + " categories for user " + userId.toString());
+ return categoryList;
+ }
+
+ private void loadCategoriesAsync() {
+ ForegroundThread.getExecutor().execute(() -> {
+ mCategoryList.postValue(loadCategories());
+ });
+ }
+
+ /**
+ * Update the category List {@link #mCategoryList}
+ */
+ public void updateCategories() {
+ if (mCategoryList == null) {
+ mCategoryList = new MutableLiveData<>();
+ }
+ loadCategoriesAsync();
+ }
+
+ /**
+ * Return whether supports multiple select {@link #mSelectMultiple} or not
*/
public boolean canSelectMultiple() {
return mSelectMultiple;
}
/**
- * Parse values from Intent and set corresponding fields
+ * Return whether the {@link #mMimeTypeFilter} is {@code null} or not
+ */
+ public boolean hasMimeTypeFilter() {
+ return !TextUtils.isEmpty(mMimeTypeFilter);
+ }
+
+ /**
+ * Parse values from {@code intent} and set corresponding fields
*/
public void parseValuesFromIntent(Intent intent) throws IllegalArgumentException {
mSelectMultiple = intent.getBooleanExtra(Intent.EXTRA_ALLOW_MULTIPLE, false);
@@ -255,7 +362,7 @@
}
}
- public static boolean isMimeTypeMedia(@Nullable String mimeType) {
+ private static boolean isMimeTypeMedia(@Nullable String mimeType) {
return isImageMimeType(mimeType) || isVideoMimeType(mimeType);
}
diff --git a/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java b/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java
new file mode 100644
index 0000000..6427ef4
--- /dev/null
+++ b/tests/src/com/android/providers/media/photopicker/data/model/CategoryTest.java
@@ -0,0 +1,126 @@
+/*
+ * 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.data.model;
+
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_CAMERA;
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_DEFAULT;
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_DOWNLOADS;
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_FAVORITES;
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_SCREENSHOTS;
+import static com.android.providers.media.photopicker.data.model.Category.CATEGORY_VIDEOS;
+import static com.android.providers.media.photopicker.data.model.Category.CategoryColumns;
+import static com.android.providers.media.photopicker.data.model.Category.CategoryType;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.net.Uri;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class CategoryTest {
+
+ @Test
+ public void testConstructor() {
+ final int itemCount = 10;
+ final String categoryName = "Album";
+ final Uri coverUri = Uri.parse("fakeCoverUri");
+ final String categoryType = CATEGORY_SCREENSHOTS;
+ final Cursor cursor = generateCursorForCategory(categoryName, coverUri, itemCount,
+ categoryType);
+ cursor.moveToFirst();
+
+ final Category category = new Category(cursor);
+
+ assertThat(category.getCategoryName(/* context= */ null)).isEqualTo(categoryName);
+ assertThat(category.getItemCount()).isEqualTo(itemCount);
+ assertThat(category.getCoverUri()).isEqualTo(coverUri);
+ assertThat(category.getCategoryType()).isEqualTo(categoryType);
+ }
+
+ /**
+ * If the {@code category} is not in {@link Category#CATEGORIES}, return {@code null}.
+ */
+ @Test
+ public void testGetCategoryName_notInList_returnNull() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_DEFAULT);
+
+ assertThat(categoryName).isNull();
+ }
+
+ @Test
+ public void testGetCategoryName_camera() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_CAMERA);
+
+ assertThat(categoryName).isEqualTo("Camera");
+ }
+
+ @Test
+ public void testGetCategoryName_downloads() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_DOWNLOADS);
+
+ assertThat(categoryName).isEqualTo("Downloads");
+ }
+
+ @Test
+ public void testGetCategoryName_favorites() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_FAVORITES);
+
+ assertThat(categoryName).isEqualTo("Favorites");
+ }
+
+ @Test
+ public void testGetCategoryName_screenshots() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_SCREENSHOTS);
+
+ assertThat(categoryName).isEqualTo("Screenshots");
+ }
+
+ @Test
+ public void testGetCategoryName_videos() {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String categoryName = Category.getCategoryName(context, CATEGORY_VIDEOS);
+
+ assertThat(categoryName).isEqualTo("Videos");
+ }
+
+ @Test
+ public void testGetDefaultCategory() {
+ final Category category = Category.getDefaultCategory();
+
+ assertThat(category.getCategoryType()).isEqualTo(CATEGORY_DEFAULT);
+ }
+
+ private static Cursor generateCursorForCategory(String categoryName, Uri coverUri,
+ int itemCount, @CategoryType String categoryType) {
+ final MatrixCursor cursor = new MatrixCursor(CategoryColumns.getAllColumns());
+ cursor.addRow(new Object[] {categoryName, coverUri, itemCount, categoryType});
+ return cursor;
+ }
+}