Merge tag 'android-13.0.0_r52' into int/13/fp3
Android 13.0.0 Release 52 (TQ3A.230605.012)
* tag 'android-13.0.0_r52':
QuickControls UI issues in RTL
Add toMediaMetadata and getProgramName that take list of program name keys as paramter
Support passing multiple res folders
Change-Id: I3fd9d424dd3b25d9d192751e8e9f7ffee3f34e9b
diff --git a/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramInfoExt.java b/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramInfoExt.java
index ce3d014..d377dbd 100644
--- a/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramInfoExt.java
+++ b/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramInfoExt.java
@@ -21,6 +21,7 @@
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioManager.ProgramInfo;
import android.hardware.radio.RadioMetadata;
import android.media.MediaMetadata;
@@ -29,6 +30,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
/**
* Proposed extensions to android.hardware.radio.RadioManager.ProgramInfo.
@@ -71,13 +73,23 @@
/**
* Returns program name suitable to display.
*
- * If there is no program name, it falls back to channel name. Flags related to
+ * <p>If there is no program name, it falls back to channel name. Flags related to
* the channel name display will be forwarded to the channel name generation method.
+ *
+ * @param info {@link ProgramInfo} to get name from
+ * @param flags Fallback method
+ * @param programNameOrder {@link RadioMetadata} metadata keys to pull from {@link ProgramInfo}
+ * for the program name
*/
- public static @NonNull String getProgramName(@NonNull ProgramInfo info, @NameFlag int flags) {
+ @NonNull
+ public static String getProgramName(@NonNull ProgramInfo info, @NameFlag int flags,
+ @NonNull String[] programNameOrder) {
+ Objects.requireNonNull(info, "info can not be null.");
+ Objects.requireNonNull(programNameOrder, "programNameOrder can not be null");
+
RadioMetadata meta = info.getMetadata();
if (meta != null) {
- for (String key : PROGRAM_NAME_ORDER) {
+ for (String key : programNameOrder) {
String value = meta.getString(key);
if (value != null) return value;
}
@@ -104,6 +116,17 @@
}
/**
+ * Returns program name suitable to display.
+ *
+ * <p>If there is no program name, it falls back to channel name. Flags related to
+ * the channel name display will be forwarded to the channel name generation method.
+ */
+ @NonNull
+ public static String getProgramName(@NonNull ProgramInfo info, @NameFlag int flags) {
+ return getProgramName(info, flags, PROGRAM_NAME_ORDER);
+ }
+
+ /**
* Proposed reimplementation of {@link RadioManager#ProgramInfo#getMetadata}.
*
* As opposed to the original implementation, it never returns null.
@@ -118,12 +141,69 @@
}
/**
- * Converts {@ProgramInfo} to {@MediaMetadata}.
+ * Converts {@link ProgramInfo} to {@link MediaMetadata} for displaying.
*
- * This method is meant to be used for currently playing station in {@link MediaSession}.
+ * <p>This method is meant to be used for displaying the currently playing station in
+ * {@link MediaSession}, only a subset of keys populated in {@link ProgramInfo#toMediaMetadata}
+ * will be populated in this method.
+ *
+ * <ul>
+ * The following keys will be populated in the {@link MediaMetadata}:
+ * <li>{@link MediaMetadata#METADATA_KEY_DISPLAY_TITLE}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_DISPLAY_SUBTITLE}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_ALBUM_ART}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_USER_RATING}</li>
+ * <ul/>
*
* @param info {@link ProgramInfo} to convert
- * @param isFavorite true, if a given program is a favorite
+ * @param isFavorite {@code true}, if a given program is a favorite
+ * @param imageResolver metadata images resolver/cache
+ * @param programNameOrder order of keys to look for program name in {@link ProgramInfo}
+ * @return {@link MediaMetadata} object
+ */
+ @NonNull
+ public static MediaMetadata toMediaDisplayMetadata(@NonNull ProgramInfo info,
+ boolean isFavorite, @NonNull ImageResolver imageResolver,
+ @NonNull String[] programNameOrder) {
+ Objects.requireNonNull(info, "info can not be null.");
+ Objects.requireNonNull(imageResolver, "imageResolver can not be null.");
+ Objects.requireNonNull(programNameOrder, "programNameOrder can not be null.");
+
+ MediaMetadata.Builder bld = new MediaMetadata.Builder();
+
+ ProgramSelector selector =
+ ProgramSelectorExt.createAmFmSelector(info.getLogicallyTunedTo().getValue());
+ String displayTitle = ProgramSelectorExt.getDisplayName(selector, info.getChannel());
+ bld.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, displayTitle);
+ String subtitle = getProgramName(info, /* flags= */ 0, programNameOrder);
+ bld.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, subtitle);
+
+ Bitmap bm = resolveAlbumArtBitmap(info.getMetadata(), imageResolver);
+ if (bm != null) bld.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bm);
+
+ bld.putRating(MediaMetadata.METADATA_KEY_USER_RATING, Rating.newHeartRating(isFavorite));
+
+ return bld.build();
+ }
+
+ /**
+ * Converts {@link ProgramInfo} to {@link MediaMetadata}.
+ *
+ * <p>This method is meant to be used for currently playing station in {@link MediaSession}.
+ *
+ * <ul>
+ * The following keys will be populated in the {@link MediaMetadata}:
+ * <li>{@link MediaMetadata#METADATA_KEY_DISPLAY_TITLE}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_TITLE}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_ARTIST}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_ALBUM}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_DISPLAY_SUBTITLE}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_ALBUM_ART}</li>
+ * <li>{@link MediaMetadata#METADATA_KEY_USER_RATING}</li>
+ * <ul/>
+ *
+ * @param info {@link ProgramInfo} to convert
+ * @param isFavorite {@code true}, if a given program is a favorite
* @param imageResolver metadata images resolver/cache
* @return {@link MediaMetadata} object
*/
@@ -158,16 +238,22 @@
}
bld.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE, subtitle);
}
- long albumArtId = RadioMetadataExt.getGlobalBitmapId(meta,
- RadioMetadata.METADATA_KEY_ART);
- if (albumArtId != 0 && imageResolver != null) {
- Bitmap bm = imageResolver.resolve(albumArtId);
- if (bm != null) bld.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bm);
- }
+
+ Bitmap bm = resolveAlbumArtBitmap(meta, imageResolver);
+ if (bm != null) bld.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bm);
}
bld.putRating(MediaMetadata.METADATA_KEY_USER_RATING, Rating.newHeartRating(isFavorite));
return bld.build();
}
+
+ private static Bitmap resolveAlbumArtBitmap(@NonNull RadioMetadata meta,
+ @NonNull ImageResolver imageResolver) {
+ long albumArtId = RadioMetadataExt.getGlobalBitmapId(meta, RadioMetadata.METADATA_KEY_ART);
+ if (albumArtId != 0 && imageResolver != null) {
+ return imageResolver.resolve(albumArtId);
+ }
+ return null;
+ }
}
diff --git a/car-qc-lib/res/layout/qc_row_view.xml b/car-qc-lib/res/layout/qc_row_view.xml
index be8c391..2d95888 100644
--- a/car-qc-lib/res/layout/qc_row_view.xml
+++ b/car-qc-lib/res/layout/qc_row_view.xml
@@ -69,7 +69,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
- app:constraint_referenced_ids="qc_icon"
app:barrierAllowsGoneWidgets="false"/>
<com.android.car.ui.uxr.DrawableStateTextView
diff --git a/tools/rro/generate-overlayable.py b/tools/rro/generate-overlayable.py
index 2f76a2d..b814118 100755
--- a/tools/rro/generate-overlayable.py
+++ b/tools/rro/generate-overlayable.py
@@ -49,10 +49,12 @@
optional_args.add_argument('-o', '--outputFile', default='', help='Output file path (absolute or relative to cwd). If empty, output to stdout')
required_args = parser.add_argument_group('required arguments')
required_args.add_argument('-n', '--targetName', help='Overlayable name for the overlay.', required=True)
- required_args.add_argument('-r', '--resourcePath', help='Path to resource directory (absolute or relative to cwd)', required=True)
+ required_args.add_argument('-r', '--resourcePath', help='Path to resource directory (absolute or relative to cwd)', required=True, action='append')
args = parser.parse_args()
- resources = get_all_resources(args.resourcePath, args.excludeFiles)
+ resources = set()
+ for path in args.resourcePath:
+ resources |= get_all_resources(path, args.excludeFiles)
generate_overlayable_file(resources, args.targetName, args.policyType, args.outputFile)
def generate_overlayable_file(resources, target_name, policy_type, output_file):
diff --git a/tools/rro/verify-overlayable.py b/tools/rro/verify-overlayable.py
index 2625310..ef99aa4 100755
--- a/tools/rro/verify-overlayable.py
+++ b/tools/rro/verify-overlayable.py
@@ -30,11 +30,13 @@
optional_args.add_argument('-e', '--excludeFiles', nargs='*', help='File paths (absolute or relative to cwd) that should be excluded when generating overlayable.xml')
optional_args.add_argument('-m', '--errorMessage', nargs='*', help='Custom error message if resources are added or removed.')
required_args = parser.add_argument_group('required arguments')
- required_args.add_argument('-r', '--resourcePath', help='Path to resource directory (absolute or relative to cwd)', required=True)
+ required_args.add_argument('-r', '--resourcePath', help='Path to resource directory (absolute or relative to cwd)', required=True, action='append')
required_args.add_argument('-o', '--overlayableFilePath', help='Filepath to overlayable.xml (absolute or relative to cwd).', required=True)
args = parser.parse_args()
- resources = get_all_resources(args.resourcePath, args.excludeFiles)
+ resources = set()
+ for path in args.resourcePath:
+ resources |= get_all_resources(path, args.excludeFiles)
old_mapping = get_resources_from_single_file(args.overlayableFilePath)
compare_resources(old_mapping, resources, args.overlayableFilePath, args.errorMessage)