Merge "plurals cleanup in android/packages/providers/MediaProvider" into sc-mainline-prod
diff --git a/apex/framework/api/current.txt b/apex/framework/api/current.txt
index 187da31..0a53fb5 100644
--- a/apex/framework/api/current.txt
+++ b/apex/framework/api/current.txt
@@ -8,7 +8,6 @@
method @NonNull public static android.app.PendingIntent createFavoriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
method @NonNull public static android.app.PendingIntent createTrashRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>, boolean);
method @NonNull public static android.app.PendingIntent createWriteRequest(@NonNull android.content.ContentResolver, @NonNull java.util.Collection<android.net.Uri>);
- method @Nullable public static String getCloudProvider(@NonNull android.content.ContentResolver);
method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
method public static long getGeneration(@NonNull android.content.Context, @NonNull String);
@@ -23,7 +22,6 @@
method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
method public static boolean isCurrentSystemGallery(@NonNull android.content.ContentResolver, int, @NonNull String);
- method public static boolean notifyCloudEvent(@NonNull android.content.ContentResolver);
method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
field public static final String ACTION_IMAGE_CAPTURE = "android.media.action.IMAGE_CAPTURE";
diff --git a/apex/framework/api/module-lib-current.txt b/apex/framework/api/module-lib-current.txt
index eae8f7a..d802177 100644
--- a/apex/framework/api/module-lib-current.txt
+++ b/apex/framework/api/module-lib-current.txt
@@ -1,41 +1 @@
// Signature format: 2.0
-package android.provider {
-
- public final class CloudMediaProviderContract {
- field public static final String EXTRA_FILTER_ALBUM = "android.provider.extra.FILTER_ALBUM";
- field public static final String EXTRA_FILTER_MIMETYPE = "android.provider.extra.FILTER_MIMETYPE";
- field public static final String EXTRA_FILTER_SIZE_BYTES = "android.provider.extra.FILTER_SIZE_BYTES";
- field public static final String EXTRA_GENERATION = "android.provider.extra.GENERATION";
- field public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
- field public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
- field public static final String METHOD_GET_MEDIA_INFO = "android:getMediaInfo";
- field public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
- }
-
- public static final class CloudMediaProviderContract.AlbumColumns {
- field public static final String DATE_TAKEN_MS = "date_taken_ms";
- field public static final String DISPLAY_NAME = "display_name";
- field public static final String ID = "id";
- field public static final String MEDIA_COUNT = "album_media_count";
- field public static final String MEDIA_COVER_ID = "album_media_cover_id";
- }
-
- public static final class CloudMediaProviderContract.MediaColumns {
- field public static final String DATE_TAKEN_MS = "date_taken_ms";
- field public static final String DURATION_MS = "duration_ms";
- field public static final String GENERATION_MODIFIED = "generation_modified";
- field public static final String ID = "id";
- field public static final String IS_FAVORITE = "is_favorite";
- field public static final String MEDIA_STORE_URI = "media_store_uri";
- field public static final String MIME_TYPE = "mime_type";
- field public static final String SIZE_BYTES = "size_bytes";
- }
-
- public static final class CloudMediaProviderContract.MediaInfo {
- field public static final String MEDIA_COUNT = "media_count";
- field public static final String MEDIA_GENERATION = "media_generation";
- field public static final String MEDIA_VERSION = "media_version";
- }
-
-}
-
diff --git a/apex/framework/java/android/provider/CloudMediaProvider.java b/apex/framework/java/android/provider/CloudMediaProvider.java
index 3ef174f..ae96fb1 100644
--- a/apex/framework/java/android/provider/CloudMediaProvider.java
+++ b/apex/framework/java/android/provider/CloudMediaProvider.java
@@ -16,6 +16,7 @@
package android.provider;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ACCOUNT_INFO;
import static android.provider.CloudMediaProviderContract.METHOD_GET_MEDIA_INFO;
import static android.provider.CloudMediaProviderContract.URI_PATH_ALBUM;
import static android.provider.CloudMediaProviderContract.URI_PATH_DELETED_MEDIA;
@@ -116,6 +117,25 @@
}
/**
+ * Returns account related information for the media collection.
+ * <p>
+ * This is useful for the OS to populate a settings page with account information and allow
+ * users configure their media collection account.
+ *
+ * @param extras containing keys to filter result:
+ * <ul>
+ * <li> {@link CloudMediaProviderContract.AccountInfo#ACTIVE_ACCOUNT_NAME}
+ * <li> {@link CloudMediaProviderContract.AccountInfo#ACCOUNT_CONFIGURATION_INTENT}
+ * </ul>
+ *
+ * @return {@link Bundle} containing {@link CloudMediaProviderContract.AccountInfo}
+ */
+ @NonNull
+ public Bundle onGetAccountInfo(@Nullable Bundle extras) {
+ throw new UnsupportedOperationException("getAccountInfo not supported");
+ }
+
+ /**
* Returns metadata about the media collection itself.
* <p>
* This is useful for the OS to determine if its cache of media items in the collection is
@@ -281,6 +301,8 @@
throws FileNotFoundException {
if (METHOD_GET_MEDIA_INFO.equals(method)) {
return onGetMediaInfo(extras);
+ } else if (METHOD_GET_ACCOUNT_INFO.equals(method)) {
+ return onGetAccountInfo(extras);
} else {
throw new UnsupportedOperationException("Method not supported " + method);
}
diff --git a/apex/framework/java/android/provider/CloudMediaProviderContract.java b/apex/framework/java/android/provider/CloudMediaProviderContract.java
index f8a2811..840f009 100644
--- a/apex/framework/java/android/provider/CloudMediaProviderContract.java
+++ b/apex/framework/java/android/provider/CloudMediaProviderContract.java
@@ -17,6 +17,7 @@
package android.provider;
import android.annotation.SystemApi;
+import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.database.Cursor;
@@ -34,7 +35,6 @@
*
* @hide
*/
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public final class CloudMediaProviderContract {
private static final String TAG = "CloudMediaProviderContract";
@@ -325,6 +325,30 @@
public static final String MEDIA_COUNT = "media_count";
}
+ /** Constants related to the account information */
+ public static final class AccountInfo {
+ private AccountInfo() {}
+
+ /**
+ * Name of the account owning the media collection synced from the cloud provider.
+ * <p>
+ * Type: STRING
+ *
+ * @see CloudMediaProvider#onGetAccountInfo
+ */
+ public static final String ACTIVE_ACCOUNT_NAME = "active_account_name";
+
+ /**
+ * {@link Intent} Intent to launch an {@link Activity} to allow users configure their media
+ * collection account information like the active account.
+ * <p>
+ * Type: PARCELABLE
+ *
+ * @see CloudMediaProvider#onGetAccountInfo
+ */
+ public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
+ }
+
/**
* Opaque pagination token to retrieve the next page (cursor) from a media or album query.
* <p>
@@ -433,10 +457,17 @@
*
* {@hide}
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final String METHOD_GET_MEDIA_INFO = "android:getMediaInfo";
/**
+ * Constant used to execute {@link CloudMediaProvider#onGetAccountInfo} via
+ * {@link ContentProvider#call}.
+ *
+ * {@hide}
+ */
+ public static final String METHOD_GET_ACCOUNT_INFO = "android:getAccountInfo";
+
+ /**
* URI path for {@link CloudMediaProvider#onQueryMedia}
*
* {@hide}
@@ -470,4 +501,11 @@
* {@hide}
*/
public static final String URI_PATH_MEDIA_INFO = "media_info";
+
+ /**
+ * URI path for {@link CloudMediaProvider#onGetAccountInfo}
+ *
+ * {@hide}
+ */
+ public static final String URI_PATH_ACCOUNT_INFO = "account_info";
}
diff --git a/apex/framework/java/android/provider/MediaStore.java b/apex/framework/java/android/provider/MediaStore.java
index 8a8a811..39df119 100644
--- a/apex/framework/java/android/provider/MediaStore.java
+++ b/apex/framework/java/android/provider/MediaStore.java
@@ -4579,6 +4579,8 @@
* enabled.
*
* See android.provider.CloudMediaProvider
+ *
+ * @hide
*/
// TODO(b/202733511): Convert See to @see tag after CloudMediaProvider API is unhidden
@Nullable
@@ -4600,6 +4602,8 @@
* {@link #getCloudProvider(ContentResolver)}, the request will be unsuccessful.
*
* @return {@code true} if the notification was successful, {@code false} otherwise
+ *
+ * @hide
*/
public static boolean notifyCloudEvent(@NonNull ContentResolver resolver) {
Objects.requireNonNull(resolver);
diff --git a/res/layout/item_album_grid.xml b/res/layout/item_album_grid.xml
index 8c148c1..ed6578a 100644
--- a/res/layout/item_album_grid.xml
+++ b/res/layout/item_album_grid.xml
@@ -42,14 +42,16 @@
<TextView
android:id="@+id/album_name"
android:layout_width="wrap_content"
- android:layout_height="@dimen/picker_album_name_min_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_album_name_min_height"
android:layout_marginTop="@dimen/picker_album_name_margin"
android:textAppearance="?attr/textAppearanceSubtitle2"/>
<TextView
android:id="@+id/item_count"
android:layout_width="wrap_content"
- android:layout_height="@dimen/picker_album_item_count_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_album_item_count_height"
android:layout_marginTop="@dimen/picker_album_item_count_margin"
android:textAppearance="?attr/textAppearanceCaption"/>
diff --git a/res/layout/item_date_header.xml b/res/layout/item_date_header.xml
index fd6f826..22c854d 100644
--- a/res/layout/item_date_header.xml
+++ b/res/layout/item_date_header.xml
@@ -17,6 +17,7 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/date_header_title"
android:layout_width="match_parent"
- android:layout_height="@dimen/picker_date_header_height"
+ android:layout_height="wrap_content"
+ android:minHeight="@dimen/picker_date_header_height"
android:padding="@dimen/picker_date_header_padding"
android:textAppearance="@style/PickerDateHeader"/>
\ No newline at end of file
diff --git a/src/com/android/providers/media/PickerUriResolver.java b/src/com/android/providers/media/PickerUriResolver.java
index 1b4d9fb..d39071d 100644
--- a/src/com/android/providers/media/PickerUriResolver.java
+++ b/src/com/android/providers/media/PickerUriResolver.java
@@ -167,6 +167,11 @@
+ CloudMediaProviderContract.URI_PATH_MEDIA_INFO);
}
+ public static Uri getAccountInfoUri(String authority) {
+ return Uri.parse("content://" + authority + "/"
+ + CloudMediaProviderContract.URI_PATH_ACCOUNT_INFO);
+ }
+
public static Uri getAlbumUri(String authority) {
return Uri.parse("content://" + authority + "/"
+ CloudMediaProviderContract.URI_PATH_ALBUM);
diff --git a/src/com/android/providers/media/photopicker/PickerDataLayer.java b/src/com/android/providers/media/photopicker/PickerDataLayer.java
index 11d9338..b343c81 100644
--- a/src/com/android/providers/media/photopicker/PickerDataLayer.java
+++ b/src/com/android/providers/media/photopicker/PickerDataLayer.java
@@ -17,8 +17,10 @@
package com.android.providers.media.photopicker;
import static android.provider.CloudMediaProviderContract.EXTRA_GENERATION;
+import static android.provider.CloudMediaProviderContract.METHOD_GET_ACCOUNT_INFO;
import static android.provider.CloudMediaProviderContract.MediaColumns;
import static android.provider.CloudMediaProviderContract.MediaInfo;
+import static com.android.providers.media.PickerUriResolver.getAccountInfoUri;
import static com.android.providers.media.PickerUriResolver.getAlbumUri;
import static com.android.providers.media.PickerUriResolver.getMediaUri;
import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
@@ -28,11 +30,13 @@
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.CloudMediaProviderContract;
import android.provider.CloudMediaProviderContract.AlbumColumns;
import android.provider.MediaStore;
import android.util.Log;
@@ -118,6 +122,32 @@
return mergeCursor;
}
+ public AccountInfo fetchCloudAccountInfo() {
+ final String cloudProvider = mDbFacade.getCloudProvider();
+ if (cloudProvider == null) {
+ return null;
+ }
+
+ try {
+ final Bundle accountBundle = mContext.getContentResolver().call(
+ getAccountInfoUri(cloudProvider), METHOD_GET_ACCOUNT_INFO, /* arg */ null,
+ /* extras */ null);
+ final String accountName = accountBundle.getString(
+ CloudMediaProviderContract.AccountInfo.ACTIVE_ACCOUNT_NAME);
+ final Intent configIntent = (Intent) accountBundle.getParcelable(
+ CloudMediaProviderContract.AccountInfo.ACCOUNT_CONFIGURATION_INTENT);
+
+ if (accountName == null) {
+ return null;
+ }
+
+ return new AccountInfo(accountName, configIntent);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to fetch account info from cloud provider: " + cloudProvider, e);
+ return null;
+ }
+ }
+
private Cursor queryProviderAlbums(String authority, Bundle queryArgs) {
if (authority == null) {
// Can happen if there is no cloud provider
@@ -152,4 +182,14 @@
// Cloud provider has switched since last query, so no longer valid
return null;
}
+
+ public static class AccountInfo {
+ public final String accountName;
+ public final Intent accountConfigurationIntent;
+
+ public AccountInfo(String accountName, Intent accountConfigurationIntent) {
+ this.accountName = accountName;
+ this.accountConfigurationIntent = accountConfigurationIntent;
+ }
+ }
}
diff --git a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
index 60fedab..909f28c 100644
--- a/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
+++ b/tests/src/com/android/providers/media/PickerProviderMediaGenerator.java
@@ -16,13 +16,16 @@
package com.android.providers.media;
+import static android.provider.CloudMediaProviderContract.AccountInfo;
import static android.provider.CloudMediaProviderContract.AlbumColumns;
import static android.provider.CloudMediaProviderContract.MediaColumns;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
+import android.content.Intent;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.os.Bundle;
import android.os.SystemClock;
import android.provider.CloudMediaProvider;
@@ -73,6 +76,8 @@
private final List<TestAlbum> mAlbums = new ArrayList<>();
private String mVersion;
private long mGeneration;
+ private String mAccountName;
+ private Intent mAccountConfigurationIntent;
public Cursor getMedia(long generation, String albumdId, String mimeType, long sizeBytes) {
return getCursor(mMedia, generation, albumdId, mimeType, sizeBytes,
@@ -89,6 +94,20 @@
/* isDeleted */ true);
}
+ public Bundle getAccountInfo() {
+ Bundle bundle = new Bundle();
+ bundle.putString(AccountInfo.ACTIVE_ACCOUNT_NAME, mAccountName);
+ bundle.putParcelable(AccountInfo.ACCOUNT_CONFIGURATION_INTENT,
+ mAccountConfigurationIntent);
+
+ return bundle;
+ }
+
+ public void setAccountInfo(String accountName, Intent configIntent) {
+ mAccountName = accountName;
+ mAccountConfigurationIntent = configIntent;
+ }
+
public void addMedia(String localId, String cloudId) {
mDeletedMedia.remove(createPlaceholderMedia(localId, cloudId));
mMedia.add(0, createTestMedia(localId, cloudId));
diff --git a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
index 93ff18a..7d661e1 100644
--- a/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
+++ b/tests/src/com/android/providers/media/cloudproviders/CloudProviderPrimary.java
@@ -101,4 +101,9 @@
return bundle;
}
+
+ @Override
+ public Bundle onGetAccountInfo(Bundle extras) {
+ return mMediaGenerator.getAccountInfo();
+ }
}
diff --git a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
index b003c70..a0e69fc 100644
--- a/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
+++ b/tests/src/com/android/providers/media/photopicker/PickerDataLayerTest.java
@@ -29,6 +29,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
import android.content.Context;
+import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.os.SystemClock;
@@ -518,6 +519,29 @@
}
}
+ @Test
+ public void testFetchCloudAccountInfo() {
+ // Cloud provider is not set so cloud account info is null
+ assertThat(mDataLayer.fetchCloudAccountInfo()).isNull();
+
+ // Set cloud provider
+ mFacade.setCloudProvider(CLOUD_PRIMARY_PROVIDER_AUTHORITY);
+
+ // Still null since cloud provider doesn't return account info yet
+ assertThat(mDataLayer.fetchCloudAccountInfo()).isNull();
+
+ // Fake cloud provider cloud account info
+ final String expectedName = "bar";
+ final Intent expectedIntent = new Intent("foo");
+ mCloudPrimaryMediaGenerator.setAccountInfo(expectedName, expectedIntent);
+
+ // Verify account info
+ final PickerDataLayer.AccountInfo info = mDataLayer.fetchCloudAccountInfo();
+ assertThat(info).isNotNull();
+ assertThat(info.accountName).isEqualTo(expectedName);
+ assertThat(info.accountConfigurationIntent).isEqualTo(expectedIntent);
+ }
+
private static void waitForIdle() {
final CountDownLatch latch = new CountDownLatch(1);
BackgroundThread.getExecutor().execute(() -> {
diff --git a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
index bb97c4d..14a4881 100644
--- a/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
+++ b/tests/src/com/android/providers/media/photopicker/espresso/PhotoPickerBaseTest.java
@@ -176,9 +176,9 @@
// Create files and change dateModified so that we can predict the recyclerView item
// position. Set modified date ahead of time, so that even if other files are created,
// the below files always have positions 1, 2 and 3.
- createFile(IMAGE_1_FILE, timeNow + 3000);
- createFile(IMAGE_2_FILE, timeNow + 2000);
- createFile(VIDEO_FILE, timeNow + 1000);
+ createFile(IMAGE_1_FILE, timeNow + 30000);
+ createFile(IMAGE_2_FILE, timeNow + 20000);
+ createFile(VIDEO_FILE, timeNow + 10000);
}
private static void pollForCondition(Supplier<Boolean> condition, String errorMessage)