Merge sc-v2-dev-plus-aosp-without-vendor@8084891

Bug: 214455710
Merged-In: I763f2164b320f45e5cafd84e971718079fb2b384
Change-Id: I047833628888634e24ac2738a459ad00aea75754
diff --git a/Android.bp b/Android.bp
index 152ef44..7b7fa5b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -28,10 +28,14 @@
     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 @@
       <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 @@
       <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 @@
 -->
 <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.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 @@
     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 @@
         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 @@
         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 @@
 
 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.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.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 @@
     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 @@
 
         localViewModel.getBrowsedMediaSource().observe(this, this::onMediaSourceChanged);
 
+        mMediaTrampoline = new MediaTrampolineHelper(this);
+
         mPlaybackFragment = new PlaybackFragment();
         mPlaybackFragment.setListener(mPlaybackFragmentListener);
 
@@ -172,9 +172,6 @@
                 .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 @@
         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 @@
                 // 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 @@
         }
 
         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.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.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 @@
     }
 
     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 @@
 
         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 @@
             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 @@
         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 @@
             @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 @@
         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 @@
             title = getAppBarDefaultTitle(mediaSource);
         }
 
-        mAppBarController.setTitle(title);
+        return title;
     }
 
     /**
@@ -647,9 +661,11 @@
         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 @@
 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);
+        if (Log.isLoggable(TAG, Log.INFO)) {
+            Log.i(TAG, "onCreate action: " + action + " component: " + componentName);
+        }
+
         MediaSource mediaSrc = null;
-
-        if (Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE.equals(action)) {
-            String componentName = intent.getStringExtra(Car.CAR_EXTRA_MEDIA_COMPONENT);
+        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 @@
             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.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 @@
     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 @@
 
         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 @@
 
         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 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 @@
  */
 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 @@
     }
 
     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.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 @@
             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 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 @@
                 .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 @@
      * @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 @@
     }
 
     /**
-     * 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 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 @@
         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 @@
         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();
+        }
     }
 
-    /** See {@link ToolbarController#canShowSearchResultItems}. */
-    public boolean canShowSearchResultsView() {
-        return mToolbarController.canShowSearchResultsView();
+    /** Proxies to {@link ToolbarController#setNavButtonMode(NavButtonMode)} */
+    public void setNavButtonMode(NavButtonMode mode) {
+        if (mode != mToolbarController.getNavButtonMode()) {
+            mToolbarController.setNavButtonMode(mode);
+            updateTabs();
+        }
     }
 
-    /** See {@link ToolbarController#setSearchResultsView}. */
-    public void setSearchResultsView(View view) {
-        mToolbarController.setSearchResultsView(view);
+    /** Proxies to {@link ToolbarController#getSearchCapabilities()} */
+    public SearchCapabilities getSearchCapabilities() {
+        return mToolbarController.getSearchCapabilities();
+    }
+
+    /** 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 @@
 
 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