summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2022-03-08 00:18:46 +0000
committerXin Li <delphij@google.com>2022-03-08 00:18:46 +0000
commitba1e50793ff86d4ae7a0f0f917f9080627811001 (patch)
tree79de42416ce3345daafa1ca9629d4ba4ba001a2c
parent33b71ac39b2ee63b75916eed8a7d8ddd7e2fbae3 (diff)
parent141b83d1a0e025e08982c94d708dddb0c3ec9f6c (diff)
downloadMedia-ba1e50793ff86d4ae7a0f0f917f9080627811001.tar.gz
Merge Android 12L
Bug: 222710654 Merged-In: I047833628888634e24ac2738a459ad00aea75754 Change-Id: I38929d8dcddbc5069653a263c333b787d501fdbb
-rw-r--r--Android.bp4
-rw-r--r--AndroidManifest.xml5
-rw-r--r--PREUPLOAD.cfg2
-rw-r--r--build.gradle76
-rw-r--r--res/layout/media_browse_grid_icons_item.xml1
-rw-r--r--res/layout/media_browse_grid_item.xml2
-rw-r--r--res/layout/media_browse_list_icons_item.xml1
-rw-r--r--res/layout/media_browse_list_item.xml3
-rw-r--r--res/layout/queue_list_item.xml3
-rw-r--r--res/values-az/strings.xml2
-rw-r--r--res/values-eu/strings.xml2
-rw-r--r--res/values-pa/strings.xml2
-rw-r--r--res/values-te/strings.xml2
-rw-r--r--res/values-zh-rCN/strings.xml2
-rw-r--r--res/values-zh-rHK/strings.xml2
-rw-r--r--res/values-zh-rTW/strings.xml2
-rw-r--r--res/values/dimens.xml2
-rw-r--r--res/values/overlayable.xml92
-rw-r--r--res/values/styles.xml2
-rw-r--r--res/values/themes.xml2
-rw-r--r--src/com/android/car/media/BrowseViewController.java19
-rw-r--r--src/com/android/car/media/MediaActivity.java78
-rw-r--r--src/com/android/car/media/MediaActivityController.java44
-rw-r--r--src/com/android/car/media/MediaDispatcherActivity.java64
-rw-r--r--src/com/android/car/media/PlaybackFragment.java10
-rw-r--r--src/com/android/car/media/browse/LimitedBrowseAdapter.java22
-rw-r--r--src/com/android/car/media/widgets/AppBarController.java158
-rw-r--r--src/com/android/car/media/widgets/TabBinder.java105
-rwxr-xr-xtools/generate-overlayable.sh4
29 files changed, 467 insertions, 246 deletions
diff --git a/Android.bp b/Android.bp
index 152ef44..7b7fa5b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,10 +28,14 @@ android_app {
required: ["allowed_privapp_com.android.car.media"],
certificate: "platform",
+ // Keep this commented out until enough apps stop requiring a system signature.
+ // certificate: ":com-android-car-apps-test",
privileged: true,
libs: ["android.car-system-stubs"],
+ min_sdk_version: "30", // Media requires apis that became public in R.
+ target_sdk_version: "31",
sdk_version: "system_current",
optimize: {
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 4d823c4..0cb777d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,7 +16,8 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.car.media">
+ package="com.android.car.media"
+ android:versionCode="10000" android:versionName="1.0.0">
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -41,8 +42,10 @@
android:name="android.car.application"
android:resource="@xml/automotive_app_desc" />
+ <!-- Private implementation, should never be exported. -->
<activity
android:name=".MediaActivity"
+ android:exported="false"
android:resizeableActivity="true"
android:windowSoftInputMode="stateHidden|adjustPan"
android:launchMode="singleTop">
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index e91b525..e60137c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,7 +1,7 @@
[Hook Scripts]
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py -f ${PREUPLOAD_FILES}
-overlayable_resource_hook = ${REPO_ROOT}/packages/apps/Car/tests/tools/rro/verify-overlayable.py -r res -e res/values/overlayable.xml res/xml/automotive_app_desc.xml -o res/values/overlayable.xml
+overlayable_resource_hook = ${REPO_ROOT}/packages/apps/Car/tests/tools/rro/verify-overlayable.py -r res -e res/values/overlayable.xml res/xml/automotive_app_desc.xml res/values/colors.xml res/values/dimens.xml res/color/progress_bar_thumb_inner_ring_color.xml res/color/progress_bar_thumb_outer_ring_color.xml -o res/values/overlayable.xml
[Builtin Hooks]
commit_msg_changeid_field = true
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..98b389f
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion gradle.ext.aaosLatestSDK
+ defaultConfig {
+ applicationId "com.android.car.media"
+ minSdkVersion 30 // Media requires apis that became public in R.
+ targetSdkVersion gradle.ext.aaosTargetSDK
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ lintOptions {
+ abortOnError false
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ }
+ }
+
+ signingConfigs {
+ debug {
+ storeFile file('../libs/certs/com_android_car_apps_test.jks')
+ storePassword 'carapps'
+ keyAlias 'carapps'
+ keyPassword 'carapps'
+ }
+ }
+}
+
+dependencies {
+ implementation "androidx.preference:preference:1.1.1"
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
+
+ def lifecycle_version = "2.2.0"
+ implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
+ // Not available in 2.3+
+ implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
+
+ implementation files(gradle.ext.lib_car_system_stubs)
+
+ implementation "androidx.media:media:1.4.1"
+
+ implementation project(":car-ui-lib")
+ implementation project(":car-apps-common")
+ implementation project(":car-media-common")
+ implementation project(":car-uxr-client-lib")
+}
diff --git a/res/layout/media_browse_grid_icons_item.xml b/res/layout/media_browse_grid_icons_item.xml
index 6e0887a..7a413d8 100644
--- a/res/layout/media_browse_grid_icons_item.xml
+++ b/res/layout/media_browse_grid_icons_item.xml
@@ -37,6 +37,7 @@
android:id="@+id/thumbnail"
android:layout_width="@dimen/media_browse_grid_icons_item_art_size"
android:layout_height="@dimen/media_browse_grid_icons_item_art_size"
+ style="@style/MediaIconContainerStyle"
android:scaleType="centerCrop"/>
</FrameLayout>
diff --git a/res/layout/media_browse_grid_item.xml b/res/layout/media_browse_grid_item.xml
index 229a674..002b076 100644
--- a/res/layout/media_browse_grid_item.xml
+++ b/res/layout/media_browse_grid_item.xml
@@ -19,6 +19,7 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_margin="@dimen/grid_item_spacing"
android:padding="@dimen/media_browse_grid_item_padding"
android:layout_marginBottom="@dimen/media_browse_grid_item_margin_bottom">
@@ -38,6 +39,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="centerCrop"
+ style="@style/MediaIconContainerStyle"
app:aspect_ratio="1"/>
</FrameLayout>
diff --git a/res/layout/media_browse_list_icons_item.xml b/res/layout/media_browse_list_icons_item.xml
index 9c8d972..a507f99 100644
--- a/res/layout/media_browse_list_icons_item.xml
+++ b/res/layout/media_browse_list_icons_item.xml
@@ -30,6 +30,7 @@
android:scaleType="centerCrop"
android:layout_marginBottom="@dimen/media_browse_list_item_thumbnail_margin_bottom"
android:layout_marginStart="@dimen/media_browse_list_icons_item_art_margin_start"
+ style="@style/MediaIconContainerStyle"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
diff --git a/res/layout/media_browse_list_item.xml b/res/layout/media_browse_list_item.xml
index 52c2d50..f0f21dc 100644
--- a/res/layout/media_browse_list_item.xml
+++ b/res/layout/media_browse_list_item.xml
@@ -28,8 +28,9 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:scaleType="centerCrop"
- app:layout_constraintStart_toStartOf="parent"
android:layout_marginBottom="@dimen/media_browse_list_item_thumbnail_margin_bottom"
+ style="@style/MediaIconContainerStyle"
+ app:layout_constraintStart_toStartOf="parent"
app:aspect_ratio="1"/>
<!-- This guideline is necessary because there are icons preceding the text which typically have
diff --git a/res/layout/queue_list_item.xml b/res/layout/queue_list_item.xml
index a77187c..718e0b0 100644
--- a/res/layout/queue_list_item.xml
+++ b/res/layout/queue_list_item.xml
@@ -32,7 +32,8 @@
android:id="@+id/thumbnail"
android:layout_width="@dimen/queue_list_item_thumbnail_size"
android:layout_height="@dimen/queue_list_item_thumbnail_size"
- android:scaleType="centerCrop"/>
+ android:scaleType="centerCrop"
+ style="@style/MediaIconContainerStyle"/>
</FrameLayout>
<Space
diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml
index 2d73a56..4ed151b 100644
--- a/res/values-az/strings.xml
+++ b/res/values-az/strings.xml
@@ -25,7 +25,7 @@
<string name="media_browse_more" msgid="6330295386693311592">"Daha çox…"</string>
<string name="media_app_title" msgid="94717597743776797">"Media"</string>
<string name="search_hint" msgid="5401750426238148416">"Mahnı, ifaçı və s. axtarın"</string>
- <string name="fragment_playback_title" msgid="5014481549024607614">"İndi oxudulur"</string>
+ <string name="fragment_playback_title" msgid="5014481549024607614">"İndi Efirdə"</string>
<string name="service_notification_title" msgid="8085444675783592744">"Mediaya qoşulur"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"Səs ayarları"</string>
<string name="menu_item_app_selector_title" msgid="4587248991114338595">"Tətbiqləri dəyişdirin"</string>
diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml
index 79bc4ea..aca4209 100644
--- a/res/values-eu/strings.xml
+++ b/res/values-eu/strings.xml
@@ -21,7 +21,7 @@
<string name="nothing_to_play" msgid="594633010485167765">"Hemen ez dago araka daitekeen multimedia-edukirik"</string>
<string name="cannot_connect_to_app" msgid="4732888036680095414">"Une honetan, <xliff:g id="ID_1">%s</xliff:g> aplikazioak ez du funtzionatzen."</string>
<string name="unknown_media_provider_name" msgid="4238216994694326667">"Ezezaguna"</string>
- <string name="unknown_error" msgid="6146463797752964372">"Arazo bat izan da"</string>
+ <string name="unknown_error" msgid="6146463797752964372">"Arazoren bat izan da"</string>
<string name="media_browse_more" msgid="6330295386693311592">"Gehiago…"</string>
<string name="media_app_title" msgid="94717597743776797">"Multimedia-edukia"</string>
<string name="search_hint" msgid="5401750426238148416">"Bilatu abestiak, artistak eta beste…"</string>
diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml
index 835e9a1..80105f6 100644
--- a/res/values-pa/strings.xml
+++ b/res/values-pa/strings.xml
@@ -24,7 +24,7 @@
<string name="unknown_error" msgid="6146463797752964372">"ਕੋਈ ਗੜਬੜ ਹੋਈ"</string>
<string name="media_browse_more" msgid="6330295386693311592">"ਹੋਰ…"</string>
<string name="media_app_title" msgid="94717597743776797">"ਮੀਡੀਆ"</string>
- <string name="search_hint" msgid="5401750426238148416">"ਗੀਤ, ਕਲਾਕਾਰ ਤੇ ਹੋਰ ਖੋਜੋ"</string>
+ <string name="search_hint" msgid="5401750426238148416">"ਗੀਤ, ਕਲਾਕਾਰ ਖੋਜੋ, ਹੋਰ"</string>
<string name="fragment_playback_title" msgid="5014481549024607614">"ਹੁਣੇ ਚੱਲ ਰਿਹਾ ਹੈ"</string>
<string name="service_notification_title" msgid="8085444675783592744">"ਮੀਡੀਆ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"ਧੁਨੀ ਸੈਟਿੰਗਾਂ"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 6297b12..14cd0f4 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -24,7 +24,7 @@
<string name="unknown_error" msgid="6146463797752964372">"ఏదో తప్పు జరిగింది"</string>
<string name="media_browse_more" msgid="6330295386693311592">"మరిన్ని…"</string>
<string name="media_app_title" msgid="94717597743776797">"మీడియా"</string>
- <string name="search_hint" msgid="5401750426238148416">"పాటలు, ఆర్టిస్ట్‌లు మొ. వెతకండి..."</string>
+ <string name="search_hint" msgid="5401750426238148416">"పాటలు, కళాకారులు, మరిన్నింటిని వెతకండి..."</string>
<string name="fragment_playback_title" msgid="5014481549024607614">"ప్రస్తుతం ప్లే అవుతున్నవి"</string>
<string name="service_notification_title" msgid="8085444675783592744">"మీడియాకు కనెక్ట్ చేస్తోంది"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"ధ్వని సెట్టింగ్‌లు"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index b928667..27f7af2 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -25,7 +25,7 @@
<string name="media_browse_more" msgid="6330295386693311592">"更多…"</string>
<string name="media_app_title" msgid="94717597743776797">"媒体"</string>
<string name="search_hint" msgid="5401750426238148416">"搜索歌曲、音乐人等…"</string>
- <string name="fragment_playback_title" msgid="5014481549024607614">"正在播放"</string>
+ <string name="fragment_playback_title" msgid="5014481549024607614">"现正播放"</string>
<string name="service_notification_title" msgid="8085444675783592744">"正在连接到媒体"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"声音设置"</string>
<string name="menu_item_app_selector_title" msgid="4587248991114338595">"切换应用"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 15e6544..6254b03 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -25,7 +25,7 @@
<string name="media_browse_more" msgid="6330295386693311592">"更多…"</string>
<string name="media_app_title" msgid="94717597743776797">"媒體"</string>
<string name="search_hint" msgid="5401750426238148416">"搜尋歌曲、演出者和更多內容…"</string>
- <string name="fragment_playback_title" msgid="5014481549024607614">"正在播放"</string>
+ <string name="fragment_playback_title" msgid="5014481549024607614">"現正播放"</string>
<string name="service_notification_title" msgid="8085444675783592744">"正在連接媒體"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"音效設定"</string>
<string name="menu_item_app_selector_title" msgid="4587248991114338595">"切換應用程式"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index a909a72..5b54540 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -25,7 +25,7 @@
<string name="media_browse_more" msgid="6330295386693311592">"更多…"</string>
<string name="media_app_title" msgid="94717597743776797">"媒體"</string>
<string name="search_hint" msgid="5401750426238148416">"搜尋歌曲、演出者和更多內容…"</string>
- <string name="fragment_playback_title" msgid="5014481549024607614">"正在播放"</string>
+ <string name="fragment_playback_title" msgid="5014481549024607614">"現正播放"</string>
<string name="service_notification_title" msgid="8085444675783592744">"正在連線到媒體"</string>
<string name="menu_item_sound_settings_title" msgid="58887078120809669">"音效設定"</string>
<string name="menu_item_app_selector_title" msgid="4587248991114338595">"切換應用程式"</string>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 84d326e..ebc0b8c 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -97,7 +97,7 @@
<!-- BrowseFragment.java -->
<!-- Spacer used between the app bar and the top of the browse list/grid -->
<dimen name="browse_spacer_height">@dimen/car_ui_padding_2</dimen>
- <dimen name="grid_item_spacing">@dimen/car_ui_padding_3</dimen>
+ <dimen name="grid_item_spacing">@dimen/car_ui_padding_1</dimen>
<!-- Space between title and subtitle on media browse list/grid -->
<dimen name="media_browse_subtitle_margin_top">@dimen/car_ui_padding_0</dimen>
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
index b7206d0..d02874e 100644
--- a/res/values/overlayable.xml
+++ b/res/values/overlayable.xml
@@ -33,108 +33,17 @@ REGENERATE USING packages/apps/Car/tests/tools/rro/generate-overlayable.py
<item type="bool" name="show_time_for_now_playing_queue_list_item"/>
<item type="bool" name="switch_to_playback_view_when_playable_item_is_clicked"/>
<item type="bool" name="use_media_source_color_for_progress_bar"/>
- <item type="color" name="album_art_background"/>
- <item type="color" name="appbar_view_icon_tint"/>
- <item type="color" name="appbar_view_settings_tint"/>
- <item type="color" name="browse_playback_bg_color"/>
- <item type="color" name="no_content_text_color"/>
- <item type="color" name="progress_bar_background"/>
- <item type="color" name="progress_bar_highlight"/>
- <item type="color" name="progress_bar_thumb_color"/>
- <item type="color" name="progress_bar_thumb_inner_ring_color"/>
- <item type="color" name="progress_bar_thumb_outer_ring_color"/>
- <item type="color" name="queue_playing_icon_color"/>
- <item type="color" name="search_bar_underline_color"/>
- <item type="color" name="search_hint_text_color"/>
- <item type="dimen" name="appbar_view_control_buttons_margin_end"/>
- <item type="dimen" name="appbar_view_control_buttons_spacing"/>
- <item type="dimen" name="appbar_view_nav_button_width"/>
- <item type="dimen" name="appbar_view_tabs_margin_end"/>
- <item type="dimen" name="appbar_view_tabs_margin_start"/>
- <item type="dimen" name="appbar_view_title_margin_start"/>
- <item type="dimen" name="apps_max_content_width"/>
- <item type="dimen" name="art_metadata_margin"/>
<item type="dimen" name="browse_fragment_bottom_padding"/>
<item type="dimen" name="browse_fragment_top_padding"/>
<item type="dimen" name="browse_fragment_top_padding_stacked"/>
- <item type="dimen" name="browse_playback_controls_height"/>
- <item type="dimen" name="browse_spacer_height"/>
- <item type="dimen" name="browse_state_error_margin_top"/>
- <item type="dimen" name="browse_tab_alpha_selected"/>
- <item type="dimen" name="browse_tab_alpha_unselected"/>
- <item type="dimen" name="browse_tab_padding"/>
- <item type="dimen" name="browse_tab_width"/>
- <item type="dimen" name="controls_margin"/>
- <item type="dimen" name="controls_spacing_inner"/>
- <item type="dimen" name="controls_spacing_outer"/>
- <item type="dimen" name="controls_tap_target_height"/>
- <item type="dimen" name="controls_tap_target_width"/>
- <item type="dimen" name="fragment_error_button_margin_bottom"/>
- <item type="dimen" name="fragment_error_button_margin_top"/>
- <item type="dimen" name="fragment_error_message_margin_x"/>
- <item type="dimen" name="fragment_metadata_margin_x"/>
- <item type="dimen" name="fragment_playback_queue_overlap_bottom"/>
- <item type="dimen" name="fragment_playback_queue_overlap_top"/>
- <item type="dimen" name="grid_item_spacing"/>
- <item type="dimen" name="media_activity_close_vector_x"/>
- <item type="dimen" name="media_activity_close_vector_y"/>
- <item type="dimen" name="media_activity_controls_container_padding"/>
- <item type="dimen" name="media_activity_controls_margin_start"/>
<item type="dimen" name="media_background_alpha"/>
- <item type="dimen" name="media_browse_grid_icons_item_art_size"/>
- <item type="dimen" name="media_browse_grid_item_margin_bottom"/>
- <item type="dimen" name="media_browse_grid_item_padding"/>
- <item type="dimen" name="media_browse_grid_item_text_margin_top"/>
- <item type="dimen" name="media_browse_header_item_height"/>
- <item type="dimen" name="media_browse_header_item_margin_x"/>
- <item type="dimen" name="media_browse_indicator_size"/>
<item type="dimen" name="media_browse_list_icons_item_art_margin_start"/>
- <item type="dimen" name="media_browse_list_icons_item_art_size"/>
<item type="dimen" name="media_browse_list_icons_item_text_margin_x"/>
- <item type="dimen" name="media_browse_list_item_arrow_size"/>
- <item type="dimen" name="media_browse_list_item_height"/>
- <item type="dimen" name="media_browse_list_item_icon_margin_start"/>
<item type="dimen" name="media_browse_list_item_text_margin_x"/>
- <item type="dimen" name="media_browse_list_item_thumbnail_margin_bottom"/>
- <item type="dimen" name="media_browse_list_item_thumbnail_size"/>
- <item type="dimen" name="media_browse_subtitle_margin_top"/>
- <item type="dimen" name="media_scrim_alpha"/>
- <item type="dimen" name="media_scrim_darkened_alpha"/>
- <item type="dimen" name="metadata_subtitles_margin"/>
- <item type="dimen" name="metadata_title_subtitle_margin"/>
<item type="dimen" name="minimized_control_bar_margin_bottom"/>
- <item type="dimen" name="missing_permission_icon_size"/>
- <item type="dimen" name="music_action_icon_inset"/>
- <item type="dimen" name="music_action_ripple_inset"/>
- <item type="dimen" name="playback_album_art_size"/>
- <item type="dimen" name="playback_background_blur_radius"/>
- <item type="dimen" name="playback_background_blur_scale"/>
- <item type="dimen" name="playback_background_raw_image_size"/>
- <item type="dimen" name="playback_queue_background_alpha"/>
- <item type="dimen" name="playback_queue_button_margin_end"/>
- <item type="dimen" name="playback_queue_list_padding_top"/>
- <item type="dimen" name="playback_seekbar_height"/>
<item type="dimen" name="playback_seekbar_margin_x"/>
- <item type="dimen" name="playback_seekbar_padding_x"/>
- <item type="dimen" name="playback_seekbar_thumb_height"/>
- <item type="dimen" name="playback_seekbar_thumb_inner_ring_inner_radius"/>
- <item type="dimen" name="playback_seekbar_thumb_inner_ring_thickness"/>
- <item type="dimen" name="playback_seekbar_thumb_offset"/>
- <item type="dimen" name="playback_seekbar_thumb_outer_ring_inner_radius"/>
- <item type="dimen" name="playback_seekbar_thumb_outer_ring_thickness"/>
- <item type="dimen" name="playback_seekbar_thumb_width"/>
- <item type="dimen" name="playback_seekbar_track_height"/>
- <item type="dimen" name="playback_title_margin_end"/>
- <item type="dimen" name="queue_button_background_size"/>
- <item type="dimen" name="queue_fading_edge_length"/>
- <item type="dimen" name="queue_list_item_height"/>
<item type="dimen" name="queue_list_item_padding_x"/>
- <item type="dimen" name="queue_list_item_spacer_width"/>
<item type="dimen" name="queue_list_item_thumbnail_container_width"/>
- <item type="dimen" name="queue_list_item_thumbnail_size"/>
- <item type="dimen" name="queue_list_item_title_time_margin"/>
- <item type="dimen" name="tab_view_icon_margin_bottom"/>
- <item type="dimen" name="tab_view_icon_size"/>
<item type="drawable" name="error_illustration"/>
<item type="drawable" name="ic_arrow_back"/>
<item type="drawable" name="ic_chevron_right"/>
@@ -251,6 +160,7 @@ REGENERATE USING packages/apps/Car/tests/tools/rro/generate-overlayable.py
<item type="style" name="BrowseListTitleStyle"/>
<item type="style" name="BrowseSubheaderStyle"/>
<item type="style" name="ErrorTextStyle"/>
+ <item type="style" name="MediaIconContainerStyle"/>
<item type="style" name="MetadataContainerStyle"/>
<item type="style" name="MetadataPlaybackSubtitleStyle"/>
<item type="style" name="MetadataPlaybackTitleStyle"/>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 6fdceae..202177e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -92,4 +92,6 @@
<style name="BrowseListItemRightArrowStyle">
<item name="android:src">@null</item>
</style>
+
+ <style name="MediaIconContainerStyle"/>
</resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index ac4d809..ce18182 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -15,7 +15,7 @@ limitations under the License.
-->
<resources>
<!-- The theme for the Media Center application. -->
- <style name="Theme.Media" parent="android:Theme.DeviceDefault.NoActionBar" >
+ <style name="Theme.Media" parent="Theme.CarUi" >
<item name="textAppearanceGridItem">@android:style/TextAppearance.DeviceDefault.Medium</item>
<item name="textAppearanceGridItemSecondary">@android:style/TextAppearance.DeviceDefault.Small</item>
<item name="android:splitMotionEvents">false</item>
diff --git a/src/com/android/car/media/BrowseViewController.java b/src/com/android/car/media/BrowseViewController.java
index c1869e4..db1cca3 100644
--- a/src/com/android/car/media/BrowseViewController.java
+++ b/src/com/android/car/media/BrowseViewController.java
@@ -32,20 +32,19 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;
-import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.car.apps.common.util.FutureData;
import com.android.car.apps.common.util.ViewUtils;
-import com.android.car.arch.common.FutureData;
import com.android.car.media.browse.BrowseAdapter;
import com.android.car.media.browse.LimitedBrowseAdapter;
-import com.android.car.media.common.GridSpacingItemDecoration;
import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.browse.MediaBrowserViewModelImpl;
import com.android.car.media.common.browse.MediaItemsRepository.MediaItemsLiveData;
import com.android.car.media.common.source.MediaSource;
import com.android.car.ui.FocusArea;
import com.android.car.ui.baselayout.Insets;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
import com.android.car.uxr.LifeCycleObserverUxrContentLimiter;
import com.android.car.uxr.UxrContentLimiterImpl;
@@ -69,7 +68,7 @@ public class BrowseViewController {
private final boolean mDisplayMediaItems;
private final LifeCycleObserverUxrContentLimiter mUxrContentLimiter;
private final View mContent;
- private final RecyclerView mBrowseList;
+ private final CarUiRecyclerView mBrowseList;
private final ImageView mErrorIcon;
private final TextView mMessage;
private final LimitedBrowseAdapter mLimitedBrowseAdapter;
@@ -184,12 +183,8 @@ public class BrowseViewController {
FragmentActivity activity = callbacks.getActivity();
mViewModel = ViewModelProviders.of(activity).get(MediaActivity.ViewModel.class);
- mBrowseList.addItemDecoration(new GridSpacingItemDecoration(
- activity.getResources().getDimensionPixelSize(R.dimen.grid_item_spacing)));
-
- GridLayoutManager manager = (GridLayoutManager) mBrowseList.getLayoutManager();
BrowseAdapter browseAdapter = new BrowseAdapter(mBrowseList.getContext());
- mLimitedBrowseAdapter = new LimitedBrowseAdapter(browseAdapter, manager,
+ mLimitedBrowseAdapter = new LimitedBrowseAdapter(mBrowseList, browseAdapter,
mBrowseAdapterObserver);
mBrowseList.setAdapter(mLimitedBrowseAdapter);
@@ -362,16 +357,16 @@ public class BrowseViewController {
int duration = mFadeDuration;
if (items == null) {
mMessage.setText(getErrorMessage());
- ViewUtils.hideViewAnimated(mBrowseList, duration);
+ ViewUtils.hideViewAnimated(mBrowseList.getView(), duration);
ViewUtils.showViewAnimated(mMessage, duration);
ViewUtils.showViewAnimated(mErrorIcon, duration);
} else if (items.isEmpty()) {
mMessage.setText(R.string.nothing_to_play);
- ViewUtils.hideViewAnimated(mBrowseList, duration);
+ ViewUtils.hideViewAnimated(mBrowseList.getView(), duration);
ViewUtils.hideViewAnimated(mErrorIcon, duration);
ViewUtils.showViewAnimated(mMessage, duration);
} else {
- ViewUtils.showViewAnimated(mBrowseList, duration);
+ ViewUtils.showViewAnimated(mBrowseList.getView(), duration);
ViewUtils.hideViewAnimated(mErrorIcon, duration);
ViewUtils.hideViewAnimated(mMessage, duration);
}
diff --git a/src/com/android/car/media/MediaActivity.java b/src/com/android/car/media/MediaActivity.java
index 21f6d46..b437a1a 100644
--- a/src/com/android/car/media/MediaActivity.java
+++ b/src/com/android/car/media/MediaActivity.java
@@ -17,8 +17,8 @@ package com.android.car.media;
import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE;
+import static com.android.car.apps.common.util.LiveDataFunctions.dataOf;
import static com.android.car.apps.common.util.VectorMath.EPSILON;
-import static com.android.car.arch.common.LiveDataFunctions.dataOf;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
@@ -27,6 +27,8 @@ import android.app.PendingIntent;
import android.car.Car;
import android.car.content.pm.CarPackageManager;
import android.car.drivingstate.CarUxRestrictions;
+import android.car.media.CarMediaIntents;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -51,15 +53,16 @@ import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModelProviders;
import com.android.car.apps.common.util.CarPackageManagerUtils;
+import com.android.car.apps.common.util.FutureData;
import com.android.car.apps.common.util.VectorMath;
import com.android.car.apps.common.util.ViewUtils;
-import com.android.car.arch.common.FutureData;
import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.MinimizedPlaybackControlBar;
import com.android.car.media.common.PlaybackErrorsHelper;
import com.android.car.media.common.browse.MediaItemsRepository;
import com.android.car.media.common.playback.PlaybackViewModel;
import com.android.car.media.common.source.MediaSource;
+import com.android.car.media.common.source.MediaTrampolineHelper;
import com.android.car.ui.AlertDialogBuilder;
import com.android.car.ui.utils.CarUxRestrictionsUtil;
@@ -105,17 +108,12 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
private float mCloseVectorY;
private float mCloseVectorNorm;
- private CarUxRestrictionsUtil mCarUxRestrictionsUtil;
- private CarUxRestrictions mActiveCarUxRestrictions;
- @CarUxRestrictions.CarUxRestrictionsInfo
- private int mRestrictions;
- private final CarUxRestrictionsUtil.OnUxRestrictionsChangedListener mListener =
- (carUxRestrictions) -> mActiveCarUxRestrictions = carUxRestrictions;
-
private PlaybackFragment.PlaybackFragmentListener mPlaybackFragmentListener =
() -> changeMode(Mode.BROWSING);
+ private MediaTrampolineHelper mMediaTrampoline;
+
/**
* Possible modes of the application UI
* Todo: refactor into non exclusive flags to allow concurrent modes (eg: play details & browse)
@@ -155,6 +153,8 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
localViewModel.getBrowsedMediaSource().observe(this, this::onMediaSourceChanged);
+ mMediaTrampoline = new MediaTrampolineHelper(this);
+
mPlaybackFragment = new PlaybackFragment();
mPlaybackFragment.setListener(mPlaybackFragmentListener);
@@ -172,9 +172,6 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
.replace(R.id.playback_container, mPlaybackFragment)
.commit();
- mMediaActivityController = new MediaActivityController(this, getMediaItemsRepository(),
- mCarPackageManager, mBrowseContainer);
-
playbackViewModel.getPlaybackController().observe(this,
playbackController -> {
if (playbackController != null) playbackController.prepare();
@@ -187,23 +184,60 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
mCar = Car.createCar(this);
mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
- mCarUxRestrictionsUtil = CarUxRestrictionsUtil.getInstance(this);
- mRestrictions = CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP;
- mCarUxRestrictionsUtil.register(mListener);
+ mMediaActivityController = new MediaActivityController(this, getMediaItemsRepository(),
+ mCarPackageManager, mBrowseContainer);
mPlaybackContainer.setOnTouchListener(new ClosePlaybackDetector(this));
}
@Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent); // getIntent() should always return the most recent
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onNewIntent: " + intent);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ Intent intent = getIntent();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onResume intent: " + intent);
+ }
+
+ if (intent != null) {
+ String compName = getIntent().getStringExtra(CarMediaIntents.EXTRA_MEDIA_COMPONENT);
+ ComponentName launchedSourceComp = (compName == null) ? null :
+ ComponentName.unflattenFromString(compName);
+
+ if (launchedSourceComp == null) {
+ // Might happen if there's no media source at all on the system as the
+ // MediaDispatcherActivity always specifies the component otherwise.
+ Log.w(TAG, "launchedSourceComp should almost never be null: " + compName);
+ }
+
+ mMediaTrampoline.setLaunchedMediaSource(launchedSourceComp);
+
+ // Mark the intent as consumed so that coming back from the media app selector doesn't
+ // set the source again.
+ setIntent(null);
+ }
+ }
+
+ @Override
protected void onDestroy() {
- mCarUxRestrictionsUtil.unregister(mListener);
mCar.disconnect();
mMediaActivityController.onDestroy();
super.onDestroy();
}
private boolean isUxRestricted() {
- return CarUxRestrictionsUtil.isRestricted(mRestrictions, mActiveCarUxRestrictions);
+ return CarUxRestrictionsUtil.isRestricted(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP,
+ CarUxRestrictionsUtil.getInstance(this).getCurrentRestrictions());
}
private void handlePlaybackState(PlaybackViewModel.PlaybackStateWrapper state,
@@ -349,12 +383,7 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
// state that can be displayed (and some send a displayable state after sending a
// non displayable one...).
changeModeInternal(Mode.BROWSING, false);
-
- // Always go through the trampoline activity to keep the dispatching logic there.
- startActivity(new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE));
}
- } else {
- startActivity(new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE));
}
}
@@ -588,6 +617,11 @@ public class MediaActivity extends FragmentActivity implements MediaActivityCont
}
void saveBrowsedMediaSource(MediaSource mediaSource) {
+ Resources res = getApplication().getResources();
+ if (MediaDispatcherActivity.isCustomMediaSource(res, mediaSource)) {
+ Log.i(TAG, "Ignoring custom media source: " + mediaSource);
+ return;
+ }
MediaSource oldSource = getMediaSourceValue();
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "MediaSource changed from " + oldSource + " to " + mediaSource);
diff --git a/src/com/android/car/media/MediaActivityController.java b/src/com/android/car/media/MediaActivityController.java
index 9a7d39b..a29b40d 100644
--- a/src/com/android/car/media/MediaActivityController.java
+++ b/src/com/android/car/media/MediaActivityController.java
@@ -37,9 +37,9 @@ import androidx.lifecycle.ViewModelProviders;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.car.apps.common.util.FutureData;
import com.android.car.apps.common.util.ViewUtils;
import com.android.car.apps.common.util.ViewUtils.ViewAnimEndListener;
-import com.android.car.arch.common.FutureData;
import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.browse.MediaBrowserViewModelImpl;
import com.android.car.media.common.browse.MediaItemsRepository;
@@ -50,7 +50,9 @@ import com.android.car.media.widgets.AppBarController;
import com.android.car.ui.FocusParkingView;
import com.android.car.ui.baselayout.Insets;
import com.android.car.ui.recyclerview.CarUiRecyclerView;
-import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.NavButtonMode;
+import com.android.car.ui.toolbar.SearchConfig;
+import com.android.car.ui.toolbar.SearchMode;
import java.util.ArrayList;
import java.util.Collection;
@@ -203,6 +205,10 @@ public class MediaActivityController extends ViewControllerBase {
}
private void onMediaBrowsingStateChanged(BrowsingState newBrowsingState) {
+ if (newBrowsingState == null) {
+ Log.e(TAG, "Null browsing state (no media source!)");
+ return;
+ }
switch (newBrowsingState.mConnectionStatus) {
case CONNECTING:
break;
@@ -255,7 +261,7 @@ public class MediaActivityController extends ViewControllerBase {
mAppBarController.setListener(mAppBarListener);
mAppBarController.setSearchQuery(mViewModel.getSearchQuery());
- if (mAppBarController.canShowSearchResultsView()) {
+ if (mAppBarController.getSearchCapabilities().canShowSearchResultsView()) {
// TODO(b/180441965) eliminate the need to create a different view and use
// mSearchResultsController.getContent() instead.
RecyclerView toolbarSearchResultsView = new RecyclerView(activity);
@@ -268,7 +274,9 @@ public class MediaActivityController extends ViewControllerBase {
toolbarSearchResultsView.setBackground(
activity.getDrawable(R.drawable.car_ui_ime_wide_screen_background));
- mAppBarController.setSearchResultsView(toolbarSearchResultsView);
+ mAppBarController.setSearchConfig(SearchConfig.builder()
+ .setSearchResultsView(toolbarSearchResultsView)
+ .build());
}
updateAppBar();
@@ -442,7 +450,12 @@ public class MediaActivityController extends ViewControllerBase {
CarUiRecyclerView carUiRecyclerView =
controller.getContent().findViewById(R.id.browse_list);
if (carUiRecyclerView != null && carUiRecyclerView instanceof LazyLayoutView
- && !carUiRecyclerView.hasFocus() && !carUiRecyclerView.isInTouchMode()) {
+ && !carUiRecyclerView.getView().hasFocus()
+ && !carUiRecyclerView.getView().isInTouchMode()) {
+ // Park the focus on the FocusParkingView to ensure that it can restore focus inside
+ // the LazyLayoutView successfully later.
+ mFpv.performAccessibilityAction(ACTION_FOCUS, null);
+
LazyLayoutView lazyLayoutView = (LazyLayoutView) carUiRecyclerView;
com.android.car.ui.utils.ViewUtils.initFocus(lazyLayoutView);
}
@@ -452,18 +465,18 @@ public class MediaActivityController extends ViewControllerBase {
@Nullable ViewAnimEndListener listener) {
CarUiRecyclerView carUiRecyclerView = content.findViewById(R.id.browse_list);
if (carUiRecyclerView != null && carUiRecyclerView instanceof LazyLayoutView
- && !carUiRecyclerView.isInTouchMode()) {
+ && !carUiRecyclerView.getView().isInTouchMode()) {
// If a CarUiRecyclerView is about to hide and it has focus, park the focus on the
// FocusParkingView before hiding the CarUiRecyclerView. Otherwise hiding the focused
// view will cause the Android framework to move focus to another view, causing visual
// jank.
- if (!show && carUiRecyclerView.hasFocus()) {
+ if (!show && carUiRecyclerView.getView().hasFocus()) {
mFpv.performAccessibilityAction(ACTION_FOCUS, null);
}
// If a new CarUiRecyclerView is about to show and there is no view focused or the
// FocusParkingView is focused, restore focus in the new CarUiRecyclerView.
if (show) {
- View focusedView = carUiRecyclerView.getRootView().findFocus();
+ View focusedView = carUiRecyclerView.getView().getRootView().findFocus();
if (focusedView == null || focusedView instanceof FocusParkingView) {
LazyLayoutView lazyLayoutView = (LazyLayoutView) carUiRecyclerView;
com.android.car.ui.utils.ViewUtils.initFocus(lazyLayoutView);
@@ -616,13 +629,14 @@ public class MediaActivityController extends ViewControllerBase {
updateAppBar();
}
- private void updateAppBarTitle() {
+ private CharSequence getAppBarTitle() {
boolean isStacked = !isAtTopStack();
final CharSequence title;
if (isStacked) {
// If not at top level, show the current item as title
- title = getCurrentMediaItem().getTitle();
+ MediaItemMetadata item = getCurrentMediaItem();
+ title = item != null ? item.getTitle() : "";
} else if (mTopItems == null) {
// If still loading the tabs, force to show an empty bar.
title = "";
@@ -635,7 +649,7 @@ public class MediaActivityController extends ViewControllerBase {
title = getAppBarDefaultTitle(mediaSource);
}
- mAppBarController.setTitle(title);
+ return title;
}
/**
@@ -647,9 +661,11 @@ public class MediaActivityController extends ViewControllerBase {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "App bar is in stacked state: " + isStacked);
}
- Toolbar.State unstackedState = isSearching ? Toolbar.State.SEARCH : Toolbar.State.HOME;
- updateAppBarTitle();
- mAppBarController.setState(isStacked ? Toolbar.State.SUBPAGE : unstackedState);
+
+ mAppBarController.setSearchMode(isSearching ? SearchMode.SEARCH : SearchMode.DISABLED);
+ mAppBarController.setNavButtonMode(isStacked || isSearching
+ ? NavButtonMode.BACK : NavButtonMode.DISABLED);
+ mAppBarController.setTitle(!isSearching ? getAppBarTitle() : null);
mAppBarController.showSearchIfSupported(!isSearching || isStacked);
}
diff --git a/src/com/android/car/media/MediaDispatcherActivity.java b/src/com/android/car/media/MediaDispatcherActivity.java
index 973937b..282cfa5 100644
--- a/src/com/android/car/media/MediaDispatcherActivity.java
+++ b/src/com/android/car/media/MediaDispatcherActivity.java
@@ -1,13 +1,17 @@
package com.android.car.media;
+import static android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT;
import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE;
import android.car.Car;
+import android.car.media.CarMediaIntents;
import android.content.ComponentName;
import android.content.Intent;
+import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;
+import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import com.android.car.media.common.source.MediaSource;
@@ -25,50 +29,60 @@ import java.util.Set;
public class MediaDispatcherActivity extends FragmentActivity {
private static final String TAG = "MediaDispatcherActivity";
+ private static Set<String> sCustomMediaComponents = null;
- private final Set<String> mCustomMediaComponents = new HashSet<>();
+ static boolean isCustomMediaSource(Resources res, @Nullable MediaSource source) {
+ if (sCustomMediaComponents == null) {
+ sCustomMediaComponents = new HashSet<>();
+ sCustomMediaComponents.addAll(
+ Arrays.asList(res.getStringArray(R.array.custom_media_packages)));
+ }
+
+ return (source != null)
+ && sCustomMediaComponents.contains(
+ source.getBrowseServiceComponentName().flattenToString());
+ }
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mCustomMediaComponents.addAll(
- Arrays.asList(getResources().getStringArray(R.array.custom_media_packages)));
-
Intent intent = getIntent();
- String action = intent != null ? intent.getAction() : null;
+ String action = null;
+ String componentName = null;
+ if (intent != null) {
+ action = intent.getAction();
+ componentName = intent.getStringExtra(EXTRA_MEDIA_COMPONENT);
+ }
- MediaSourceViewModel mediaSrcVM = MediaSourceViewModel.get(getApplication(),
- MEDIA_SOURCE_MODE_BROWSE);
- MediaSource mediaSrc = null;
+ if (Log.isLoggable(TAG, Log.INFO)) {
+ Log.i(TAG, "onCreate action: " + action + " component: " + componentName);
+ }
- if (Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE.equals(action)) {
- String componentName = intent.getStringExtra(Car.CAR_EXTRA_MEDIA_COMPONENT);
+ MediaSource mediaSrc = null;
+ if (CarMediaIntents.ACTION_MEDIA_TEMPLATE.equals(action)) {
if (componentName != null) {
- ComponentName component = ComponentName.unflattenFromString(componentName);
- mediaSrc = MediaSource.create(this, component);
- if (mediaSrc != null) {
- mediaSrcVM.setPrimaryMediaSource(mediaSrc, MEDIA_SOURCE_MODE_BROWSE);
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "onCreate componentName : " + componentName);
- }
+ ComponentName mediaSrcComp = ComponentName.unflattenFromString(componentName);
+ if (mediaSrcComp != null) {
+ mediaSrc = MediaSource.create(this, mediaSrcComp);
}
}
}
+ // Retrieve the current source if none was set. However, do NOT set it and rely on setting
+ // the EXTRA_MEDIA_COMPONENT on the intent launched below. This avoids source notifications
+ // as well as extra trips back here, all of which would be useless.
if (mediaSrc == null) {
+ MediaSourceViewModel mediaSrcVM = MediaSourceViewModel.get(getApplication(),
+ MEDIA_SOURCE_MODE_BROWSE);
mediaSrc = mediaSrcVM.getPrimaryMediaSource().getValue();
}
Intent newIntent = null;
- if (mediaSrc != null
- && mCustomMediaComponents.contains(
- mediaSrc.getBrowseServiceComponentName().flattenToString())) {
+ if ((mediaSrc != null) && isCustomMediaSource(getResources(), mediaSrc)) {
// Launch custom app (e.g. Radio)
String srcPackage = mediaSrc.getPackageName();
newIntent = getPackageManager().getLaunchIntentForPackage(srcPackage);
- newIntent.putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT,
- mediaSrc.getBrowseServiceComponentName().flattenToString());
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Getting launch intent for package : " + srcPackage + (newIntent != null
? " succeeded" : " failed"));
@@ -79,6 +93,12 @@ public class MediaDispatcherActivity extends FragmentActivity {
newIntent = new Intent(this, MediaActivity.class);
}
+ // Add the selected media source to the intent so the launched activity gets it right away
+ if (mediaSrc != null) {
+ newIntent.putExtra(EXTRA_MEDIA_COMPONENT,
+ mediaSrc.getBrowseServiceComponentName().flattenToString());
+ }
+
newIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(newIntent);
finish();
diff --git a/src/com/android/car/media/PlaybackFragment.java b/src/com/android/car/media/PlaybackFragment.java
index cbf1ade..fa3ab7a 100644
--- a/src/com/android/car/media/PlaybackFragment.java
+++ b/src/com/android/car/media/PlaybackFragment.java
@@ -55,10 +55,11 @@ import com.android.car.media.common.playback.PlaybackViewModel;
import com.android.car.media.common.source.MediaSourceViewModel;
import com.android.car.media.widgets.AppBarController;
import com.android.car.ui.core.CarUi;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
import com.android.car.ui.recyclerview.ContentLimiting;
import com.android.car.ui.recyclerview.ScrollingLimitedViewHolder;
import com.android.car.ui.toolbar.MenuItem;
-import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.NavButtonMode;
import com.android.car.ui.toolbar.ToolbarController;
import com.android.car.ui.utils.DirectManipulationHelper;
import com.android.car.uxr.LifeCycleObserverUxrContentLimiter;
@@ -86,7 +87,7 @@ public class PlaybackFragment extends Fragment {
private View mControlBarScrim;
private PlaybackControlsActionBar mPlaybackControls;
private QueueItemsAdapter mQueueAdapter;
- private RecyclerView mQueue;
+ private CarUiRecyclerView mQueue;
private ViewGroup mSeekBarContainer;
private SeekBar mSeekBar;
private List<View> mViewsToHideForCustomActions;
@@ -483,8 +484,7 @@ public class PlaybackFragment extends Fragment {
mAppBarController.setTitle(R.string.fragment_playback_title);
mAppBarController.setBackgroundShown(false);
- mAppBarController.setNavButtonMode(Toolbar.NavButtonMode.DOWN);
- mAppBarController.setState(Toolbar.State.SUBPAGE);
+ mAppBarController.setNavButtonMode(NavButtonMode.DOWN);
// Update toolbar's logo
MediaSourceViewModel mediaSourceViewModel = getMediaSourceViewModel();
@@ -623,6 +623,8 @@ public class PlaybackFragment extends Fragment {
int decorationHeight = getResources().getDimensionPixelSize(
R.dimen.playback_queue_list_padding_top);
+ // TODO (b/206038962): addItemDecoration is not supported anymore. Find another way to
+ // support this.
// Put the decoration above the first item.
int decorationPosition = 0;
mQueue.addItemDecoration(new QueueTopItemDecoration(decorationHeight, decorationPosition));
diff --git a/src/com/android/car/media/browse/LimitedBrowseAdapter.java b/src/com/android/car/media/browse/LimitedBrowseAdapter.java
index 6d3c6a7..ee0a7f7 100644
--- a/src/com/android/car/media/browse/LimitedBrowseAdapter.java
+++ b/src/com/android/car/media/browse/LimitedBrowseAdapter.java
@@ -22,6 +22,8 @@ import androidx.recyclerview.widget.RecyclerView;
import com.android.car.media.R;
import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.ui.recyclerview.CarUiGridLayoutStyle;
+import com.android.car.ui.recyclerview.CarUiRecyclerView;
import com.android.car.ui.recyclerview.DelegatingContentLimitingAdapter;
import java.util.List;
@@ -31,21 +33,23 @@ import java.util.List;
*/
public class LimitedBrowseAdapter extends DelegatingContentLimitingAdapter<BrowseViewHolder> {
+ private final CarUiRecyclerView mRecyclerView;
private final BrowseAdapter mBrowseAdapter;
- private final GridLayoutManager mLayoutManager;
private final int mMaxSpanSize;
@Nullable private String mAnchorId;
- public LimitedBrowseAdapter(BrowseAdapter browseAdapter, GridLayoutManager manager,
+ public LimitedBrowseAdapter(CarUiRecyclerView recyclerView, BrowseAdapter browseAdapter,
BrowseAdapter.Observer browseAdapterObserver) {
super(browseAdapter, R.id.browse_list_uxr_config);
+ mRecyclerView = recyclerView;
mBrowseAdapter = browseAdapter;
- mLayoutManager = manager;
- mMaxSpanSize = manager.getSpanCount();
- mLayoutManager.setSpanSizeLookup(mSpanSizeLookup);
+ CarUiGridLayoutStyle layoutStyle = (CarUiGridLayoutStyle) mRecyclerView.getLayoutStyle();
+ mMaxSpanSize = layoutStyle.getSpanCount();
+ layoutStyle.setSpanSizeLookup(mSpanSizeLookup);
+ mRecyclerView.setLayoutStyle(layoutStyle);
mBrowseAdapter.registerObserver(browseAdapterObserver);
mBrowseAdapter.setOnListChangedListener(((previousList, currentList) -> {
updateUnderlyingDataChanged(currentList.size(), validateAnchor());
@@ -119,17 +123,17 @@ public class LimitedBrowseAdapter extends DelegatingContentLimitingAdapter<Brows
}
private int getFirstVisibleItemPosition() {
- int firstItem = mLayoutManager.findFirstCompletelyVisibleItemPosition();
+ int firstItem = mRecyclerView.findFirstCompletelyVisibleItemPosition();
if (firstItem == RecyclerView.NO_POSITION) {
- firstItem = mLayoutManager.findFirstVisibleItemPosition();
+ firstItem = mRecyclerView.findFirstVisibleItemPosition();
}
return firstItem;
}
private int getLastVisibleItemPosition() {
- int lastItem = mLayoutManager.findLastCompletelyVisibleItemPosition();
+ int lastItem = mRecyclerView.findLastCompletelyVisibleItemPosition();
if (lastItem == RecyclerView.NO_POSITION) {
- lastItem = mLayoutManager.findLastVisibleItemPosition();
+ lastItem = mRecyclerView.findLastVisibleItemPosition();
}
return lastItem;
}
diff --git a/src/com/android/car/media/widgets/AppBarController.java b/src/com/android/car/media/widgets/AppBarController.java
index 962f2ac..38eccdf 100644
--- a/src/com/android/car/media/widgets/AppBarController.java
+++ b/src/com/android/car/media/widgets/AppBarController.java
@@ -4,27 +4,32 @@ import android.car.drivingstate.CarUxRestrictions;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
-import android.view.View;
+import android.util.Size;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.car.media.MediaAppConfig;
import com.android.car.media.R;
import com.android.car.media.common.MediaItemMetadata;
import com.android.car.media.common.source.MediaSource;
import com.android.car.ui.toolbar.MenuItem;
-import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.NavButtonMode;
+import com.android.car.ui.toolbar.SearchCapabilities;
+import com.android.car.ui.toolbar.SearchConfig;
+import com.android.car.ui.toolbar.SearchMode;
import com.android.car.ui.toolbar.ToolbarController;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
- * Media template application bar. The callers should set properties via the public methods (e.g.,
- * {@link #setItems}, {@link #setTitle}, {@link #setHasSettings}), and set the visibility of the
- * views via {@link #setState}. A detailed explanation of all possible states of this application
- * bar can be seen at {@link Toolbar.State}.
+ * Media template application bar. This class wraps a {@link ToolbarController} and
+ * adds media-specific methods to it like {@link #setItems} and {@link #setSearchSupported}.
*/
public class AppBarController {
@@ -32,23 +37,25 @@ public class AppBarController {
CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP;
private static final int MEDIA_UX_RESTRICTION_NONE = CarUxRestrictions.UX_RESTRICTIONS_BASELINE;
- private int mMaxTabs;
+ private final int mMaxTabs;
+ private final ArrayList<TabBinder<MediaItemMetadata.ArtworkRef>> mTabs = new ArrayList<>();
private final ToolbarController mToolbarController;
+ private final Context mApplicationContext;
private final boolean mUseSourceLogoForAppSelector;
+ private final MenuItem mSearch;
+ private final MenuItem mSettings;
+ private final MenuItem mEqualizer;
+ private final MenuItem mAppSelector;
+
@NonNull
private AppBarListener mListener = new AppBarListener();
- private MenuItem mSearch;
- private MenuItem mSettings;
- private MenuItem mEqualizer;
- private MenuItem mAppSelector;
-
private boolean mSearchSupported;
private boolean mShowSearchIfSupported;
private String mSearchQuery;
-
- private Intent mAppSelectorIntent;
+ private int mSelectedTab = -1;
+ private Drawable mLogo;
/**
* Application bar listener
@@ -82,20 +89,19 @@ public class AppBarController {
public AppBarController(Context context, ToolbarController controller) {
mToolbarController = controller;
+ mApplicationContext = context.getApplicationContext();
mMaxTabs = context.getResources().getInteger(R.integer.max_tabs);
mUseSourceLogoForAppSelector =
context.getResources().getBoolean(R.bool.use_media_source_logo_for_app_selector);
- mAppSelectorIntent = MediaSource.getSourceSelectorIntent(context, false);
+ Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(context, false);
- mToolbarController.registerOnTabSelectedListener(tab ->
- mListener.onTabSelected(((MediaItemTab) tab).getItem()));
- mToolbarController.registerOnSearchListener(query -> {
+ mToolbarController.registerSearchListener(query -> {
mSearchQuery = query;
mListener.onSearch(query);
});
- mToolbarController.registerOnSearchCompletedListener(
+ mToolbarController.registerSearchCompletedListener(
() -> mListener.onSearch(mSearchQuery));
mSearch = MenuItem.builder(context)
.setToSearch()
@@ -116,19 +122,19 @@ public class AppBarController {
.setTinted(!mUseSourceLogoForAppSelector)
.setIcon(mUseSourceLogoForAppSelector
? null : context.getDrawable(R.drawable.ic_app_switch))
- .setOnClickListener(m -> context.startActivity(mAppSelectorIntent))
+ .setOnClickListener(m -> context.startActivity(appSelectorIntent))
.build();
mToolbarController.setMenuItems(
Arrays.asList(mSearch, mEqualizer, mSettings, mAppSelector));
- setAppLauncherSupported(mAppSelectorIntent != null);
+ mAppSelector.setVisible(appSelectorIntent != null);
}
/**
* Sets a listener of this application bar events. In order to avoid memory leaks, consumers
* must reset this reference by setting the listener to null.
*/
- public void setListener(AppBarListener listener) {
+ public void setListener(@NonNull AppBarListener listener) {
mListener = listener;
}
@@ -138,18 +144,45 @@ public class AppBarController {
* @param items list of tabs to show, or null if no tabs should be shown.
*/
public void setItems(@Nullable List<MediaItemMetadata> items) {
- mToolbarController.clearAllTabs();
+ if (items == null) {
+ items = Collections.emptyList();
+ }
- if (items != null && !items.isEmpty()) {
- int count = 0;
- for (MediaItemMetadata item : items) {
- mToolbarController.addTab(new MediaItemTab(item));
+ for (TabBinder<MediaItemMetadata.ArtworkRef> tabBinder : mTabs) {
+ tabBinder.setUpdateListener(null);
+ tabBinder.setImage(mApplicationContext, null);
+ }
- count++;
- if (count >= mMaxTabs) {
- break;
- }
- }
+ mTabs.clear();
+
+ Size maxArtSize = MediaAppConfig.getMediaItemsBitmapMaxSize(mApplicationContext);
+ for (MediaItemMetadata item : items.subList(0, Math.min(items.size(), mMaxTabs))) {
+ TabBinder<MediaItemMetadata.ArtworkRef> newTab = new TabBinder<>(
+ mApplicationContext,
+ maxArtSize,
+ item,
+ item2 -> {
+ mSelectedTab = mTabs.indexOf(item2);
+ mListener.onTabSelected(item2.getMediaItemMetadata());
+ });
+ newTab.setImage(mApplicationContext, item.getArtworkKey());
+ mTabs.add(newTab);
+ }
+ mSelectedTab = mTabs.isEmpty() ? -1 : 0;
+ for (TabBinder<MediaItemMetadata.ArtworkRef> tabBinder : mTabs) {
+ tabBinder.setUpdateListener(x -> updateTabs());
+ }
+ updateTabs();
+ }
+
+ private void updateTabs() {
+ if (mToolbarController.getNavButtonMode() != NavButtonMode.DISABLED) {
+ mToolbarController.setTabs(Collections.emptyList());
+ } else {
+ mToolbarController.setTabs(mTabs.stream()
+ .map(TabBinder::getToolbarTab)
+ .collect(Collectors.toList()),
+ mSelectedTab);
}
}
@@ -189,21 +222,14 @@ public class AppBarController {
}
/**
- * Sets whether launching app selector is supported
- */
- private void setAppLauncherSupported(boolean supported) {
- mAppSelector.setVisible(supported);
- }
-
- /**
* Updates the currently active item
*/
public void setActiveItem(MediaItemMetadata item) {
- for (int i = 0; i < mToolbarController.getTabCount(); i++) {
- MediaItemTab mediaItemTab = (MediaItemTab) mToolbarController.getTab(i);
+ for (int i = 0; i < mTabs.size(); i++) {
+ MediaItemMetadata mediaItemMetadata = mTabs.get(i).getMediaItemMetadata();
boolean match = item != null && Objects.equals(
item.getId(),
- mediaItemTab.getItem().getId());
+ mediaItemMetadata.getId());
if (match) {
mToolbarController.selectTab(i);
return;
@@ -216,10 +242,19 @@ public class AppBarController {
}
public void setLogo(Drawable drawable) {
- if (mUseSourceLogoForAppSelector) {
- mAppSelector.setIcon(drawable);
+ mLogo = drawable;
+ updateLogo();
+ }
+
+ private void updateLogo() {
+ if (mToolbarController.getSearchMode() == SearchMode.DISABLED) {
+ if (mUseSourceLogoForAppSelector) {
+ mAppSelector.setIcon(mLogo);
+ } else {
+ mToolbarController.setLogo(mLogo);
+ }
} else {
- mToolbarController.setLogo(drawable);
+ mToolbarController.setLogo(null);
}
}
@@ -235,10 +270,6 @@ public class AppBarController {
mToolbarController.setTitle(title);
}
- public void setState(Toolbar.State state) {
- mToolbarController.setState(state);
- }
-
public void setMenuItems(List<MenuItem> items) {
mToolbarController.setMenuItems(items);
}
@@ -247,17 +278,30 @@ public class AppBarController {
mToolbarController.setBackgroundShown(shown);
}
- public void setNavButtonMode(Toolbar.NavButtonMode mode) {
- mToolbarController.setNavButtonMode(mode);
+ /** Proxies to {@link ToolbarController#setSearchMode(SearchMode)} */
+ public void setSearchMode(SearchMode mode) {
+ if (mToolbarController.getSearchMode() != mode) {
+ mToolbarController.setSearchMode(mode);
+ updateTabs();
+ updateLogo();
+ }
+ }
+
+ /** Proxies to {@link ToolbarController#setNavButtonMode(NavButtonMode)} */
+ public void setNavButtonMode(NavButtonMode mode) {
+ if (mode != mToolbarController.getNavButtonMode()) {
+ mToolbarController.setNavButtonMode(mode);
+ updateTabs();
+ }
}
- /** See {@link ToolbarController#canShowSearchResultItems}. */
- public boolean canShowSearchResultsView() {
- return mToolbarController.canShowSearchResultsView();
+ /** Proxies to {@link ToolbarController#getSearchCapabilities()} */
+ public SearchCapabilities getSearchCapabilities() {
+ return mToolbarController.getSearchCapabilities();
}
- /** See {@link ToolbarController#setSearchResultsView}. */
- public void setSearchResultsView(View view) {
- mToolbarController.setSearchResultsView(view);
+ /** Proxies to {@link ToolbarController#setSearchConfig(SearchConfig)} */
+ public void setSearchConfig(SearchConfig searchConfig) {
+ mToolbarController.setSearchConfig(searchConfig);
}
}
diff --git a/src/com/android/car/media/widgets/TabBinder.java b/src/com/android/car/media/widgets/TabBinder.java
new file mode 100644
index 0000000..ad728f6
--- /dev/null
+++ b/src/com/android/car/media/widgets/TabBinder.java
@@ -0,0 +1,105 @@
+/*
+ * 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.car.media.widgets;
+
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Size;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.apps.common.CommonFlags;
+import com.android.car.apps.common.imaging.ImageBinder;
+import com.android.car.media.R;
+import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.ui.toolbar.Tab;
+
+import java.util.function.Consumer;
+
+/**
+ * A {@link ImageBinder} that exposes a downloads images from a {@link MediaItemMetadata}
+ * into a toolbar tab.
+ *
+ * The resulting tab can be retrieved from the {@link #getToolbarTab} method.
+ * There are two listeners that must be supplied in the constructor: a selected
+ * listener and an update listener. The selected listener is called when the
+ * tab is selected. The update listener is called when the tab has changed in
+ * some way, and the updated version needs to be retrieved from {@link #getToolbarTab}
+ * again.
+ *
+ * @param <T> The type of {@link ImageBinder.ImageRef} to use.
+ */
+public class TabBinder<T extends ImageBinder.ImageRef> extends ImageBinder<T> {
+ private final MediaItemMetadata mMediaItemMetadata;
+ private final Context mContext;
+
+ private Consumer<TabBinder<T>> mUpdateListener;
+ private Tab mTab;
+
+ protected TabBinder(
+ @NonNull Context context,
+ @NonNull Size maxImageSize,
+ @NonNull MediaItemMetadata item,
+ @NonNull Consumer<TabBinder<T>> selectedListener) {
+ super(PlaceholderType.NONE, maxImageSize);
+ mContext = context;
+ mMediaItemMetadata = item;
+ CharSequence title = mMediaItemMetadata.getTitle();
+ mTab = Tab.builder()
+ .setText(title == null ? null : title.toString())
+ .setSelectedListener(tab -> selectedListener.accept(this))
+ .build();
+ }
+
+ @Override
+ protected void setDrawable(@Nullable Drawable drawable) {
+ boolean shouldUseToolbarTint = true;
+ CommonFlags flags = CommonFlags.getInstance(mContext);
+ if (flags.shouldFlagImproperImageRefs()) {
+ if (drawable instanceof BitmapDrawable) {
+ int tint = mContext.getColor(
+ R.color.improper_image_refs_tint_color);
+ drawable.setColorFilter(new PorterDuffColorFilter(tint, PorterDuff.Mode.SRC_ATOP));
+ shouldUseToolbarTint = false;
+ }
+ }
+
+ mTab = mTab.copy()
+ .setIcon(drawable)
+ .setTinted(shouldUseToolbarTint)
+ .build();
+ if (mUpdateListener != null) {
+ mUpdateListener.accept(this);
+ }
+ }
+
+ public void setUpdateListener(Consumer<TabBinder<T>> updateListener) {
+ mUpdateListener = updateListener;
+ }
+
+ public MediaItemMetadata getMediaItemMetadata() {
+ return mMediaItemMetadata;
+ }
+
+ public Tab getToolbarTab() {
+ return mTab;
+ }
+}
diff --git a/tools/generate-overlayable.sh b/tools/generate-overlayable.sh
index 833d9f5..b6fe78a 100755
--- a/tools/generate-overlayable.sh
+++ b/tools/generate-overlayable.sh
@@ -22,8 +22,8 @@ fi
PROJECT_TOP=$ANDROID_BUILD_TOP/packages/apps/Car/Media
-python $ANDROID_BUILD_TOP/packages/apps/Car/tests/tools/rro/generate-overlayable.py \
+python3 $ANDROID_BUILD_TOP/packages/apps/Car/tests/tools/rro/generate-overlayable.py \
-n CarMediaApp \
-r $PROJECT_TOP/res \
- -e $PROJECT_TOP/res/values/overlayable.xml $PROJECT_TOP/res/xml/automotive_app_desc.xml \
+ -e $PROJECT_TOP/res/values/overlayable.xml $PROJECT_TOP/res/xml/automotive_app_desc.xml $PROJECT_TOP/res/values/colors.xml $PROJECT_TOP/res/values/dimens.xml $PROJECT_TOP/res/color/progress_bar_thumb_inner_ring_color.xml $PROJECT_TOP/res/color/progress_bar_thumb_outer_ring_color.xml \
-o $PROJECT_TOP/res/values/overlayable.xml \ No newline at end of file