Merge "Remove [email protected] from app/OWNERS" into main
diff --git a/OWNERS b/OWNERS
index d363985..b022f36 100644
--- a/OWNERS
+++ b/OWNERS
@@ -7,4 +7,3 @@
 # Secondary
 [email protected]
 [email protected]
[email protected]
\ No newline at end of file
diff --git a/app/Android.bp b/app/Android.bp
index 159e5a0..8537aca 100644
--- a/app/Android.bp
+++ b/app/Android.bp
@@ -18,11 +18,11 @@
 }
 
 genrule {
-  name: "statslog-carlauncher-java-gen",
-  tools: ["stats-log-api-gen"],
-  cmd: "$(location stats-log-api-gen) --java $(out) --module carlauncher"
-      + " --javaPackage com.android.car.carlauncher --javaClass CarLauncherStatsLog",
-  out: ["com/android/car/carlauncher/CarLauncherStatsLog.java"],
+    name: "statslog-carlauncher-java-gen",
+    tools: ["stats-log-api-gen"],
+    cmd: "$(location stats-log-api-gen) --java $(out) --module carlauncher" +
+        " --javaPackage com.android.car.carlauncher --javaClass CarLauncherStatsLog",
+    out: ["com/android/car/carlauncher/CarLauncherStatsLog.java"],
 }
 
 carlauncher_srcs = [
@@ -51,11 +51,14 @@
         "CarAppGrid-lib",
         "SystemUISharedLib",
         "android.car.cluster.navigation",
+        "car-resource-common",
     ],
 
     libs: ["android.car"],
 
     manifest: "AndroidManifest.xml",
+    // TODO(b/319708040): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 android_app {
@@ -90,11 +93,14 @@
     dex_preopt: {
         enabled: false,
     },
+    // TODO(b/319708040): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 aconfig_declarations {
     name: "car_launcher_flags",
     package: "com.android.car.carlauncher",
+    container: "system",
     srcs: ["car_launcher_flags.aconfig"],
 }
 
diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml
index 0acb74b..50f4d47 100644
--- a/app/AndroidManifest.xml
+++ b/app/AndroidManifest.xml
@@ -75,6 +75,8 @@
     <uses-permission android:name="android.car.permission.CONTROL_CAR_CLIMATE"/>
     <!-- Permission to read navigation state -->
     <uses-permission android:name="android.car.permission.CAR_MONITOR_CLUSTER_NAVIGATION_STATE"/>
+    <!-- Permission to read notifications -->
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS"/>
 
     <!-- To connect to media browser services in other apps, media browser clients
     that target Android 11 need to add the following in their manifest -->
@@ -102,6 +104,7 @@
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
                 <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.SECONDARY_HOME" />
                 <category android:name="android.intent.category.DEFAULT"/>
                 <category android:name="android.intent.category.LAUNCHER_APP"/>
             </intent-filter>
diff --git a/app/OWNERS b/app/OWNERS
index ba386b7..6e10365 100644
--- a/app/OWNERS
+++ b/app/OWNERS
@@ -5,9 +5,8 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 [email protected]
[email protected]  # for TaskView only
[email protected]  # for TaskView only
 
 # Recents
 per-file src/com/android/car/carlauncher/recents/* = [email protected]
diff --git a/app/car_launcher_flags.aconfig b/app/car_launcher_flags.aconfig
index 27d01b9..e7a16d2 100644
--- a/app/car_launcher_flags.aconfig
+++ b/app/car_launcher_flags.aconfig
@@ -1,4 +1,5 @@
 package: "com.android.car.carlauncher"
+container: "system"
 
 flag {
   name: "calm_mode"
@@ -6,3 +7,17 @@
   description: "This flag controls Calm mode"
   bug: "295396130"
 }
+
+flag {
+  name: "media_session_card"
+  namespace: "car_sys_exp"
+  description: "This flag controls whether the media card uses media sessions"
+  bug: "323250843"
+}
+
+flag {
+  name: "media_card_fullscreen"
+  namespace: "car_sys_exp"
+  description: "This flag controls Media widget redesign"
+  bug: "310686518"
+}
diff --git a/docklib-util/res/values/strings.xml b/app/res/anim/fade_in.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/anim/fade_in.xml
index 2c24df7..fe21775 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/anim/fade_in.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,8 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/calm_mode_activity_fade_duration"
+    android:interpolator="@android:anim/accelerate_interpolator"
+    android:fromAlpha="0.0"
+    android:toAlpha="1.0" />
diff --git a/docklib-util/res/values/strings.xml b/app/res/anim/fade_out.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to app/res/anim/fade_out.xml
index 2c24df7..3bdf9db 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/anim/fade_out.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+    android:interpolator="@android:anim/decelerate_interpolator"
+    android:duration="@integer/calm_mode_activity_fade_duration"
+    android:zAdjustment="top"
+    android:fromAlpha="1.0"
+    android:toAlpha="0.0" />
diff --git a/docklib-util/res/values/strings.xml b/app/res/animator/calm_mode_enter.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to app/res/animator/calm_mode_enter.xml
index 2c24df7..afa7e1a 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/animator/calm_mode_enter.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:duration="@integer/calm_mode_content_fade_duration"
+    android:propertyName="alpha"
+    android:valueFrom="0"
+    android:valueTo="1"
+    android:interpolator="@android:anim/accelerate_interpolator" />
diff --git a/docklib-util/res/values/strings.xml b/app/res/color/media_card_panel_button_background_tint_state_list.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/color/media_card_panel_button_background_tint_state_list.xml
index 2c24df7..e75e3bb 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/color/media_card_panel_button_background_tint_state_list.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/car_surface_container_highest"
+        android:state_selected="false"/>
+    <item android:color="@color/car_on_surface"
+        android:state_selected="true"/>
+</selector>
diff --git a/docklib-util/res/values/strings.xml b/app/res/color/media_card_panel_button_tint_state_list.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/color/media_card_panel_button_tint_state_list.xml
index 2c24df7..3cdf6ab 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/color/media_card_panel_button_tint_state_list.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/car_surface"
+        android:state_selected="true"/>
+    <item android:color="@color/car_on_surface"
+        android:state_selected="false"/>
+</selector>
diff --git a/docklib-util/res/values/strings.xml b/app/res/color/media_card_seekbar_thumb_color.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/color/media_card_seekbar_thumb_color.xml
index 2c24df7..f81a676 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/color/media_card_seekbar_thumb_color.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@android:color/transparent"
+        android:state_selected="true"/>
+    <item android:color="@color/car_on_surface"
+        android:state_selected="false"/>
+</selector>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/button_ripple.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/button_ripple.xml
index 2c24df7..8950091 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/button_ripple.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,11 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/car_ui_ripple_color">
+    <item android:id="@android:id/mask">
+        <shape android:shape="oval">
+            <solid android:color="@color/car_ui_ripple_color"/>
+        </shape>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/circle_button_background.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/circle_button_background.xml
index 2c24df7..6422682 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/circle_button_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,12 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="oval">
+            <solid android:color="@color/car_surface_container_highest"/>
+        </shape>
+    </item>
+    <item android:drawable="@drawable/button_ripple"/>
+</layer-list>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/divider.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/divider.xml
index 2c24df7..6c48401 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/divider.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,8 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/car_on_background"/>
+    <size android:height="0.5dp"/>
+</shape>
\ No newline at end of file
diff --git a/app/res/drawable/empty_action_drawable.xml b/app/res/drawable/empty_action_drawable.xml
new file mode 100644
index 0000000..35bd53b
--- /dev/null
+++ b/app/res/drawable/empty_action_drawable.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M480,760Q364.33,760 282.17,677.83Q200,595.67 200,480Q200,364.33 282.17,282.17Q364.33,200 480,200Q595.67,200 677.83,282.17Q760,364.33 760,480Q760,595.67 677.83,677.83Q595.67,760 480,760Z"/>
+</vector>
diff --git a/app/res/drawable/ic_history.xml b/app/res/drawable/ic_history.xml
new file mode 100644
index 0000000..9f16dd8
--- /dev/null
+++ b/app/res/drawable/ic_history.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@color/car_on_surface"
+        android:pathData="M146.67,880Q119.67,880 99.83,860.17Q80,840.33 80,813.33L80,386.67Q80,359.67 99.83,339.83Q119.67,320 146.67,320L813.33,320Q840.33,320 860.17,339.83Q880,359.67 880,386.67L880,813.33Q880,840.33 860.17,860.17Q840.33,880 813.33,880L146.67,880ZM146.67,813.33L813.33,813.33Q813.33,813.33 813.33,813.33Q813.33,813.33 813.33,813.33L813.33,386.67Q813.33,386.67 813.33,386.67Q813.33,386.67 813.33,386.67L146.67,386.67Q146.67,386.67 146.67,386.67Q146.67,386.67 146.67,386.67L146.67,813.33Q146.67,813.33 146.67,813.33Q146.67,813.33 146.67,813.33ZM404.67,752.67L632,600L404.67,448L404.67,752.67ZM152.67,266.67L152.67,200L807.33,200L807.33,266.67L152.67,266.67ZM280,146.67L280,80L680,80L680,146.67L280,146.67ZM146.67,813.33Q146.67,813.33 146.67,813.33Q146.67,813.33 146.67,813.33L146.67,386.67Q146.67,386.67 146.67,386.67Q146.67,386.67 146.67,386.67L146.67,386.67Q146.67,386.67 146.67,386.67Q146.67,386.67 146.67,386.67L146.67,813.33Q146.67,813.33 146.67,813.33Q146.67,813.33 146.67,813.33Z"/>
+</vector>
diff --git a/app/res/drawable/ic_overflow_horizontal.xml b/app/res/drawable/ic_overflow_horizontal.xml
new file mode 100644
index 0000000..9cc19e1
--- /dev/null
+++ b/app/res/drawable/ic_overflow_horizontal.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@color/car_on_surface"
+        android:pathData="M218.57,538.67Q194.33,538.67 177.17,521.41Q160,504.14 160,479.91Q160,455.67 177.26,438.5Q194.52,421.33 218.76,421.33Q243,421.33 260.17,438.59Q277.33,455.86 277.33,480.09Q277.33,504.33 260.07,521.5Q242.81,538.67 218.57,538.67ZM479.91,538.67Q455.67,538.67 438.5,521.41Q421.33,504.14 421.33,479.91Q421.33,455.67 438.6,438.5Q455.86,421.33 480.09,421.33Q504.33,421.33 521.5,438.59Q538.67,455.86 538.67,480.09Q538.67,504.33 521.41,521.5Q504.14,538.67 479.91,538.67ZM741.24,538.67Q717,538.67 699.83,521.41Q682.67,504.14 682.67,479.91Q682.67,455.67 699.93,438.5Q717.19,421.33 741.43,421.33Q765.67,421.33 782.83,438.59Q800,455.86 800,480.09Q800,504.33 782.74,521.5Q765.48,538.67 741.24,538.67Z"/>
+</vector>
diff --git a/app/res/drawable/ic_play_pause_selector.xml b/app/res/drawable/ic_play_pause_selector.xml
new file mode 100644
index 0000000..142869a
--- /dev/null
+++ b/app/res/drawable/ic_play_pause_selector.xml
@@ -0,0 +1,51 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true" android:state_enabled="true">
+        <vector
+            android:width="40dp"
+            android:height="40dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960">
+            <path
+                android:fillColor="@color/car_surface"
+                android:pathData="M556.67,760L556.67,200L726.67,200L726.67,760L556.67,760ZM233.33,760L233.33,200L403.33,200L403.33,760L233.33,760Z"/>
+        </vector>
+    </item>
+    <item android:state_selected="false" android:state_enabled="true">
+        <vector
+            android:width="40dp"
+            android:height="40dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960">
+            <path
+                android:fillColor="@color/car_surface"
+                android:pathData="M320,758L320,198L760,478L320,758Z"/>
+        </vector>
+    </item>
+    <item android:state_enabled="false">
+        <vector
+            android:width="40dp"
+            android:height="40dp"
+            android:viewportWidth="960"
+            android:viewportHeight="960">
+            <path
+                android:fillColor="@color/car_surface"
+                android:pathData="M642.67,557.33L328,247.33L328,198L768,478L642.67,557.33ZM792,895.33L528,630.67L328,758L328,430.67L65.33,167.33L112,120.67L840,848.67L792,895.33Z"/>
+        </vector>
+    </item>
+</selector>
diff --git a/app/res/drawable/ic_queue.xml b/app/res/drawable/ic_queue.xml
new file mode 100644
index 0000000..bd7b661
--- /dev/null
+++ b/app/res/drawable/ic_queue.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="40dp"
+    android:height="40dp"
+    android:viewportWidth="960"
+    android:viewportHeight="960">
+    <path
+        android:fillColor="@color/car_on_surface"
+        android:pathData="M641.96,800Q593.33,800 559.33,765.96Q525.33,731.92 525.33,683.29Q525.33,634.67 558.78,600.67Q592.22,566.67 640,566.67Q654.31,566.67 667.32,569.17Q680.33,571.67 692,578L692,240L880,240L880,314L758.67,314L758.67,684Q758.67,732.33 724.63,766.17Q690.59,800 641.96,800ZM120,640L120,573.33L430.67,573.33L430.67,640L120,640ZM120,473.33L120,406.67L595.33,406.67L595.33,473.33L120,473.33ZM120,306.67L120,240L595.33,240L595.33,306.67L120,306.67Z"/>
+</vector>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/media_card_button_panel_background.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/media_card_button_panel_background.xml
index 2c24df7..8f6cdf3 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/media_card_button_panel_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,9 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false">
+        <shape android:shape="rectangle"/>
+    </item>
+    <item android:drawable="@drawable/pill_button_shape" android:state_enabled="true"/>
+</selector>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/media_card_panel_button_shape.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/media_card_panel_button_shape.xml
index 2c24df7..63dd7b5 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/media_card_panel_button_shape.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,8 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="112dp" />
+    <solid android:color="@color/media_card_panel_button_background_tint_state_list"/>
+</shape>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/media_card_panel_handlebar.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/media_card_panel_handlebar.xml
index 2c24df7..e98db81 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/media_card_panel_handlebar.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,13 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/car_ui_ripple_color">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="16dp"/>
+            <solid android:color="@color/car_on_surface_variant"/>
+        </shape>
+    </item>
+</ripple>
diff --git a/app/res/drawable/media_card_seekbar_progress.xml b/app/res/drawable/media_card_seekbar_progress.xml
new file mode 100644
index 0000000..f2b5d52
--- /dev/null
+++ b/app/res/drawable/media_card_seekbar_progress.xml
@@ -0,0 +1,33 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@android:id/background">
+        <shape android:shape="line">
+            <stroke android:width="4dp" />
+        </shape>
+    </item>
+
+    <item android:id="@android:id/progress">
+        <clip>
+            <shape android:shape="line">
+                <stroke android:width="4dp" />
+            </shape>
+        </clip>
+    </item>
+
+</layer-list>
diff --git a/app/res/drawable/media_card_seekbar_thumb.xml b/app/res/drawable/media_card_seekbar_thumb.xml
new file mode 100644
index 0000000..b9dee23
--- /dev/null
+++ b/app/res/drawable/media_card_seekbar_thumb.xml
@@ -0,0 +1,65 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_selected="true">
+        <vector
+            android:width="8dp"
+            android:height="32dp"
+            android:viewportWidth="12"
+            android:viewportHeight="32">
+            <group>
+                <clip-path
+                    android:pathData="M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z" />
+                <path
+                    android:pathData="M4 4V28H8V4"
+                    android:fillColor="@android:color/transparent" />
+            </group>
+            <group>
+                <clip-path
+                    android:pathData="M0 0V32H12V0M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z" />
+                <path
+                    android:pathData="M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z"
+                    android:strokeWidth="4"
+                    android:strokeColor="@android:color/transparent" />
+            </group>
+        </vector>
+
+    </item>
+    <item android:state_selected="false">
+        <vector
+            android:width="12dp"
+            android:height="32dp"
+            android:viewportWidth="12"
+            android:viewportHeight="32">
+            <group>
+                <clip-path
+                    android:pathData="M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z" />
+                <path
+                    android:pathData="M4 4V28H8V4"
+                    android:fillColor="@color/car_on_surface" />
+            </group>
+            <group>
+                <clip-path
+                    android:pathData="M0 0V32H12V0M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z" />
+                <path
+                    android:pathData="M6 4C7.10457 4 8 4.89543 8 6V26C8 27.1046 7.10457 28 6 28C4.89543 28 4 27.1046 4 26V6C4 4.89543 4.89543 4 6 4Z"
+                    android:strokeWidth="4"
+                    android:strokeColor="@android:color/transparent" />
+            </group>
+        </vector>
+    </item>
+</selector>
diff --git a/app/res/drawable/pill_button_shape.xml b/app/res/drawable/pill_button_shape.xml
new file mode 100644
index 0000000..69342ad
--- /dev/null
+++ b/app/res/drawable/pill_button_shape.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape
+            android:shape="rectangle">
+            <corners android:radius="@dimen/media_card_pill_radius" />
+            <solid android:color="@color/car_surface_container_highest" />
+        </shape>
+    </item>
+    <item android:drawable="@drawable/button_ripple"/>
+</layer-list>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/radius_16_background.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/radius_16_background.xml
index 2c24df7..df0bc57 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/radius_16_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,7 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="16dp"/>
+</shape>
diff --git a/docklib-util/res/values/strings.xml b/app/res/drawable/radius_8_background.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to app/res/drawable/radius_8_background.xml
index 2c24df7..3ade4bb 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/res/drawable/radius_8_background.xml
@@ -1,6 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -15,8 +14,7 @@
   ~ limitations under the License.
   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="8dp"/>
+</shape>
diff --git a/app/res/layout/calm_mode_date_and_temperature.xml b/app/res/layout/calm_mode_date_and_temperature.xml
deleted file mode 100644
index 66afd0a..0000000
--- a/app/res/layout/calm_mode_date_and_temperature.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 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.
-  -->
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_height="wrap_content"
-    android:layout_width="wrap_content">
-    <TextClock
-        android:id="@+id/date"
-        style="@style/CalmMode.Text.Date"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        android:layout_marginEnd="@dimen/calm_mode_padding"
-        app:layout_constraintEnd_toStartOf="@id/temperature_icon"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <androidx.constraintlayout.widget.Group
-        android:id="@+id/temperature_group"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:visibility="gone"
-        app:constraint_referenced_ids="temperature_icon,temperature"/>
-
-    <ImageView
-        android:id="@+id/temperature_icon"
-        style="@style/CalmMode.Icon.Temperature"
-        android:layout_marginEnd="@dimen/calm_mode_padding"
-        android:src="@drawable/ic_temperature"
-        app:layout_constraintEnd_toStartOf="@id/temperature"
-        app:layout_constraintTop_toTopOf="@id/temperature"
-        app:layout_constraintBottom_toBottomOf="@id/temperature" />
-
-    <TextView
-        android:id="@+id/temperature"
-        style="@style/CalmMode.Text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        android:layout_marginEnd="@dimen/calm_mode_padding"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"/>
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/res/layout/calm_mode_fragment.xml b/app/res/layout/calm_mode_fragment.xml
index bdb8095..c1d1502 100644
--- a/app/res/layout/calm_mode_fragment.xml
+++ b/app/res/layout/calm_mode_fragment.xml
@@ -22,7 +22,7 @@
     android:layout_height="match_parent">
 
     <ImageView
-        style="@style/CalmMode.BackgroundImage"
+        style="@style/CalmModeBackgroundImage"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
@@ -30,88 +30,85 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
-    <androidx.constraintlayout.widget.Group
-        android:id="@+id/media_group"
+    <TextClock
+        android:id="@+id/date"
+        style="@style/CalmModeText.Date"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/calm_mode_padding_vertical"
         android:visibility="gone"
-        app:constraint_referenced_ids="media_icon,media_title" />
-
-    <ImageView
-        android:id="@+id/media_icon"
-        style="@style/CalmMode.Icon"
-        android:scaleType="fitCenter"
-        android:src="@drawable/ic_media"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        android:layout_marginStart="@dimen/calm_mode_padding"
-        app:layout_constraintBottom_toTopOf="@id/barrier"
+        app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintEnd_toStartOf="@id/temperature_icon"
+        app:layout_constraintHorizontal_chainStyle="packed" />
 
     <TextView
-        android:id="@+id/media_title"
-        style="@style/CalmMode.Text.MediaTitle"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        android:layout_marginStart="@dimen/calm_mode_icon_margin"
-        app:layout_constraintBottom_toTopOf="@id/barrier"
-        app:layout_constraintStart_toEndOf="@id/media_icon"
-        app:layout_constraintTop_toTopOf="parent"/>
-
-    <androidx.constraintlayout.widget.Group
-        android:id="@+id/nav_group"
+        android:id="@+id/temperature_icon"
+        style="@style/CalmModeText.Temperature"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:visibility="gone"
-        app:constraint_referenced_ids="nav_state_icon,nav_state" />
+        app:layout_constraintBottom_toBottomOf="@id/temperature"
+        app:layout_constraintStart_toEndOf="@id/date"
+        app:layout_constraintEnd_toStartOf="@id/temperature"
+        android:text="@string/calm_mode_separator" />
+
+    <TextView
+        android:id="@+id/temperature"
+        style="@style/CalmModeText.Temperature"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_marginTop="@dimen/calm_mode_padding_vertical"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/temperature_icon"
+        app:layout_constraintEnd_toStartOf="@id/nav_state_icon" />
 
     <ImageView
         android:id="@+id/nav_state_icon"
-        style="@style/CalmMode.Icon"
+        style="@style/CalmModeIcon"
+        android:layout_width="@dimen/calm_mode_icon_size_regular"
+        android:layout_height="@dimen/calm_mode_icon_size_regular"
+        android:visibility="gone"
+        android:layout_marginStart="@dimen/calm_mode_icon_margin"
         android:layout_marginEnd="@dimen/calm_mode_icon_margin"
         android:src="@drawable/ic_navigation"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        app:layout_constraintBottom_toTopOf="@id/barrier"
+        app:layout_constraintTop_toTopOf="@id/nav_state"
+        app:layout_constraintBottom_toBottomOf="@id/nav_state"
         app:layout_constraintEnd_toStartOf="@id/nav_state"
-        app:layout_constraintTop_toTopOf="parent" />
+        app:layout_constraintStart_toEndOf="@id/temperature" />
 
     <TextView
         android:id="@+id/nav_state"
-        style="@style/CalmMode.Text"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/calm_mode_padding"
-        android:layout_marginStart="@dimen/calm_mode_icon_margin"
-        app:layout_constraintBottom_toTopOf="@id/barrier"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"/>
-
-    <include
-        android:id="@+id/date_and_temperature_container"
-        layout="@layout/calm_mode_date_and_temperature"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/barrier"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        app:barrierAllowsGoneWidgets="true"
-        app:barrierDirection="bottom"
-        app:constraint_referenced_ids="media_icon, media_title, nav_icon, nav_state, date_and_temperature_container" />
-
-    <TextClock
-        android:id="@+id/clock"
-        style="@style/CalmMode.Text.Clock"
+        style="@style/CalmModeText"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:visibility="gone"
+        android:layout_marginTop="@dimen/calm_mode_padding_vertical"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintStart_toEndOf="@id/nav_state_icon"
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <TextClock
+        android:id="@+id/clock"
+        style="@style/CalmModeClock"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        app:layout_constraintTop_toBottomOf="@id/date"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/barrier"/>
+        app:layout_constraintEnd_toEndOf="parent" />
+
+    <TextView
+        android:id="@+id/media_title"
+        style="@style/CalmModeText.MediaTitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:visibility="gone"
+        android:layout_marginTop="@dimen/calm_mode_clock_media_title_margin"
+        app:layout_constraintTop_toBottomOf="@id/clock"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent" />
+
 </androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/res/layout/card_fragment_audio_card.xml b/app/res/layout/card_fragment_audio_card.xml
new file mode 100644
index 0000000..bea311f
--- /dev/null
+++ b/app/res/layout/card_fragment_audio_card.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/in_call_fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+
+    <FrameLayout
+        android:id="@+id/media_fragment_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/app/res/layout/media_card_fullscreen.xml b/app/res/layout/media_card_fullscreen.xml
new file mode 100644
index 0000000..10ad76e
--- /dev/null
+++ b/app/res/layout/media_card_fullscreen.xml
@@ -0,0 +1,262 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    app:cardBackgroundColor="@color/car_surface_container_high"
+    app:cardCornerRadius="@dimen/media_card_card_radius"
+    app:cardElevation="0dp">
+
+    <androidx.constraintlayout.motion.widget.MotionLayout
+        android:id="@+id/motion_layout"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        app:layoutDescription="@xml/panel_animation_motion_scene">
+
+        <LinearLayout
+            android:id="@+id/media_card_panel_content_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_marginTop="@dimen/media_card_panel_content_margin_top"
+            android:background="@color/car_surface_container_highest"
+            android:orientation="vertical"
+            app:layout_constraintBottom_toBottomOf="parent">
+            <FrameLayout
+                android:id="@+id/media_card_panel_handlebar"
+                android:layout_width="match_parent"
+                android:layout_height="@dimen/media_card_panel_handlebar_touch_target_height"
+                android:paddingStart="@dimen/media_card_panel_handlebar_horizontal_padding"
+                android:paddingEnd="@dimen/media_card_panel_handlebar_horizontal_padding"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toBottomOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent">
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/media_card_panel_handlebar_height"
+                    android:layout_gravity="center"
+                    android:background="@drawable/media_card_panel_handlebar" />
+            </FrameLayout>
+
+            <androidx.viewpager2.widget.ViewPager2
+                android:id="@+id/view_pager"
+                android:background="@color/car_surface_container_highest"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent" />
+        </LinearLayout>
+
+        <FrameLayout
+            android:id="@+id/empty_panel"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="@color/car_surface_container_high"
+            app:layout_constraintTop_toTopOf="parent"/>
+
+        <ImageView
+            android:id="@+id/album_art"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/media_card_album_art_size"
+            android:scaleType="fitCenter"
+            android:adjustViewBounds="true"
+            android:background="@android:color/transparent"
+            android:layout_marginStart="@dimen/media_card_horizontal_margin"
+            android:layout_marginEnd="@dimen/media_card_album_art_end_margin"
+            android:layout_marginTop="@dimen/media_card_horizontal_margin"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="0"
+            app:layout_constrainedWidth="true"/>
+
+        <ImageView
+            android:id="@+id/media_widget_app_icon"
+            android:layout_width="@dimen/media_card_app_icon_size"
+            android:layout_height="@dimen/media_card_app_icon_size"
+            android:clipToOutline="true"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            android:layout_marginTop="@dimen/media_card_horizontal_margin"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+        <androidx.constraintlayout.widget.Guideline
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:id="@+id/guideline"
+            app:layout_constraintGuide_begin="@dimen/media_card_text_view_guideline_start"
+            android:orientation="horizontal"/>
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:text="@string/metadata_default_title"
+            android:textColor="@color/car_text_primary"
+            android:gravity="center_vertical"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginTop="@dimen/media_card_view_separation_margin"
+            android:layout_marginHorizontal="@dimen/media_card_horizontal_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/guideline"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintVertical_bias="0"/>
+
+        <TextView
+            android:id="@+id/subtitle"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            style="@style/TextAppearance.Car.Body.Small"
+            android:textColor="@color/car_text_secondary"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginTop="@dimen/media_card_artist_top_margin"
+            android:layout_marginHorizontal="@dimen/media_card_horizontal_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toBottomOf="@id/title" />
+
+        <SeekBar
+            android:id="@+id/playback_seek_bar"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:paddingEnd="0dp"
+            android:paddingStart="0dp"
+            android:progressBackgroundTint="@color/car_surface_container_highest"
+            android:progressDrawable="@drawable/media_card_seekbar_progress"
+            android:progressTint="@color/car_primary"
+            android:splitTrack="true"
+            android:thumb="@drawable/media_card_seekbar_thumb"
+            android:thumbTint="@color/car_on_surface"
+            android:thumbOffset="0px"
+            android:layout_marginTop="@dimen/media_card_view_separation_margin"
+            android:layout_marginStart="@dimen/media_card_horizontal_margin"
+            android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+            android:clickable="true"
+            android:focusable="true"
+            app:layout_goneMarginEnd="@dimen/media_card_horizontal_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/content_format"
+            app:layout_constraintTop_toBottomOf="@id/subtitle" />
+
+        <com.android.car.media.common.ContentFormatView
+            android:id="@+id/content_format"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/media_card_logo_size"
+            android:layout_gravity="center_vertical"
+            android:adjustViewBounds="true"
+            android:scaleType="fitStart"
+            app:logoTint="@color/car_on_surface_variant"
+            app:logoSize="small"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            app:layout_constraintStart_toEndOf="@id/playback_seek_bar"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/playback_seek_bar"
+            app:layout_constraintBottom_toBottomOf="@id/playback_seek_bar" />
+
+        <ImageButton
+            android:id="@+id/play_pause_button"
+            android:layout_width="0dp"
+            android:layout_height="@dimen/media_card_large_button_size"
+            android:src="@drawable/ic_play_pause_selector"
+            android:scaleType="center"
+            android:tint="@color/car_surface_container_high"
+            android:background="@drawable/pill_button_shape"
+            android:backgroundTint="@color/car_primary"
+            android:layout_marginBottom="@dimen/media_card_play_button_bottom_margin"
+            app:layout_goneMarginEnd="@dimen/media_card_horizontal_margin"
+            app:layout_goneMarginStart="@dimen/media_card_horizontal_margin"
+            app:layout_constraintStart_toEndOf="@id/playback_action_id1"
+            app:layout_constraintEnd_toStartOf="@id/playback_action_id2"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintVertical_bias="1" />
+
+        <ImageButton
+            android:id="@+id/playback_action_id1"
+            android:layout_width="@dimen/media_card_large_button_size"
+            android:layout_height="@dimen/media_card_large_button_size"
+            android:scaleType="fitCenter"
+            android:padding="@dimen/media_card_large_button_icon_padding"
+            android:cropToPadding="true"
+            android:tint="@color/car_on_surface_variant"
+            android:background="@drawable/circle_button_background"
+            android:layout_marginStart="@dimen/media_card_horizontal_margin"
+            android:layout_marginEnd="@dimen/media_card_play_button_horizontal_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/play_pause_button"
+            app:layout_constraintTop_toTopOf="@id/play_pause_button"
+            app:layout_constraintBottom_toBottomOf="@id/play_pause_button"
+            app:layout_constraintHorizontal_bias="0"/>
+
+        <ImageButton
+            android:id="@+id/playback_action_id2"
+            android:layout_width="@dimen/media_card_large_button_size"
+            android:layout_height="@dimen/media_card_large_button_size"
+            android:scaleType="fitCenter"
+            android:padding="@dimen/media_card_large_button_icon_padding"
+            android:cropToPadding="true"
+            android:tint="@color/car_on_surface_variant"
+            android:background="@drawable/circle_button_background"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            android:layout_marginStart="@dimen/media_card_play_button_horizontal_margin"
+            app:layout_constraintStart_toEndOf="@id/play_pause_button"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="@id/play_pause_button"
+            app:layout_constraintBottom_toBottomOf="@id/play_pause_button"
+            app:layout_constraintHorizontal_bias="1" />
+
+        <LinearLayout
+            android:id="@+id/button_panel_background"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/media_card_bottom_panel_height"
+            android:background="@drawable/media_card_button_panel_background"
+            android:backgroundTint="@color/car_surface_container_highest"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintVertical_bias="1">
+            <ImageButton
+                android:id="@+id/overflow_button"
+                android:layout_width="0dp"
+                android:layout_height="@dimen/media_card_bottom_panel_button_size"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_overflow_horizontal"
+                android:layout_weight="1"
+                style="@style/MediaCardPanelButtonStyle"/>
+            <ImageButton
+                android:id="@+id/queue_button"
+                android:layout_width="0dp"
+                android:layout_height="@dimen/media_card_bottom_panel_button_size"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_queue"
+                android:layout_weight="1"
+                style="@style/MediaCardPanelButtonStyle"/>
+            <ImageButton
+                android:id="@+id/history_button"
+                android:layout_width="0dp"
+                android:layout_height="@dimen/media_card_bottom_panel_button_size"
+                android:layout_gravity="center"
+                android:src="@drawable/ic_history"
+                android:layout_weight="1"
+                style="@style/MediaCardPanelButtonStyle"/>
+        </LinearLayout>
+    </androidx.constraintlayout.motion.widget.MotionLayout>
+</androidx.cardview.widget.CardView>
diff --git a/app/res/layout/media_card_history_header_item.xml b/app/res/layout/media_card_history_header_item.xml
new file mode 100644
index 0000000..5b073e3
--- /dev/null
+++ b/app/res/layout/media_card_history_header_item.xml
@@ -0,0 +1,38 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingBottom="@dimen/media_card_view_separation_margin">
+
+    <ImageView
+        android:id="@+id/history_card_header_icon"
+        android:layout_width="@dimen/media_card_view_header_icon_size"
+        android:layout_height="@dimen/media_card_view_header_icon_size"
+        android:layout_marginStart="@dimen/media_card_horizontal_margin"
+        android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+        android:src="@drawable/ic_history"/>
+
+    <TextView
+        android:id="@+id/history_card_history_header_title_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:includeFontPadding="false"
+        android:text="@string/media_card_history_header_title"
+        android:textAppearance="@style/TextAppearance.Car.Body.Small"
+        android:textColor="@color/car_text_primary"/>
+</LinearLayout>
diff --git a/app/res/layout/media_card_history_item.xml b/app/res/layout/media_card_history_item.xml
new file mode 100644
index 0000000..eeb9bb4
--- /dev/null
+++ b/app/res/layout/media_card_history_item.xml
@@ -0,0 +1,109 @@
+
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="@dimen/media_card_history_item_height"
+    android:layout_width="match_parent"
+    android:foreground="?android:attr/selectableItemBackground"
+    app:cardBackgroundColor="@android:color/transparent"
+    app:contentPaddingLeft="@dimen/media_card_horizontal_margin"
+    app:contentPaddingRight="@dimen/media_card_horizontal_margin">
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/history_card_container_active"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent">
+        <TextView
+            android:id="@+id/history_card_title_active"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:textAppearance="@style/TextAppearance.Car.Body.Small"
+            android:textColor="@color/car_text_primary"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/history_card_album_art"
+            app:layout_constraintTop_toTopOf="@id/history_card_album_art"
+            app:layout_constraintBottom_toTopOf="@id/history_card_subtitle_active"
+            app:layout_constraintHorizontal_bias="0"/>
+        <TextView
+            android:id="@+id/history_card_subtitle_active"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:textAppearance="@style/TextAppearance.Car.Body.Small"
+            android:textColor="@color/car_text_secondary"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginStart="@dimen/media_card_view_separation_margin"
+            android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+            app:layout_constraintStart_toEndOf="@id/history_card_app_thumbnail"
+            app:layout_constraintEnd_toStartOf="@id/history_card_album_art"
+            app:layout_constraintTop_toBottomOf="@id/history_card_title_active"
+            app:layout_constraintBottom_toBottomOf="@id/history_card_album_art"
+            app:layout_constraintHorizontal_bias="0"/>
+        <ImageView
+            android:id="@+id/history_card_album_art"
+            android:layout_width="@dimen/media_card_history_item_icon_size"
+            android:layout_height="@dimen/media_card_history_item_icon_size"
+            android:background="@drawable/radius_8_background"
+            android:clipToOutline="true"
+            android:scaleType="centerCrop"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+        <ImageView
+            android:id="@+id/history_card_app_thumbnail"
+            android:layout_width="@dimen/media_card_history_item_thumbnail_size"
+            android:layout_height="@dimen/media_card_history_item_thumbnail_size"
+            android:background="@drawable/radius_16_background"
+            android:clipToOutline="true"
+            android:scaleType="centerCrop"
+            app:layout_constraintTop_toTopOf="@id/history_card_subtitle_active"
+            app:layout_constraintBottom_toBottomOf="@id/history_card_subtitle_active"
+            app:layout_constraintStart_toStartOf="parent"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/history_card_container_inactive"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent">
+        <TextView
+            android:id="@+id/history_card_app_title_inactive"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:textAppearance="@style/TextAppearance.Car.Body.Small"
+            android:textColor="@color/car_text_primary"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintEnd_toStartOf="@id/history_item_app_icon_inactive"
+            app:layout_constraintTop_toTopOf="@id/history_item_app_icon_inactive"
+            app:layout_constraintBottom_toBottomOf="@id/history_item_app_icon_inactive"
+            app:layout_constraintHorizontal_bias="0"/>
+        <ImageView
+            android:id="@+id/history_item_app_icon_inactive"
+            android:layout_width="@dimen/media_card_history_item_icon_size"
+            android:layout_height="@dimen/media_card_history_item_icon_size"
+            android:background="@drawable/radius_16_background"
+            android:clipToOutline="true"
+            android:scaleType="centerCrop"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"/>
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</androidx.cardview.widget.CardView>
diff --git a/app/res/layout/media_card_panel_content_item.xml b/app/res/layout/media_card_panel_content_item.xml
new file mode 100644
index 0000000..1c11c1b
--- /dev/null
+++ b/app/res/layout/media_card_panel_content_item.xml
@@ -0,0 +1,120 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TableLayout
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"
+        android:id="@+id/overflow_grid"
+        android:stretchColumns="0,1"
+        android:background="@color/car_surface_container_highest">
+        <TableRow
+            android:layout_weight="1"
+            android:gravity="center">
+            <ImageButton
+                android:id="@+id/playback_action_id3"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle" />
+            <ImageButton
+                android:id="@+id/playback_action_id4"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle" />
+        </TableRow>
+
+        <TableRow
+            android:layout_weight="1"
+            android:gravity="center">
+            <ImageButton
+                android:id="@+id/playback_action_id5"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle" />
+            <ImageButton
+                android:id="@+id/playback_action_id6"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle" />
+        </TableRow>
+
+        <TableRow
+            android:layout_weight="1"
+            android:gravity="center">
+            <ImageButton
+                android:id="@+id/playback_action_id7"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle"/>
+            <ImageButton
+                android:id="@+id/playback_action_id8"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle" />
+        </TableRow>
+
+        <TableRow
+            android:layout_weight="1"
+            android:gravity="center">
+            <ImageButton
+                android:id="@+id/playback_action_id9"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle"/>
+            <ImageButton
+                android:id="@+id/playback_action_id10"
+                android:layout_width="@dimen/media_card_small_button_size"
+                android:layout_height="@dimen/media_card_small_button_size"
+                style="@style/MediaCardCustomActionButtonStyle"/>
+        </TableRow>
+    </TableLayout>
+
+    <FrameLayout
+        android:id="@+id/queue_list_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:paddingStart="@dimen/media_card_horizontal_margin"
+        android:paddingEnd="@dimen/media_card_horizontal_margin"
+        android:background="@color/car_surface_container_highest"
+        android:visibility="gone">
+        <com.android.car.apps.common.CarUiRecyclerViewNoScrollbar
+            android:id="@+id/queue_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:enableDivider="true"
+            android:requiresFadingEdge="vertical"
+            android:fadingEdgeLength="@dimen/media_card_recycler_view_fading_edge_length"/>
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/history_list_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/car_surface_container_highest"
+        android:visibility="gone">
+        <com.android.car.apps.common.CarUiRecyclerViewNoScrollbar
+            android:id="@+id/history_list"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            app:enableDivider="true"
+            android:requiresFadingEdge="vertical"
+            android:fadingEdgeLength="@dimen/media_card_recycler_view_fading_edge_length"/>
+    </FrameLayout>
+</FrameLayout>
diff --git a/app/res/layout/media_card_queue_header_item.xml b/app/res/layout/media_card_queue_header_item.xml
new file mode 100644
index 0000000..e7b1111
--- /dev/null
+++ b/app/res/layout/media_card_queue_header_item.xml
@@ -0,0 +1,36 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingBottom="@dimen/media_card_view_separation_margin">
+
+    <ImageView
+        android:id="@+id/header_app_icon"
+        android:layout_width="@dimen/media_card_queue_header_app_icon_size"
+        android:layout_height="@dimen/media_card_queue_header_app_icon_size"
+        android:layout_marginEnd="@dimen/media_card_view_separation_margin" />
+
+    <TextView
+        android:id="@+id/media_card_queue_header_title_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:includeFontPadding="false"
+        android:text="@string/media_card_queue_header_title"
+        android:textAppearance="@style/TextAppearance.Car.Body.Small"
+        android:textColor="@color/car_text_primary"/>
+</LinearLayout>
diff --git a/app/res/layout/media_card_queue_item.xml b/app/res/layout/media_card_queue_item.xml
new file mode 100644
index 0000000..966bb2e
--- /dev/null
+++ b/app/res/layout/media_card_queue_item.xml
@@ -0,0 +1,85 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<!--
+  ~ CopyEnd (C) 2024 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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_height="wrap_content"
+    android:layout_width="match_parent"
+    android:paddingTop="@dimen/media_card_view_separation_margin"
+    android:paddingBottom="@dimen/media_card_view_separation_margin">
+
+    <TextView
+        android:id="@+id/queue_list_item_title"
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:textAppearance="@style/TextAppearance.Car.Body.Small"
+        android:textColor="@color/car_text_primary"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/thumbnail"
+        app:layout_constraintTop_toTopOf="@id/thumbnail"
+        app:layout_constraintBottom_toBottomOf="@id/thumbnail"
+        app:layout_constraintVertical_bias="0"
+        app:layout_constraintHorizontal_bias="0"/>
+
+    <TextView
+        android:id="@+id/queue_list_item_subtitle"
+        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:textAppearance="@style/TextAppearance.Car.Body.Small"
+        android:textColor="@color/car_text_secondary"
+        android:maxLines="1"
+        android:ellipsize="end"
+        android:layout_marginEnd="@dimen/media_card_view_separation_margin"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toStartOf="@id/thumbnail"
+        app:layout_constraintTop_toTopOf="@id/thumbnail"
+        app:layout_constraintBottom_toBottomOf="@id/thumbnail"
+        app:layout_constraintVertical_bias="1"
+        app:layout_constraintHorizontal_bias="0"/>
+
+    <ImageView
+        android:id="@+id/thumbnail"
+        android:layout_width="@dimen/media_card_queue_item_thumbnail_size"
+        android:layout_height="@dimen/media_card_queue_item_thumbnail_size"
+        android:background="@drawable/radius_8_background"
+        android:clipToOutline="true"
+        android:scaleType="centerCrop"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toEndOf="@id/queue_list_item_title"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHorizontal_bias="1" />
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/app/res/values-af/strings.xml b/app/res/values-af/strings.xml
index 026c73c..b9deb8d 100644
--- a/app/res/values-af/strings.xml
+++ b/app/res/values-af/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Vee alles uit"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App is nie beskikbaar nie"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Kalmmodus"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Waglys"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Mediabron"</string>
 </resources>
diff --git a/app/res/values-am/strings.xml b/app/res/values-am/strings.xml
index 6b1b6c1..d7a0acd 100644
--- a/app/res/values-am/strings.xml
+++ b/app/res/values-am/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ሁሉንም አጽዳ"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"መተግበሪያ አይገኝም"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"የእርጋታ ሁነታ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ሰልፍ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"የሚዲያ ምንጭ"</string>
 </resources>
diff --git a/app/res/values-ar/strings.xml b/app/res/values-ar/strings.xml
index 345de0d..bd1730a 100644
--- a/app/res/values-ar/strings.xml
+++ b/app/res/values-ar/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"محو الكل"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"التطبيق غير متاح."</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"وضع الهدوء"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"قائمة المحتوى التالي"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"مصدر الوسائط"</string>
 </resources>
diff --git a/app/res/values-as/strings.xml b/app/res/values-as/strings.xml
index 23500ec..0edbaba 100644
--- a/app/res/values-as/strings.xml
+++ b/app/res/values-as/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"আটাইবোৰ মচক"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"এপ্‌টো উপলব্ধ নহয়"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"শান্ত ম’ড"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"শাৰী"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"মিডিয়াৰ উৎস"</string>
 </resources>
diff --git a/app/res/values-az/strings.xml b/app/res/values-az/strings.xml
index 130ebd6..111c8f6 100644
--- a/app/res/values-az/strings.xml
+++ b/app/res/values-az/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Hamısını silin"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Tətbiq əlçatan deyil"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Sakit rejim"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Növbə"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media mənbəyi"</string>
 </resources>
diff --git a/app/res/values-b+sr+Latn/strings.xml b/app/res/values-b+sr+Latn/strings.xml
index 2061112..35e35e4 100644
--- a/app/res/values-b+sr+Latn/strings.xml
+++ b/app/res/values-b+sr+Latn/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Obriši sve"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacija nije dostupna"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Režim opuštanja"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Redosled"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Izvor medija"</string>
 </resources>
diff --git a/app/res/values-be/strings.xml b/app/res/values-be/strings.xml
index 2e6f4da..d4dedb0 100644
--- a/app/res/values-be/strings.xml
+++ b/app/res/values-be/strings.xml
@@ -27,12 +27,14 @@
     <string name="projected_launch_text" msgid="5034079820478748609">"Запусціць Android Auto"</string>
     <string name="projected_onclick_launch_error_toast_text" msgid="8853804785626030351">"Не ўдалося запусціць Android Auto. Дзеянні не знойдзены."</string>
     <string name="projection_devices" msgid="2556503818120676439">"{count,plural, =1{# прылада}one{# прылада}few{# прылады}many{# прылад}other{# прылады}}"</string>
-    <string name="weather_app_name" msgid="4356705068077942048">"Надвор\'е"</string>
+    <string name="weather_app_name" msgid="4356705068077942048">"Надвор’е"</string>
     <string name="fake_weather_main_text" msgid="2545755284647327839">"--°, пераважна сонечна"</string>
-    <string name="fake_weather_footer_text" msgid="8640814250285014485">"Маўтын-В\'ю • макс.: --°, мін.: --°"</string>
+    <string name="fake_weather_footer_text" msgid="8640814250285014485">"Маўтын-В’ю • макс.: --°, мін.: --°"</string>
     <string name="times_separator" msgid="1962841895013564645">"/"</string>
     <string name="recents_empty_state_text" msgid="8228569970506899117">"Няма нядаўніх элементаў"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Ачысціць усё"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Праграма недаступная"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Рэжым спакою"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Чарга"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Крыніца мультымедыя"</string>
 </resources>
diff --git a/app/res/values-bg/strings.xml b/app/res/values-bg/strings.xml
index 8425793..37e0d59 100644
--- a/app/res/values-bg/strings.xml
+++ b/app/res/values-bg/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Изчистване на всичко"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Приложението не е налично"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Режим на покой"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Опашка"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Източник на мултимедията"</string>
 </resources>
diff --git a/app/res/values-bn/strings.xml b/app/res/values-bn/strings.xml
index d9b579c..cf7c2fd 100644
--- a/app/res/values-bn/strings.xml
+++ b/app/res/values-bn/strings.xml
@@ -34,5 +34,7 @@
     <string name="recents_empty_state_text" msgid="8228569970506899117">"কোনও সাম্প্রতিক আইটেম নেই"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"সব মুছে দিন"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"অ্যাপ উপলভ্য নেই"</string>
-    <string name="calm_mode_title" msgid="4364804976931157567">"\'কাম\' মোড"</string>
+    <string name="calm_mode_title" msgid="4364804976931157567">"Calm মোড"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"সারি"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"মিডিয়া সোর্স"</string>
 </resources>
diff --git a/app/res/values-bs/strings.xml b/app/res/values-bs/strings.xml
index 23c696f..c17c375 100644
--- a/app/res/values-bs/strings.xml
+++ b/app/res/values-bs/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Obriši sve"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacija nije dostupna"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Način rada za opuštanje"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Red čekanja"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Izvor medijskog sadržaja"</string>
 </resources>
diff --git a/app/res/values-ca/strings.xml b/app/res/values-ca/strings.xml
index e2d8eec..2db4151 100644
--- a/app/res/values-ca/strings.xml
+++ b/app/res/values-ca/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Esborra-ho tot"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"L\'aplicació no està disponible"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Mode de calma"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Cua"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Font del contingut multimèdia"</string>
 </resources>
diff --git a/app/res/values-cs/strings.xml b/app/res/values-cs/strings.xml
index 8fd05bd..bbf2d9d 100644
--- a/app/res/values-cs/strings.xml
+++ b/app/res/values-cs/strings.xml
@@ -31,8 +31,10 @@
     <string name="fake_weather_main_text" msgid="2545755284647327839">"--° Většinou slunečno"</string>
     <string name="fake_weather_footer_text" msgid="8640814250285014485">"Mountain View • Nejvyšší: --° Nejnižší: --°"</string>
     <string name="times_separator" msgid="1962841895013564645">"/"</string>
-    <string name="recents_empty_state_text" msgid="8228569970506899117">"Žádné nedávné položky"</string>
+    <string name="recents_empty_state_text" msgid="8228569970506899117">"Žádné položky z nedávné doby"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Vymazat vše"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikace není k dispozici"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Klidný režim"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Fronta"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Zdroj médií"</string>
 </resources>
diff --git a/app/res/values-da/strings.xml b/app/res/values-da/strings.xml
index f17d3fa..f6f6125 100644
--- a/app/res/values-da/strings.xml
+++ b/app/res/values-da/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Ryd alt"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Appen er ikke tilgængelig"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Beroligende tilstand"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Kø"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Mediekilde"</string>
 </resources>
diff --git a/app/res/values-de/strings.xml b/app/res/values-de/strings.xml
index ef60b01..4bae3c9 100644
--- a/app/res/values-de/strings.xml
+++ b/app/res/values-de/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Alles löschen"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App nicht verfügbar"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Ruhemodus"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Wiedergabeliste"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Medienquelle"</string>
 </resources>
diff --git a/app/res/values-el/strings.xml b/app/res/values-el/strings.xml
index 9c9bf06..469d2c7 100644
--- a/app/res/values-el/strings.xml
+++ b/app/res/values-el/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Διαγραφή όλων"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Η εφαρμογή δεν είναι διαθέσιμη"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Λειτουργία ηρεμίας"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Ουρά"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Πηγή μέσων"</string>
 </resources>
diff --git a/app/res/values-en-rAU/strings.xml b/app/res/values-en-rAU/strings.xml
index 9c42c4d..1c7025f 100644
--- a/app/res/values-en-rAU/strings.xml
+++ b/app/res/values-en-rAU/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Clear all"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App isn\'t available"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Calm mode"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Queue"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media source"</string>
 </resources>
diff --git a/app/res/values-en-rCA/strings.xml b/app/res/values-en-rCA/strings.xml
index a62433b..f3a023b 100644
--- a/app/res/values-en-rCA/strings.xml
+++ b/app/res/values-en-rCA/strings.xml
@@ -17,39 +17,24 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- no translation found for app_title (1056886619192068947) -->
-    <skip />
+    <string name="app_title" msgid="1056886619192068947">"Car Launcher"</string>
     <string name="default_media_song_title" msgid="7837564242036091946"></string>
-    <!-- no translation found for tap_for_more_info_text (4240146824238692769) -->
-    <skip />
-    <!-- no translation found for tap_to_launch_text (7150379866796152196) -->
-    <skip />
-    <!-- no translation found for ongoing_call_duration_text_separator (2140398350095052096) -->
-    <skip />
-    <!-- no translation found for ongoing_call_text (7160701768924041827) -->
-    <skip />
-    <!-- no translation found for dialing_call_text (3286036311692512894) -->
-    <skip />
-    <!-- no translation found for projected_launch_text (5034079820478748609) -->
-    <skip />
-    <!-- no translation found for projected_onclick_launch_error_toast_text (8853804785626030351) -->
-    <skip />
-    <!-- no translation found for projection_devices (2556503818120676439) -->
-    <skip />
-    <!-- no translation found for weather_app_name (4356705068077942048) -->
-    <skip />
-    <!-- no translation found for fake_weather_main_text (2545755284647327839) -->
-    <skip />
-    <!-- no translation found for fake_weather_footer_text (8640814250285014485) -->
-    <skip />
-    <!-- no translation found for times_separator (1962841895013564645) -->
-    <skip />
-    <!-- no translation found for recents_empty_state_text (8228569970506899117) -->
-    <skip />
-    <!-- no translation found for recents_clear_all_text (3594272268167720553) -->
-    <skip />
-    <!-- no translation found for failure_opening_recent_task_message (963567570097465902) -->
-    <skip />
-    <!-- no translation found for calm_mode_title (4364804976931157567) -->
-    <skip />
+    <string name="tap_for_more_info_text" msgid="4240146824238692769">"Tap card for more info"</string>
+    <string name="tap_to_launch_text" msgid="7150379866796152196">"Tap card to launch"</string>
+    <string name="ongoing_call_duration_text_separator" msgid="2140398350095052096">" • "</string>
+    <string name="ongoing_call_text" msgid="7160701768924041827">"Ongoing call"</string>
+    <string name="dialing_call_text" msgid="3286036311692512894">"Dialing…"</string>
+    <string name="projected_launch_text" msgid="5034079820478748609">"Launch Android Auto"</string>
+    <string name="projected_onclick_launch_error_toast_text" msgid="8853804785626030351">"Unable to launch Android Auto. No activity found."</string>
+    <string name="projection_devices" msgid="2556503818120676439">"{count,plural, =1{# device}other{# devices}}"</string>
+    <string name="weather_app_name" msgid="4356705068077942048">"Weather"</string>
+    <string name="fake_weather_main_text" msgid="2545755284647327839">"--° Mostly sunny"</string>
+    <string name="fake_weather_footer_text" msgid="8640814250285014485">"Mountain View • H: --° L: --°"</string>
+    <string name="times_separator" msgid="1962841895013564645">"/"</string>
+    <string name="recents_empty_state_text" msgid="8228569970506899117">"No recent items"</string>
+    <string name="recents_clear_all_text" msgid="3594272268167720553">"Clear All"</string>
+    <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App isn\'t available"</string>
+    <string name="calm_mode_title" msgid="4364804976931157567">"Calm mode"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Queue"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media Source"</string>
 </resources>
diff --git a/app/res/values-en-rGB/strings.xml b/app/res/values-en-rGB/strings.xml
index 9c42c4d..1c7025f 100644
--- a/app/res/values-en-rGB/strings.xml
+++ b/app/res/values-en-rGB/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Clear all"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App isn\'t available"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Calm mode"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Queue"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media source"</string>
 </resources>
diff --git a/app/res/values-en-rIN/strings.xml b/app/res/values-en-rIN/strings.xml
index 9c42c4d..1c7025f 100644
--- a/app/res/values-en-rIN/strings.xml
+++ b/app/res/values-en-rIN/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Clear all"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App isn\'t available"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Calm mode"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Queue"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media source"</string>
 </resources>
diff --git a/app/res/values-en-rXC/strings.xml b/app/res/values-en-rXC/strings.xml
index 3a23f19..879e1c5 100644
--- a/app/res/values-en-rXC/strings.xml
+++ b/app/res/values-en-rXC/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‏‏‏‏‎‎‎‎‏‎‏‏‎‏‎‏‏‏‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‎‎‏‎‏‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎Clear All‎‏‎‎‏‎"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‎‏‏‏‎‎App isn\'t available‎‏‎‎‏‎"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‏‏‎‎‎‏‏‏‏‏‏‎Calm mode‎‏‎‎‏‎"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‏‏‏‏‎‏‏‏‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‏‏‏‎‏‏‏‎Queue‎‏‎‎‏‎"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‎‎‎‎‏‎‎‏‎‎‎‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‏‎‏‏‎‎‎‏‏‎Media Source‎‏‎‎‏‎"</string>
 </resources>
diff --git a/app/res/values-es-rUS/strings.xml b/app/res/values-es-rUS/strings.xml
index bc3a90d..f87067d 100644
--- a/app/res/values-es-rUS/strings.xml
+++ b/app/res/values-es-rUS/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Borrar todo"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"La app no está disponible"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modo calma"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Fila"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Fuente multimedia"</string>
 </resources>
diff --git a/app/res/values-es/strings.xml b/app/res/values-es/strings.xml
index 961c95b..31e209c 100644
--- a/app/res/values-es/strings.xml
+++ b/app/res/values-es/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Borrar todo"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"La aplicación no está disponible"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modo Calma"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Cola"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Fuente de contenido multimedia"</string>
 </resources>
diff --git a/app/res/values-et/strings.xml b/app/res/values-et/strings.xml
index db6e3e6..473b29d 100644
--- a/app/res/values-et/strings.xml
+++ b/app/res/values-et/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Kustuta kõik"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Rakendus ei ole saadaval"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Lõõgastusrežiim"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Järjekord"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Meediaallikas"</string>
 </resources>
diff --git a/app/res/values-eu/strings.xml b/app/res/values-eu/strings.xml
index c53b647..aee645e 100644
--- a/app/res/values-eu/strings.xml
+++ b/app/res/values-eu/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Garbitu guztia"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Ez dago erabilgarri aplikazioa"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modu lasaia"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Ilara"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Multimedia-iturburua"</string>
 </resources>
diff --git a/app/res/values-fa/strings.xml b/app/res/values-fa/strings.xml
index c4ceec8..244e395 100644
--- a/app/res/values-fa/strings.xml
+++ b/app/res/values-fa/strings.xml
@@ -19,8 +19,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_title" msgid="1056886619192068947">"راه‌انداز خودرو"</string>
     <string name="default_media_song_title" msgid="7837564242036091946"></string>
-    <string name="tap_for_more_info_text" msgid="4240146824238692769">"برای اطلاعات بیشتر، روی کارت ضربه بزنید"</string>
-    <string name="tap_to_launch_text" msgid="7150379866796152196">"برای راه‌اندازی، روی کارت ضربه بزنید"</string>
+    <string name="tap_for_more_info_text" msgid="4240146824238692769">"برای اطلاعات بیشتر، روی کارت تک‌ضرب بزنید"</string>
+    <string name="tap_to_launch_text" msgid="7150379866796152196">"برای راه‌اندازی، روی کارت تک‌ضرب بزنید"</string>
     <string name="ongoing_call_duration_text_separator" msgid="2140398350095052096">" • "</string>
     <string name="ongoing_call_text" msgid="7160701768924041827">"تماس درحال انجام"</string>
     <string name="dialing_call_text" msgid="3286036311692512894">"درحال شماره‌گیری…"</string>
@@ -31,8 +31,10 @@
     <string name="fake_weather_main_text" msgid="2545755284647327839">"--° بیشتر آفتابی"</string>
     <string name="fake_weather_footer_text" msgid="8640814250285014485">"مانتین ویو • بیشینه: --° کمینه: --°"</string>
     <string name="times_separator" msgid="1962841895013564645">"/"</string>
-    <string name="recents_empty_state_text" msgid="8228569970506899117">"مورد جدیدی وجود ندارد"</string>
+    <string name="recents_empty_state_text" msgid="8228569970506899117">"چیز جدیدی اینجا نیست"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"پاک کردن همه"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"برنامه دردسترس نیست"</string>
-    <string name="calm_mode_title" msgid="4364804976931157567">"حالت آرام"</string>
+    <string name="calm_mode_title" msgid="4364804976931157567">"حالت «آرام»"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"صف پخش"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"منبع رسانه"</string>
 </resources>
diff --git a/app/res/values-fi/strings.xml b/app/res/values-fi/strings.xml
index 8d31ddc..f2be9a3 100644
--- a/app/res/values-fi/strings.xml
+++ b/app/res/values-fi/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Tyhjennä kaikki"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Sovellus ei ole käytettävissä"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Rauhallinen tila"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Jono"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Medialähde"</string>
 </resources>
diff --git a/app/res/values-fr-rCA/strings.xml b/app/res/values-fr-rCA/strings.xml
index e638bd1..ed8adb3 100644
--- a/app/res/values-fr-rCA/strings.xml
+++ b/app/res/values-fr-rCA/strings.xml
@@ -34,6 +34,7 @@
     <string name="recents_empty_state_text" msgid="8228569970506899117">"Aucun élément récent"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Tout effacer"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"L\'application n\'est pas accessible"</string>
-    <!-- no translation found for calm_mode_title (4364804976931157567) -->
-    <skip />
+    <string name="calm_mode_title" msgid="4364804976931157567">"Mode Calme"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"File d\'attente"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Source du contenu multimédia"</string>
 </resources>
diff --git a/app/res/values-fr/strings.xml b/app/res/values-fr/strings.xml
index 8573c01..bee99a1 100644
--- a/app/res/values-fr/strings.xml
+++ b/app/res/values-fr/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Tout effacer"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Appli indisponible"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Mode calme"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"File d\'attente"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Source multimédia"</string>
 </resources>
diff --git a/app/res/values-gl/strings.xml b/app/res/values-gl/strings.xml
index b581569..5183865 100644
--- a/app/res/values-gl/strings.xml
+++ b/app/res/values-gl/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Borrar todo"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"A aplicación non está dispoñible"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modo de calma"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Cola"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Fonte do contido multimedia"</string>
 </resources>
diff --git a/app/res/values-gu/strings.xml b/app/res/values-gu/strings.xml
index ff9c5b7..e04a037 100644
--- a/app/res/values-gu/strings.xml
+++ b/app/res/values-gu/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"બધું સાફ કરો"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ઍપ ઉપલબ્ધ નથી"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"શાંત મોડ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"કતાર"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"મીડિયા સૉર્સ"</string>
 </resources>
diff --git a/app/res/values-hi/strings.xml b/app/res/values-hi/strings.xml
index bbc3ee2..80f72bf 100644
--- a/app/res/values-hi/strings.xml
+++ b/app/res/values-hi/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"सभी मिटाएं"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ऐप्लिकेशन उपलब्ध नहीं है"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"काम (शांत) मोड"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"सूची"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"मीडिया सोर्स"</string>
 </resources>
diff --git a/app/res/values-hr/strings.xml b/app/res/values-hr/strings.xml
index 1c33dff..efc3cb1 100644
--- a/app/res/values-hr/strings.xml
+++ b/app/res/values-hr/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Izbriši sve"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacija nije dostupna"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Način opuštanja"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Red čekanja"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Izvor medija"</string>
 </resources>
diff --git a/app/res/values-hu/strings.xml b/app/res/values-hu/strings.xml
index 4c08fa7..6b62690 100644
--- a/app/res/values-hu/strings.xml
+++ b/app/res/values-hu/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Összes törlése"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Az alkalmazás nem áll rendelkezésre"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Nyugalom mód"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Lejátszási sor"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Médiaforrás"</string>
 </resources>
diff --git a/app/res/values-hy/strings.xml b/app/res/values-hy/strings.xml
index 906fb6b..269c47a 100644
--- a/app/res/values-hy/strings.xml
+++ b/app/res/values-hy/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Ջնջել բոլորը"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Հավելվածը հասանելի չէ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Հանգստի ռեժիմ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Հերթացանկ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Մեդիաֆայլի աղբյուրը"</string>
 </resources>
diff --git a/app/res/values-in/strings.xml b/app/res/values-in/strings.xml
index ab9c491..41544df 100644
--- a/app/res/values-in/strings.xml
+++ b/app/res/values-in/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Hapus Semua"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikasi tidak tersedia"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Mode tenang"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Antrean"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Sumber Media"</string>
 </resources>
diff --git a/app/res/values-is/strings.xml b/app/res/values-is/strings.xml
index 3362bd4..3cc559e 100644
--- a/app/res/values-is/strings.xml
+++ b/app/res/values-is/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Hreinsa allt"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Forritið er ekki í boði"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Róleg stilling"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Röð"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Uppruni efnis"</string>
 </resources>
diff --git a/app/res/values-it/strings.xml b/app/res/values-it/strings.xml
index 15b9890..2c21e8e 100644
--- a/app/res/values-it/strings.xml
+++ b/app/res/values-it/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Cancella tutto"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App non disponibile"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modalità Calma"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Coda"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Fonte di contenuti multimediali"</string>
 </resources>
diff --git a/app/res/values-iw/strings.xml b/app/res/values-iw/strings.xml
index 050d187..d809ed0 100644
--- a/app/res/values-iw/strings.xml
+++ b/app/res/values-iw/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ניקוי הכול"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"האפליקציה לא זמינה"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"מצב רגיעה"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"הבאים בתור"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"מקור המדיה"</string>
 </resources>
diff --git a/app/res/values-ja/strings.xml b/app/res/values-ja/strings.xml
index ce968b5..2a37dd9 100644
--- a/app/res/values-ja/strings.xml
+++ b/app/res/values-ja/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"すべて消去"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"このアプリは使用できません"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Calm モード"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"キュー"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"メディアソース"</string>
 </resources>
diff --git a/app/res/values-ka/strings.xml b/app/res/values-ka/strings.xml
index 9920b17..324a74f 100644
--- a/app/res/values-ka/strings.xml
+++ b/app/res/values-ka/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ყველას გასუფთავება"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"აპი მიუწვდომელია"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"წყნარი რეჟიმი"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"რიგი"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"მედიაწყარო"</string>
 </resources>
diff --git a/app/res/values-kk/strings.xml b/app/res/values-kk/strings.xml
index fe1b8d9..bbfa91a 100644
--- a/app/res/values-kk/strings.xml
+++ b/app/res/values-kk/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Барлығын өшіру"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Қолданба қолжетімді емес."</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Тыныштық режимі"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Кезек"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Медиа дереккөздері"</string>
 </resources>
diff --git a/app/res/values-km/strings.xml b/app/res/values-km/strings.xml
index 0ca0780..9a3b520 100644
--- a/app/res/values-km/strings.xml
+++ b/app/res/values-km/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"សម្អាតទាំងអស់"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"មិន​មាន​កម្មវិធី"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"មុខងារស្ងាត់"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ជួរ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"ប្រភព​មេឌៀ"</string>
 </resources>
diff --git a/app/res/values-kn/strings.xml b/app/res/values-kn/strings.xml
index 2d42f2f..67259ee 100644
--- a/app/res/values-kn/strings.xml
+++ b/app/res/values-kn/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ಎಲ್ಲವನ್ನೂ ಅಳಿಸಿ"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ಆ್ಯಪ್ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"ಶಾಂತ ಮೋಡ್"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ಸರದಿ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"ಮಾಧ್ಯಮದ ಮೂಲ"</string>
 </resources>
diff --git a/app/res/values-ko/strings.xml b/app/res/values-ko/strings.xml
index b85a262..4c5bd05 100644
--- a/app/res/values-ko/strings.xml
+++ b/app/res/values-ko/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"모두 지우기"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"앱을 사용할 수 없음"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"고요 모드"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"현재 재생목록"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"미디어 소스"</string>
 </resources>
diff --git a/app/res/values-ky/strings.xml b/app/res/values-ky/strings.xml
index 5ff6cb1..ebc3ad4 100644
--- a/app/res/values-ky/strings.xml
+++ b/app/res/values-ky/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Баарын тазалоо"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Колдонмо жеткиликтүү эмес"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Тынчтык режими"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Кезек"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Мультимедия булагы"</string>
 </resources>
diff --git a/app/res/values-lo/strings.xml b/app/res/values-lo/strings.xml
index 6cd8049..3aa1070 100644
--- a/app/res/values-lo/strings.xml
+++ b/app/res/values-lo/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ລຶບລ້າງທັງໝົດ"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ແອັບບໍ່ພ້ອມໃຫ້ນຳໃຊ້"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"ໂໝດສະຫງົບ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ຄິວ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"ແຫຼ່ງທີ່ມາຂອງສື່"</string>
 </resources>
diff --git a/app/res/values-lt/strings.xml b/app/res/values-lt/strings.xml
index 1c0d051..917f0ec 100644
--- a/app/res/values-lt/strings.xml
+++ b/app/res/values-lt/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Išvalyti viską"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Programa nepasiekiama"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Ramybės režimas"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Eilė"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Medijos šaltinis"</string>
 </resources>
diff --git a/app/res/values-lv/strings.xml b/app/res/values-lv/strings.xml
index 4d8b539..5bef76d 100644
--- a/app/res/values-lv/strings.xml
+++ b/app/res/values-lv/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Notīrīt visu"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Lietotne nav pieejama"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Miera režīms"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Rinda"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Multivides avots"</string>
 </resources>
diff --git a/app/res/values-mk/strings.xml b/app/res/values-mk/strings.xml
index a7f5d5c..a27ca54 100644
--- a/app/res/values-mk/strings.xml
+++ b/app/res/values-mk/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Избриши ги сите"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Апликацијата не е достапна"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Режим на мирување"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Редица"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Извор на аудиовизуелни содржини"</string>
 </resources>
diff --git a/app/res/values-ml/strings.xml b/app/res/values-ml/strings.xml
index c65d5bb..4149487 100644
--- a/app/res/values-ml/strings.xml
+++ b/app/res/values-ml/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"എല്ലാം മായ്‌ക്കുക"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ആപ്പ് ലഭ്യമല്ല"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"\'ശാന്തം\' മോഡ്"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ക്യൂ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"മീഡിയാ ഉറവിടം"</string>
 </resources>
diff --git a/app/res/values-mn/strings.xml b/app/res/values-mn/strings.xml
index 7a0ec37..7b38c04 100644
--- a/app/res/values-mn/strings.xml
+++ b/app/res/values-mn/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Бүгдийг арилгах"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Апп боломжгүй байна"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Тайван горим"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Дараалал"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Медиагийн эх сурвалж"</string>
 </resources>
diff --git a/app/res/values-mr/strings.xml b/app/res/values-mr/strings.xml
index dc2bce3..4b16e02 100644
--- a/app/res/values-mr/strings.xml
+++ b/app/res/values-mr/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"सर्व साफ करा"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"अ‍ॅप उपलब्ध नाही"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"शांत मोड"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"क्यू"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"मीडिया स्रोत"</string>
 </resources>
diff --git a/app/res/values-ms/strings.xml b/app/res/values-ms/strings.xml
index fe43262..a5c1f39 100644
--- a/app/res/values-ms/strings.xml
+++ b/app/res/values-ms/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Kosongkan Semua"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Apl tidak tersedia"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Mod Calm"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Baris gilir"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Sumber Media"</string>
 </resources>
diff --git a/app/res/values-my/strings.xml b/app/res/values-my/strings.xml
index 57b5005..4401c9f 100644
--- a/app/res/values-my/strings.xml
+++ b/app/res/values-my/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"အားလုံးရှင်းရန်"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"အက်ပ် မရနိုင်ပါ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"အငြိမ်မုဒ်"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"စာရင်းစဉ်"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"မီဒီယာရင်းမြစ်"</string>
 </resources>
diff --git a/app/res/values-nb/strings.xml b/app/res/values-nb/strings.xml
index e9f4b4a..83901e8 100644
--- a/app/res/values-nb/strings.xml
+++ b/app/res/values-nb/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Fjern alt"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Appen er ikke tilgjengelig"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Roligmodus"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Kø"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Mediekilde"</string>
 </resources>
diff --git a/app/res/values-ne/strings.xml b/app/res/values-ne/strings.xml
index 4053971..d70c2d2 100644
--- a/app/res/values-ne/strings.xml
+++ b/app/res/values-ne/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"सबै सामग्री मेटाउनुहोस्"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"एप उपलब्ध छैन"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"शान्त मोड"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"लाइन"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"मिडियाको स्रोत"</string>
 </resources>
diff --git a/app/res/values-nl/strings.xml b/app/res/values-nl/strings.xml
index 86a9cdb..57fabfa 100644
--- a/app/res/values-nl/strings.xml
+++ b/app/res/values-nl/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Alles wissen"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"App is niet beschikbaar"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Kalme modus"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Wachtrij"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Mediabron"</string>
 </resources>
diff --git a/app/res/values-or/strings.xml b/app/res/values-or/strings.xml
index 410bac8..0bb6c16 100644
--- a/app/res/values-or/strings.xml
+++ b/app/res/values-or/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ସବୁ ଖାଲି କରନ୍ତୁ"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ଆପ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"ଶାନ୍ତ ମୋଡ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ଧାଡ଼ି"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"ମିଡିଆ ସୋର୍ସ"</string>
 </resources>
diff --git a/app/res/values-pa/strings.xml b/app/res/values-pa/strings.xml
index 1e52c85..8659649 100644
--- a/app/res/values-pa/strings.xml
+++ b/app/res/values-pa/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ਸਭ ਕਲੀਅਰ ਕਰੋ"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ਐਪ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"ਸ਼ਾਂਤ ਮੋਡ"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"ਕਤਾਰ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"ਮੀਡੀਆ ਸਰੋਤ"</string>
 </resources>
diff --git a/app/res/values-pl/strings.xml b/app/res/values-pl/strings.xml
index 3fbe11a..da3000c 100644
--- a/app/res/values-pl/strings.xml
+++ b/app/res/values-pl/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Wyczyść wszystko"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacja jest niedostępna"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Tryb cichy"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Kolejka"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Źródło multimediów"</string>
 </resources>
diff --git a/app/res/values-pt-rPT/strings.xml b/app/res/values-pt-rPT/strings.xml
index ff491a2..a3f7bd2 100644
--- a/app/res/values-pt-rPT/strings.xml
+++ b/app/res/values-pt-rPT/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Limpar tudo"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"A app não está disponível"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modo Calm"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Fila"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Origem de multimédia"</string>
 </resources>
diff --git a/app/res/values-pt/strings.xml b/app/res/values-pt/strings.xml
index daa7dd9..6c90235 100644
--- a/app/res/values-pt/strings.xml
+++ b/app/res/values-pt/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Limpar tudo"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"O app não está disponível"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modo foco"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Fila"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Fonte de mídia"</string>
 </resources>
diff --git a/app/res/values-ro/strings.xml b/app/res/values-ro/strings.xml
index 5d46cd8..0880f88 100644
--- a/app/res/values-ro/strings.xml
+++ b/app/res/values-ro/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Șterge tot"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplicația nu este disponibilă"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modul Calm"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Coadă"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Sursă media"</string>
 </resources>
diff --git a/app/res/values-ru/strings.xml b/app/res/values-ru/strings.xml
index 33dc160..4b5409d 100644
--- a/app/res/values-ru/strings.xml
+++ b/app/res/values-ru/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Очистить"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Приложение недоступно"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Режим покоя"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Очередь"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Источник мультимедиа"</string>
 </resources>
diff --git a/app/res/values-si/strings.xml b/app/res/values-si/strings.xml
index c18a55c..77bf213 100644
--- a/app/res/values-si/strings.xml
+++ b/app/res/values-si/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"සියල්ල හිස් කරන්න"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"යෙදුම නොතිබේ"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"සන්සුන් ප්‍රකාරය"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"පෝලිම"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"මාධ්‍ය ප්‍රභවය"</string>
 </resources>
diff --git a/app/res/values-sk/strings.xml b/app/res/values-sk/strings.xml
index 46f6548..14839ef 100644
--- a/app/res/values-sk/strings.xml
+++ b/app/res/values-sk/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Vymazať všetko"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikácia nie je k dispozícii"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Pokojný režim"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Poradie"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Zdroj médií"</string>
 </resources>
diff --git a/app/res/values-sl/strings.xml b/app/res/values-sl/strings.xml
index 9a408fc..66a86ff 100644
--- a/app/res/values-sl/strings.xml
+++ b/app/res/values-sl/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Izbriši vse"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacija ni na voljo"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Umirjeni način"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Čakalna vrsta"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Vir predstavnosti"</string>
 </resources>
diff --git a/app/res/values-sq/strings.xml b/app/res/values-sq/strings.xml
index 3d7d39b..2d5b5f9 100644
--- a/app/res/values-sq/strings.xml
+++ b/app/res/values-sq/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Pastro të gjitha"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Aplikacioni nuk ofrohet"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Modaliteti i qetësisë"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Radha"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Burimi i medias"</string>
 </resources>
diff --git a/app/res/values-sr/strings.xml b/app/res/values-sr/strings.xml
index 3a3ae8f..33e5e34 100644
--- a/app/res/values-sr/strings.xml
+++ b/app/res/values-sr/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Обриши све"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Апликација није доступна"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Режим опуштања"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Редослед"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Извор медија"</string>
 </resources>
diff --git a/app/res/values-sv/strings.xml b/app/res/values-sv/strings.xml
index af06154..31e5d96 100644
--- a/app/res/values-sv/strings.xml
+++ b/app/res/values-sv/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Rensa allt"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Appen är inte tillgänglig"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Lugnt läge"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Kö"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Mediekälla"</string>
 </resources>
diff --git a/app/res/values-sw/strings.xml b/app/res/values-sw/strings.xml
index 9e71a9d..0e4b207 100644
--- a/app/res/values-sw/strings.xml
+++ b/app/res/values-sw/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Futa Zote"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Programu haipatikani"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Hali ya utulivu"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Foleni"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Chanzo cha Maudhui"</string>
 </resources>
diff --git a/app/res/values-ta/strings.xml b/app/res/values-ta/strings.xml
index fb29415..633389b 100644
--- a/app/res/values-ta/strings.xml
+++ b/app/res/values-ta/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"அனைத்தையும் அழி"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ஆப்ஸ் கிடைக்கவில்லை"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"அமைதிப் பயன்முறை"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"வரிசை"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"மீடியா ஆதாரம்"</string>
 </resources>
diff --git a/app/res/values-te/strings.xml b/app/res/values-te/strings.xml
index 887b13f..08b5b89 100644
--- a/app/res/values-te/strings.xml
+++ b/app/res/values-te/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"అన్నీ తీసివేయండి"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"యాప్ అందుబాటులో లేదు"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"క్లెయిమ్ మోడ్"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"క్యూ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"మీడియా సోర్స్"</string>
 </resources>
diff --git a/app/res/values-th/strings.xml b/app/res/values-th/strings.xml
index f84e327..f3c88ed 100644
--- a/app/res/values-th/strings.xml
+++ b/app/res/values-th/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"ล้างทั้งหมด"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"แอปไม่พร้อมใช้งาน"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"โหมด Calm"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"คิว"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"แหล่งที่มาของสื่อ"</string>
 </resources>
diff --git a/app/res/values-tl/strings.xml b/app/res/values-tl/strings.xml
index 3c76a8b..f391d00 100644
--- a/app/res/values-tl/strings.xml
+++ b/app/res/values-tl/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"I-clear Lahat"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Hindi available ang app"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Calm mode"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Queue"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Source ng Media"</string>
 </resources>
diff --git a/app/res/values-tr/strings.xml b/app/res/values-tr/strings.xml
index eecca89..fed16ee 100644
--- a/app/res/values-tr/strings.xml
+++ b/app/res/values-tr/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Tümünü Temizle"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Uygulama kullanılamıyor"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Sakin mod"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Sıra"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Medya Kaynağı"</string>
 </resources>
diff --git a/app/res/values-uk/strings.xml b/app/res/values-uk/strings.xml
index 1aa273e..c4768a6 100644
--- a/app/res/values-uk/strings.xml
+++ b/app/res/values-uk/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Очистити все"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Додаток недоступний"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Спокійний режим"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Черга"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Джерело мультимедіа"</string>
 </resources>
diff --git a/app/res/values-ur/strings.xml b/app/res/values-ur/strings.xml
index 230dc64..d720ba9 100644
--- a/app/res/values-ur/strings.xml
+++ b/app/res/values-ur/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"تمام صاف کریں"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"ایپ دستیاب نہیں ہے"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"پُرسکون وضع"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"قطار"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"میڈیا کا ماخذ"</string>
 </resources>
diff --git a/app/res/values-uz/strings.xml b/app/res/values-uz/strings.xml
index 97bc0cc..651ea49 100644
--- a/app/res/values-uz/strings.xml
+++ b/app/res/values-uz/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Hammasini yopish"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Ilova mavjud emas"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Dam olish rejimi"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Navbat"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Media manbasi"</string>
 </resources>
diff --git a/app/res/values-vi/strings.xml b/app/res/values-vi/strings.xml
index 8e84aae..347cf9e 100644
--- a/app/res/values-vi/strings.xml
+++ b/app/res/values-vi/strings.xml
@@ -34,6 +34,7 @@
     <string name="recents_empty_state_text" msgid="8228569970506899117">"Không có mục nào gần đây"</string>
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Xoá tất cả"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"Hiện không có ứng dụng"</string>
-    <!-- no translation found for calm_mode_title (4364804976931157567) -->
-    <skip />
+    <string name="calm_mode_title" msgid="4364804976931157567">"Chế độ Tĩnh lặng"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Danh sách chờ"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Nguồn nội dung nghe nhìn"</string>
 </resources>
diff --git a/app/res/values-zh-rCN/strings.xml b/app/res/values-zh-rCN/strings.xml
index 1992d3b..c88a3bd 100644
--- a/app/res/values-zh-rCN/strings.xml
+++ b/app/res/values-zh-rCN/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"全部清除"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"应用无法打开"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"平静模式"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"队列"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"媒体来源"</string>
 </resources>
diff --git a/app/res/values-zh-rHK/strings.xml b/app/res/values-zh-rHK/strings.xml
index 031c0af..76fa14f 100644
--- a/app/res/values-zh-rHK/strings.xml
+++ b/app/res/values-zh-rHK/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"全部清除"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"目前無法使用這個應用程式"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"平靜模式"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"序列"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"媒體來源"</string>
 </resources>
diff --git a/app/res/values-zh-rTW/strings.xml b/app/res/values-zh-rTW/strings.xml
index 128f739..44faf22 100644
--- a/app/res/values-zh-rTW/strings.xml
+++ b/app/res/values-zh-rTW/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"全部清除"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"應用程式目前無法使用"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"平靜模式"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"待播清單"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"媒體來源"</string>
 </resources>
diff --git a/app/res/values-zu/strings.xml b/app/res/values-zu/strings.xml
index 7abc29f..a5557e5 100644
--- a/app/res/values-zu/strings.xml
+++ b/app/res/values-zu/strings.xml
@@ -35,4 +35,6 @@
     <string name="recents_clear_all_text" msgid="3594272268167720553">"Sula Konke"</string>
     <string name="failure_opening_recent_task_message" msgid="963567570097465902">"I-app ayitholakali"</string>
     <string name="calm_mode_title" msgid="4364804976931157567">"Imodi ezolile"</string>
+    <string name="media_card_queue_header_title" msgid="8801994125708995575">"Ulayini"</string>
+    <string name="media_card_history_header_title" msgid="8337396297165848931">"Umthombo Wemidiya"</string>
 </resources>
diff --git a/app/res/values/colors.xml b/app/res/values/colors.xml
index 954c3cf..d741960 100644
--- a/app/res/values/colors.xml
+++ b/app/res/values/colors.xml
@@ -15,15 +15,17 @@
 -->
 <resources>
     <color name="date_divider_bar_color">@*android:color/car_grey_500</color>
-    <color name="media_button_tint">@*android:color/car_tint</color>
     <color name="card_background_scrim">#11151B</color>
     <color name="tap_for_more_text_color">#DADCE0</color>
     <color name="dialer_button_icon_color">#FFFFFF</color>
     <color name="dialer_end_call_button_color">#EE675C</color>
-    <color name="minimized_progress_bar_background">#5CFFFFFF</color>
     <color name="launcher_home_icon_color">@*android:color/car_accent_light</color>
     <color name="seek_bar_color">@*android:color/car_accent</color>
     <color name="recents_background_color">@*android:color/car_grey_900</color>
     <color name="default_recents_thumbnail_color">@*android:color/car_grey_846</color>
     <color name="clear_all_recents_text_color">@*android:color/car_accent</color>
+
+    <!-- CarUiPortraitLauncherReferenceRRO relies on overlaying these values -->
+    <color name="media_button_tint">@*android:color/car_tint</color>
+    <color name="minimized_progress_bar_background">#5CFFFFFF</color>
 </resources>
diff --git a/app/res/values/config.xml b/app/res/values/config.xml
index 1bf9bae..bd4bbba 100644
--- a/app/res/values/config.xml
+++ b/app/res/values/config.xml
@@ -61,22 +61,13 @@
     <string-array name="config_taskViewPackages" translatable="false">
     </string-array>
 
-    <!--
-        Determines whether or not to use the RemoteCarTaskView from car-lib instead of the
-        CarTaskView that exists inside the CarLauncher.
-    -->
-    <bool name="config_useRemoteCarTaskView">true</bool>
-
-    <!--
-        The Activity to use as a passenger Launcher, if empty, it assumes CarLauncher can do
-        the passenger Launcher role too.
-    -->
-    <string name="config_passengerLauncherComponent">com.android.car.multidisplay/.launcher.LauncherActivity</string>
-
     <!-- Boolean value to indicate if the secondary descriptive text of homescreen cards
          without controls should have multiple lines -->
     <bool name="config_homecard_single_line_secondary_descriptive_text">true</bool>
 
+    <!-- Boolean value to indicate if the recents activity should open the most recent task on dismiss. -->
+    <bool name="config_launch_most_recent_task_on_recents_dismiss">true</bool>
+
     <!-- Config values for Calm mode and information shown on the Calm mode screen -->
     <bool name="config_enableCalmMode">true</bool>
     <bool name="config_calmMode_showClock">true</bool>
@@ -84,7 +75,6 @@
     <bool name="config_calmMode_showNavigation">true</bool>
     <bool name="config_calmMode_showDate">true</bool>
     <bool name="config_calmMode_showTemperature">true</bool>
-    <string name="config_calmMode_packageName">com.android.car.carlauncher</string>
-    <string name="config_calmMode_activityName">com.android.car.carlauncher.calmmode.CalmModeActivity</string>
+    <string name="config_calmMode_componentName">com.android.car.carlauncher/com.android.car.carlauncher.calmmode.CalmModeActivity</string>
 
 </resources>
diff --git a/app/res/values/dimens.xml b/app/res/values/dimens.xml
index 7398e4b..48c8722 100644
--- a/app/res/values/dimens.xml
+++ b/app/res/values/dimens.xml
@@ -29,7 +29,7 @@
     <dimen name="main_screen_widget_margin">16dp</dimen>
 
     <!-- Card dimensions -->
-    <dimen name="card_width">388dp</dimen>
+    <dimen name="card_width">400dp</dimen>
 
     <!-- Card Header dimensions -->
     <dimen name="card_content_margin">24dp</dimen>
@@ -42,8 +42,8 @@
     <dimen name="descriptive_text_with_controls_top_margin">24dp</dimen>
     <!-- Percent transparency of the scrim applied to the image used for the card's background as a float between 0 and 1, where 0 applies no darkening scrim-->
     <dimen name="card_background_scrim_alpha" format="float">0.72</dimen>
-    <!--Percent by which to blur the image used for the card's background as a float between 0 and 1, where 0 is not blurred-->
-    <dimen name="card_background_image_blur_radius" format="float">0.36</dimen>
+    <!--Blur radius for the RenderEffect for the card's background image as a float, where 0 is not blurred -->
+    <dimen name="card_background_image_blur_radius" format="float">25</dimen>
 
     <!-- card_content_descriptive_text dimensions -->
     <dimen name="card_content_image_size">76dp</dimen>
@@ -88,9 +88,55 @@
     <dimen name="recent_task_width">432dp</dimen>
 
     <!-- Calm mode sizes -->
-    <dimen name="calm_mode_padding">12dp</dimen>
+    <dimen name="calm_mode_padding_vertical">40dp</dimen>
+    <dimen name="calm_mode_padding_horizontal">12dp</dimen>
     <dimen name="calm_mode_icon_size_regular">32dp</dimen>
     <dimen name="calm_mode_icon_size_small">16dp</dimen>
-    <dimen name="calm_mode_icon_margin">4dp</dimen>
+    <dimen name="calm_mode_icon_margin">24dp</dimen>
     <dimen name="calm_mode_text_size">24sp</dimen>
+    <dimen name="calm_mode_clock_translationY">40dp</dimen>
+    <dimen name="calm_mode_clock_media_title_margin">-16dp</dimen>
+
+    <!-- Home fullscreen media card dimens -->
+    <dimen name="media_card_large_button_icon_padding">25dp</dimen>
+    <dimen name="media_card_panel_button_icon_padding">12dp</dimen>
+    <dimen name="media_card_app_icon_size">32dp</dimen>
+    <dimen name="media_card_horizontal_margin">32dp</dimen>
+    <dimen name="media_card_view_separation_margin">16dp</dimen>
+    <dimen name="media_card_artist_top_margin">4dp</dimen>
+    <dimen name="media_card_album_art_size">200dp</dimen>
+    <dimen name="media_card_album_art_end_margin">96dp</dimen>
+    <item name="media_card_album_art_drawable_corner_ratio" format="float" type="dimen">0.08</item>
+    <dimen name="media_card_logo_size">32dp</dimen>
+    <dimen name="media_card_small_button_size">40dp</dimen>
+    <dimen name="media_card_large_button_size">80dp</dimen>
+    <dimen name="media_card_bottom_panel_button_size">56dp</dimen>
+    <dimen name="media_card_bottom_panel_height">96dp</dimen>
+    <dimen name="media_card_card_radius">32dp</dimen>
+    <dimen name="media_card_play_button_bottom_margin">120dp</dimen>
+    <dimen name="media_card_play_button_horizontal_margin">8dp</dimen>
+    <dimen name="media_card_margin_panel_open">24dp</dimen>
+    <dimen name="media_card_bottom_panel_margin_top">208dp</dimen>
+    <dimen name="media_card_bottom_panel_animated_final_position">128dp</dimen>
+    <dimen name="media_card_panel_handlebar_offscreen_start_position">1000dp</dimen>
+    <dimen name="media_card_bottom_panel_animated_size">@dimen/media_card_bottom_panel_button_size</dimen>
+    <dimen name="media_card_bottom_panel_animated_horizontal_margin">0dp</dimen>
+    <dimen name="media_card_pill_radius">160dp</dimen>
+    <dimen name="media_card_panel_handlebar_height">8dp</dimen>
+    <dimen name="media_card_panel_handlebar_touch_target_height">60dp</dimen>
+    <dimen name="media_card_panel_handlebar_horizontal_padding">164dp</dimen>
+    <dimen name="media_card_queue_header_app_icon_size">26dp</dimen>
+    <dimen name="media_card_queue_item_thumbnail_size">80dp</dimen>
+    <dimen name="media_card_panel_content_margin_top">216dp</dimen>
+    <dimen name="media_card_title_animated_line_height">36dp</dimen>
+    <dimen name="media_card_title_default_line_height">40dp</dimen>
+    <dimen name="media_card_title_animated_text_size">28sp</dimen>
+    <dimen name="media_card_title_default_text_size">32sp</dimen>
+    <dimen name="media_card_recycler_view_fading_edge_length">80dp</dimen>
+    <dimen name="media_card_history_item_height">112dp</dimen>
+    <dimen name="media_card_history_item_vertical_margin">16dp</dimen>
+    <dimen name="media_card_history_item_icon_size">80dp</dimen>
+    <dimen name="media_card_history_item_thumbnail_size">32dp</dimen>
+    <dimen name="media_card_view_header_icon_size">32dp</dimen>
+    <dimen name="media_card_text_view_guideline_start">248dp</dimen>
 </resources>
diff --git a/app/res/values/integers.xml b/app/res/values/integers.xml
index bddc3f9..cbd5746 100644
--- a/app/res/values/integers.xml
+++ b/app/res/values/integers.xml
@@ -20,4 +20,11 @@
 
     <!-- Max for seekbar progress -->
     <integer name="optional_seekbar_max">1000</integer>
+
+    <!-- Calm mode animation duration in ms -->
+    <integer name="calm_mode_activity_fade_duration">500</integer>
+    <integer name="calm_mode_content_fade_duration">750</integer>
+
+    <!-- Fullscreen media card animation duration in ms -->
+    <integer name="media_card_bottom_panel_open_duration">400</integer>
 </resources>
diff --git a/app/res/values/overlayable.xml b/app/res/values/overlayable.xml
index 8a71715..d4c72ef 100644
--- a/app/res/values/overlayable.xml
+++ b/app/res/values/overlayable.xml
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- Copyright (C) 2023 The Android Open Source Project
+<!-- Copyright (C) 2024 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
@@ -16,7 +16,10 @@
 <resources>
   <overlayable name="CarLauncher">
     <policy type="system|product|signature">
+      <item type="animator" name="calm_mode_enter"/>
       <item type="animator" name="recents_clear_all"/>
+      <item type="anim" name="fade_in"/>
+      <item type="anim" name="fade_out"/>
       <item type="array" name="config_homeCardModuleClasses"/>
       <item type="array" name="config_homeCardPreferredMapActivities"/>
       <item type="array" name="config_taskViewPackages"/>
@@ -35,7 +38,7 @@
       <item type="bool" name="config_calmMode_showTemperature"/>
       <item type="bool" name="config_enableCalmMode"/>
       <item type="bool" name="config_homecard_single_line_secondary_descriptive_text"/>
-      <item type="bool" name="config_useRemoteCarTaskView"/>
+      <item type="bool" name="config_launch_most_recent_task_on_recents_dismiss"/>
       <item type="bool" name="show_seek_bar"/>
       <item type="bool" name="use_media_source_color_for_seek_bar"/>
       <item type="color" name="card_background_scrim"/>
@@ -47,6 +50,9 @@
       <item type="color" name="dialer_icon_tint_state_list"/>
       <item type="color" name="launcher_home_icon_color"/>
       <item type="color" name="media_button_tint"/>
+      <item type="color" name="media_card_panel_button_background_tint_state_list"/>
+      <item type="color" name="media_card_panel_button_tint_state_list"/>
+      <item type="color" name="media_card_seekbar_thumb_color"/>
       <item type="color" name="minimized_progress_bar_background"/>
       <item type="color" name="recents_background_color"/>
       <item type="color" name="seek_bar_color"/>
@@ -55,10 +61,13 @@
       <item type="dimen" name="button_tap_target_icon_padding"/>
       <item type="dimen" name="button_tap_target_size"/>
       <item type="dimen" name="button_trio_margin"/>
+      <item type="dimen" name="calm_mode_clock_media_title_margin"/>
+      <item type="dimen" name="calm_mode_clock_translationY"/>
       <item type="dimen" name="calm_mode_icon_margin"/>
       <item type="dimen" name="calm_mode_icon_size_regular"/>
       <item type="dimen" name="calm_mode_icon_size_small"/>
-      <item type="dimen" name="calm_mode_padding"/>
+      <item type="dimen" name="calm_mode_padding_horizontal"/>
+      <item type="dimen" name="calm_mode_padding_vertical"/>
       <item type="dimen" name="calm_mode_text_size"/>
       <item type="dimen" name="card_background_image_blur_radius"/>
       <item type="dimen" name="card_background_scrim_alpha"/>
@@ -78,6 +87,47 @@
       <item type="dimen" name="horizontal_border_size"/>
       <item type="dimen" name="launcher_card_corner_radius"/>
       <item type="dimen" name="main_screen_widget_margin"/>
+      <item type="dimen" name="media_card_album_art_drawable_corner_ratio"/>
+      <item type="dimen" name="media_card_album_art_end_margin"/>
+      <item type="dimen" name="media_card_album_art_size"/>
+      <item type="dimen" name="media_card_app_icon_size"/>
+      <item type="dimen" name="media_card_artist_top_margin"/>
+      <item type="dimen" name="media_card_bottom_panel_animated_final_position"/>
+      <item type="dimen" name="media_card_bottom_panel_animated_horizontal_margin"/>
+      <item type="dimen" name="media_card_bottom_panel_animated_size"/>
+      <item type="dimen" name="media_card_bottom_panel_button_size"/>
+      <item type="dimen" name="media_card_bottom_panel_height"/>
+      <item type="dimen" name="media_card_bottom_panel_margin_top"/>
+      <item type="dimen" name="media_card_card_radius"/>
+      <item type="dimen" name="media_card_history_item_height"/>
+      <item type="dimen" name="media_card_history_item_icon_size"/>
+      <item type="dimen" name="media_card_history_item_thumbnail_size"/>
+      <item type="dimen" name="media_card_history_item_vertical_margin"/>
+      <item type="dimen" name="media_card_horizontal_margin"/>
+      <item type="dimen" name="media_card_large_button_icon_padding"/>
+      <item type="dimen" name="media_card_large_button_size"/>
+      <item type="dimen" name="media_card_logo_size"/>
+      <item type="dimen" name="media_card_margin_panel_open"/>
+      <item type="dimen" name="media_card_panel_button_icon_padding"/>
+      <item type="dimen" name="media_card_panel_content_margin_top"/>
+      <item type="dimen" name="media_card_panel_handlebar_height"/>
+      <item type="dimen" name="media_card_panel_handlebar_horizontal_padding"/>
+      <item type="dimen" name="media_card_panel_handlebar_offscreen_start_position"/>
+      <item type="dimen" name="media_card_panel_handlebar_touch_target_height"/>
+      <item type="dimen" name="media_card_pill_radius"/>
+      <item type="dimen" name="media_card_play_button_bottom_margin"/>
+      <item type="dimen" name="media_card_play_button_horizontal_margin"/>
+      <item type="dimen" name="media_card_queue_header_app_icon_size"/>
+      <item type="dimen" name="media_card_queue_item_thumbnail_size"/>
+      <item type="dimen" name="media_card_recycler_view_fading_edge_length"/>
+      <item type="dimen" name="media_card_small_button_size"/>
+      <item type="dimen" name="media_card_text_view_guideline_start"/>
+      <item type="dimen" name="media_card_title_animated_line_height"/>
+      <item type="dimen" name="media_card_title_animated_text_size"/>
+      <item type="dimen" name="media_card_title_default_line_height"/>
+      <item type="dimen" name="media_card_title_default_text_size"/>
+      <item type="dimen" name="media_card_view_header_icon_size"/>
+      <item type="dimen" name="media_card_view_separation_margin"/>
       <item type="dimen" name="media_top_margin"/>
       <item type="dimen" name="playback_controls_margin"/>
       <item type="dimen" name="recent_task_col_space"/>
@@ -91,33 +141,50 @@
       <item type="dimen" name="tap_text_margin"/>
       <item type="dimen" name="text_block_top_margin"/>
       <item type="dimen" name="vertical_border_size"/>
+      <item type="drawable" name="button_ripple"/>
       <item type="drawable" name="car_button_background"/>
+      <item type="drawable" name="circle_button_background"/>
       <item type="drawable" name="control_bar_contact_image_background"/>
       <item type="drawable" name="control_bar_image_background"/>
       <item type="drawable" name="default_audio_background"/>
       <item type="drawable" name="dialer_button_active_state_circle"/>
+      <item type="drawable" name="divider"/>
+      <item type="drawable" name="empty_action_drawable"/>
       <item type="drawable" name="ic_apps_black"/>
       <item type="drawable" name="ic_arrow_back_black"/>
       <item type="drawable" name="ic_call_end"/>
       <item type="drawable" name="ic_call_end_button"/>
       <item type="drawable" name="ic_clear_black"/>
       <item type="drawable" name="ic_dialpad"/>
+      <item type="drawable" name="ic_history"/>
       <item type="drawable" name="ic_launcher_home"/>
       <item type="drawable" name="ic_media"/>
       <item type="drawable" name="ic_mic_on"/>
       <item type="drawable" name="ic_mute_activatable"/>
       <item type="drawable" name="ic_navigation"/>
+      <item type="drawable" name="ic_overflow_horizontal"/>
+      <item type="drawable" name="ic_play_pause_selector"/>
+      <item type="drawable" name="ic_queue"/>
       <item type="drawable" name="ic_recent_dismiss"/>
       <item type="drawable" name="ic_search_black"/>
       <item type="drawable" name="ic_temperature"/>
+      <item type="drawable" name="media_card_button_panel_background"/>
+      <item type="drawable" name="media_card_panel_button_shape"/>
+      <item type="drawable" name="media_card_panel_handlebar"/>
+      <item type="drawable" name="media_card_seekbar_progress"/>
+      <item type="drawable" name="media_card_seekbar_thumb"/>
+      <item type="drawable" name="pill_button_shape"/>
+      <item type="drawable" name="radius_16_background"/>
+      <item type="drawable" name="radius_8_background"/>
       <item type="drawable" name="recent_clear_all_button_background"/>
       <item type="drawable" name="recent_dismiss_button_background"/>
-      <item type="id" name="barrier"/>
+      <item type="id" name="album_art"/>
       <item type="id" name="bottom_card"/>
       <item type="id" name="bottom_edge"/>
       <item type="id" name="bottom_line"/>
       <item type="id" name="button_center"/>
       <item type="id" name="button_left"/>
+      <item type="id" name="button_panel_background"/>
       <item type="id" name="button_right"/>
       <item type="id" name="button_trio"/>
       <item type="id" name="calm_mode_container"/>
@@ -128,22 +195,42 @@
       <item type="id" name="card_name"/>
       <item type="id" name="card_view"/>
       <item type="id" name="clock"/>
+      <item type="id" name="content_format"/>
       <item type="id" name="date"/>
-      <item type="id" name="date_and_temperature_container"/>
       <item type="id" name="descriptive_text_layout"/>
       <item type="id" name="descriptive_text_with_controls_layout"/>
       <item type="id" name="divider_horizontal"/>
+      <item type="id" name="empty_panel"/>
       <item type="id" name="empty_state"/>
       <item type="id" name="end_edge"/>
       <item type="id" name="fragment_container_view"/>
+      <item type="id" name="guideline"/>
+      <item type="id" name="header_app_icon"/>
+      <item type="id" name="history_button"/>
+      <item type="id" name="history_card_album_art"/>
+      <item type="id" name="history_card_app_thumbnail"/>
+      <item type="id" name="history_card_app_title_inactive"/>
+      <item type="id" name="history_card_container_active"/>
+      <item type="id" name="history_card_container_inactive"/>
+      <item type="id" name="history_card_header_icon"/>
+      <item type="id" name="history_card_history_header_title_view"/>
+      <item type="id" name="history_card_subtitle_active"/>
+      <item type="id" name="history_card_title_active"/>
+      <item type="id" name="history_item_app_icon_inactive"/>
+      <item type="id" name="history_list"/>
+      <item type="id" name="history_list_container"/>
+      <item type="id" name="in_call_fragment_container"/>
       <item type="id" name="maps_card"/>
+      <item type="id" name="media_card_panel_content_container"/>
+      <item type="id" name="media_card_panel_handlebar"/>
+      <item type="id" name="media_card_queue_header_title_view"/>
       <item type="id" name="media_descriptive_text"/>
-      <item type="id" name="media_group"/>
-      <item type="id" name="media_icon"/>
+      <item type="id" name="media_fragment_container"/>
       <item type="id" name="media_layout"/>
       <item type="id" name="media_playback_controls_bar"/>
       <item type="id" name="media_title"/>
-      <item type="id" name="nav_group"/>
+      <item type="id" name="media_widget_app_icon"/>
+      <item type="id" name="motion_layout"/>
       <item type="id" name="nav_state"/>
       <item type="id" name="nav_state_icon"/>
       <item type="id" name="optional_image"/>
@@ -153,32 +240,56 @@
       <item type="id" name="optional_timer"/>
       <item type="id" name="optional_timer_separator"/>
       <item type="id" name="optional_times"/>
+      <item type="id" name="overflow_button"/>
+      <item type="id" name="overflow_grid"/>
+      <item type="id" name="play_pause_button"/>
+      <item type="id" name="playback_action_id1"/>
+      <item type="id" name="playback_action_id10"/>
+      <item type="id" name="playback_action_id2"/>
+      <item type="id" name="playback_action_id3"/>
+      <item type="id" name="playback_action_id4"/>
+      <item type="id" name="playback_action_id5"/>
+      <item type="id" name="playback_action_id6"/>
+      <item type="id" name="playback_action_id7"/>
+      <item type="id" name="playback_action_id8"/>
+      <item type="id" name="playback_action_id9"/>
+      <item type="id" name="playback_seek_bar"/>
       <item type="id" name="primary_text"/>
+      <item type="id" name="queue_button"/>
+      <item type="id" name="queue_list"/>
+      <item type="id" name="queue_list_container"/>
+      <item type="id" name="queue_list_item_subtitle"/>
+      <item type="id" name="queue_list_item_title"/>
       <item type="id" name="recent_tasks_group"/>
       <item type="id" name="recent_tasks_list"/>
       <item type="id" name="recent_tasks_list_focus_area"/>
       <item type="id" name="recents_clear_all_button"/>
       <item type="id" name="secondary_text"/>
       <item type="id" name="start_edge"/>
+      <item type="id" name="subtitle"/>
       <item type="id" name="tap_for_more_text"/>
       <item type="id" name="task_dismiss_button"/>
       <item type="id" name="task_icon"/>
       <item type="id" name="task_thumbnail"/>
       <item type="id" name="temperature"/>
-      <item type="id" name="temperature_group"/>
       <item type="id" name="temperature_icon"/>
       <item type="id" name="text_block"/>
       <item type="id" name="text_block_layout"/>
+      <item type="id" name="thumbnail"/>
+      <item type="id" name="title"/>
       <item type="id" name="top_card"/>
       <item type="id" name="top_edge"/>
       <item type="id" name="top_line"/>
       <item type="id" name="vertical_barrier"/>
+      <item type="id" name="view_pager"/>
+      <item type="integer" name="calm_mode_activity_fade_duration"/>
+      <item type="integer" name="calm_mode_content_fade_duration"/>
       <item type="integer" name="card_content_text_block_max_lines"/>
+      <item type="integer" name="media_card_bottom_panel_open_duration"/>
       <item type="integer" name="optional_seekbar_max"/>
       <item type="integer" name="playback_controls_bar_columns"/>
       <item type="layout" name="button_trio"/>
       <item type="layout" name="calm_mode_activity"/>
-      <item type="layout" name="calm_mode_date_and_temperature"/>
       <item type="layout" name="calm_mode_fragment"/>
       <item type="layout" name="car_launcher"/>
       <item type="layout" name="car_launcher_multiwindow"/>
@@ -187,8 +298,15 @@
       <item type="layout" name="card_content_media"/>
       <item type="layout" name="card_content_text_block"/>
       <item type="layout" name="card_fragment"/>
+      <item type="layout" name="card_fragment_audio_card"/>
       <item type="layout" name="control_bar_container"/>
       <item type="layout" name="descriptive_text"/>
+      <item type="layout" name="media_card_fullscreen"/>
+      <item type="layout" name="media_card_history_header_item"/>
+      <item type="layout" name="media_card_history_item"/>
+      <item type="layout" name="media_card_panel_content_item"/>
+      <item type="layout" name="media_card_queue_header_item"/>
+      <item type="layout" name="media_card_queue_item"/>
       <item type="layout" name="optional_seek_bar_with_times"/>
       <item type="layout" name="recent_clear_all_view"/>
       <item type="layout" name="recent_task_view"/>
@@ -198,10 +316,9 @@
       <item type="layout" name="tap_for_more_text"/>
       <item type="layout" name="text_block"/>
       <item type="string" name="app_title"/>
+      <item type="string" name="calm_mode_separator"/>
       <item type="string" name="calm_mode_title"/>
-      <item type="string" name="config_calmMode_activityName"/>
-      <item type="string" name="config_calmMode_packageName"/>
-      <item type="string" name="config_passengerLauncherComponent"/>
+      <item type="string" name="config_calmMode_componentName"/>
       <item type="string" name="config_smallCanvasOptimizedMapIntent"/>
       <item type="string" name="config_tosMapIntent"/>
       <item type="string" name="default_media_song_title"/>
@@ -209,6 +326,8 @@
       <item type="string" name="failure_opening_recent_task_message"/>
       <item type="string" name="fake_weather_footer_text"/>
       <item type="string" name="fake_weather_main_text"/>
+      <item type="string" name="media_card_history_header_title"/>
+      <item type="string" name="media_card_queue_header_title"/>
       <item type="string" name="ongoing_call_duration_text_separator"/>
       <item type="string" name="ongoing_call_text"/>
       <item type="string" name="projected_launch_text"/>
@@ -220,18 +339,19 @@
       <item type="string" name="tap_to_launch_text"/>
       <item type="string" name="times_separator"/>
       <item type="string" name="weather_app_name"/>
-      <item type="style" name="CalmMode"/>
-      <item type="style" name="CalmMode.BackgroundImage"/>
-      <item type="style" name="CalmMode.Icon"/>
-      <item type="style" name="CalmMode.Icon.Temperature"/>
-      <item type="style" name="CalmMode.Text"/>
-      <item type="style" name="CalmMode.Text.Clock"/>
-      <item type="style" name="CalmMode.Text.Date"/>
-      <item type="style" name="CalmMode.Text.MediaTitle"/>
+      <item type="style" name="CalmModeBackgroundImage"/>
+      <item type="style" name="CalmModeClock"/>
+      <item type="style" name="CalmModeIcon"/>
+      <item type="style" name="CalmModeText"/>
+      <item type="style" name="CalmModeText.Date"/>
+      <item type="style" name="CalmModeText.MediaTitle"/>
+      <item type="style" name="CalmModeText.Temperature"/>
       <item type="style" name="CardViewStyle"/>
       <item type="style" name="ClearAllRecentTasksButton"/>
       <item type="style" name="ContextualSpace"/>
       <item type="style" name="HiddenRecentTaskThumbnail"/>
+      <item type="style" name="MediaCardCustomActionButtonStyle"/>
+      <item type="style" name="MediaCardPanelButtonStyle"/>
       <item type="style" name="RecentTaskDismissButton"/>
       <item type="style" name="RecentTaskIcon"/>
       <item type="style" name="RecentTaskThumbnail"/>
@@ -241,6 +361,7 @@
       <item type="style" name="Theme.CalmMode"/>
       <item type="style" name="Theme.Launcher"/>
       <item type="style" name="TitleText"/>
+      <item type="xml" name="panel_animation_motion_scene"/>
     </policy>
   </overlayable>
 </resources>
diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml
index 2ec8f42..bcb5546 100644
--- a/app/res/values/strings.xml
+++ b/app/res/values/strings.xml
@@ -56,4 +56,9 @@
 
     <!-- Calm mode strings -->
     <string name="calm_mode_title">Calm mode</string>
+    <string name="calm_mode_separator" translatable="false">\u0020\u0020\u0020\u2022\u0020\u0020\u0020</string>
+
+    <!-- Fullscreen media card strings -->
+    <string name="media_card_queue_header_title">Queue</string>
+    <string name="media_card_history_header_title">Media Source</string>
 </resources>
diff --git a/app/res/values/styles.xml b/app/res/values/styles.xml
index c0ea7b3..de9c13f 100644
--- a/app/res/values/styles.xml
+++ b/app/res/values/styles.xml
@@ -86,55 +86,65 @@
         <item name="cornerSize">8dp</item>
     </style>
 
-    <style name="CalmMode">
-    </style>
-
-    <style name="CalmMode.Text" parent="CalmMode">
+    <style name="CalmModeText" parent="TextAppearance.Car.Body.Small">
         <item name="android:textColor">@android:color/white</item>
-        <item name="android:textSize">@dimen/calm_mode_text_size</item>
-        <item name="android:minHeight">@dimen/calm_mode_icon_size_regular</item>
     </style>
 
-    <style name="CalmMode.Icon" parent="CalmMode">
-        <item name="android:layout_width">@dimen/calm_mode_icon_size_regular</item>
-        <item name="android:layout_height">@dimen/calm_mode_icon_size_regular</item>
+    <style name="CalmModeIcon">
         <item name="android:scaleType">fitCenter</item>
         <item name="android:tint">@android:color/white</item>
     </style>
 
-    <style name="CalmMode.BackgroundImage" parent="CalmMode">
+    <style name="CalmModeBackgroundImage">
         <item name="android:src">@android:color/black</item>
         <item name="android:scaleType">centerCrop</item>
     </style>
 
-    <style name="CalmMode.Text.Date" parent="CalmMode.Text">
+    <style name="CalmModeText.Date">
+        <item name="android:textColor">@color/car_outline</item>
         <item name="android:format12Hour">EE, MMM dd</item>
         <item name="android:format24Hour">EE, MMM dd</item>
     </style>
 
-    <style name="CalmMode.Text.MediaTitle" parent="CalmMode.Text">
+    <style name="CalmModeText.MediaTitle">
+        <item name="android:textColor">@color/car_outline</item>
         <item name="android:ellipsize">end</item>
-        <item name="android:maxWidth">300dp</item>
+        <item name="android:maxWidth">1024dp</item>
         <item name="android:maxLines">2</item>
-        <item name="android:textAlignment">viewStart</item>
-    </style>
-
-    <style name="CalmMode.Text.Clock" parent="CalmMode.Text">
-        <item name="android:gravity">center</item>
         <item name="android:textAlignment">center</item>
-        <item name="android:textSize">200sp</item>
-        <item name="android:lineSpacingMultiplier">0.75</item>
-        <item name="android:format12Hour">hh\nmm</item>
-        <item name="android:format24Hour">HH\nmm</item>
-        <item name="android:letterSpacing">0.02</item>
-        <item name="android:lineSpacingExtra">-10sp</item>
-        <item name="android:fontVariationSettings">"'wght' 100, 'wdth' 10"</item>
-        <item name="android:fontFamily">roboto-flex</item>
     </style>
 
-    <style name="CalmMode.Icon.Temperature" parent="CalmMode.Icon">
-        <item name="android:layout_width">@dimen/calm_mode_icon_size_small</item>
-        <item name="android:layout_height">@dimen/calm_mode_icon_size_small</item>
+    <style name="CalmModeText.Temperature">
+        <item name="android:textColor">@color/car_outline</item>
+    </style>
+
+    <style name="CalmModeClock" parent="TextAppearance.Car.Display">
+        <item name="android:fontFamily">roboto-flex</item>
+        <item name="android:textFontWeight">300</item>
+        <item name="android:fontVariationSettings">"'wght' 300, 'wdth' 100, 'xtra' 468, 'xopq' 96, 'yopq' 79, 'ytuc' 712, 'ytas' 750, 'ytde' -203, 'ytfi' 738, 'opsz' 144"</item>
+        <item name="android:textColor">@color/car_neutral_90</item>
+        <item name="android:textSize">300sp</item>
+        <item name="android:textAlignment">center</item>
+        <item name="android:format12Hour">hh:mm</item>
+        <item name="android:format24Hour">HH:mm</item>
+        <item name="android:includeFontPadding">false</item>
+        <item name="android:lineSpacingMultiplier">1</item>
+        <item name="android:lineSpacingExtra">0pt</item>
+
+    </style>
+
+    <style name="MediaCardPanelButtonStyle">
+        <item name="android:scaleType">centerInside</item>
+        <item name="android:padding">@dimen/media_card_panel_button_icon_padding</item>
+        <item name="android:cropToPadding">true</item>
+        <item name="android:tint">@color/media_card_panel_button_tint_state_list</item>
+        <item name="android:background">@drawable/media_card_panel_button_shape</item>
+    </style>
+
+    <style name="MediaCardCustomActionButtonStyle">
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:tint">@color/car_on_surface</item>
+        <item name="android:background">@android:color/transparent</item>
     </style>
 
 </resources>
diff --git a/app/res/values/themes.xml b/app/res/values/themes.xml
index 06183e3..0ce0fd4 100644
--- a/app/res/values/themes.xml
+++ b/app/res/values/themes.xml
@@ -23,8 +23,10 @@
 
     <style name="Theme.CalmMode" parent="Theme.CarUi.NoToolbar">
         <item name="android:windowNoTitle">true</item>
-        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowIsTranslucent">true</item>
         <item name="android:windowSplashScreenAnimatedIcon">@android:color/transparent</item>
         <item name="android:windowBackground">@android:color/black</item>
+        <item name="android:activityOpenEnterAnimation">@anim/fade_in</item>
+        <item name="android:activityOpenExitAnimation">@anim/fade_out</item>
     </style>
 </resources>
diff --git a/app/res/xml/panel_animation_motion_scene.xml b/app/res/xml/panel_animation_motion_scene.xml
new file mode 100644
index 0000000..2ef5eb4
--- /dev/null
+++ b/app/res/xml/panel_animation_motion_scene.xml
@@ -0,0 +1,256 @@
+<!--
+  ~ Copyright (C) 2024 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 motionlicable 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.
+  -->
+
+<MotionScene
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:motion="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools">
+
+    <!-- layoutDuringTransition respects queue RecyclerView scroll animations -->
+    <Transition
+        motion:constraintSetEnd="@id/end"
+        motion:constraintSetStart="@id/start"
+        motion:duration="@integer/media_card_bottom_panel_open_duration"
+        motion:motionInterpolator="standard"
+        motion:layoutDuringTransition="honorRequest">
+        <KeyFrameSet>
+            <KeyAttribute
+                android:alpha="1"
+                motion:framePosition="0"
+                motion:motionTarget="@id/playback_action_id1" />
+            <KeyAttribute
+                android:alpha="0"
+                motion:framePosition="60"
+                motion:motionTarget="@id/playback_action_id1" />
+            <KeyAttribute
+                android:alpha="1"
+                motion:framePosition="0"
+                motion:motionTarget="@id/playback_action_id2" />
+            <KeyAttribute
+                android:alpha="0"
+                motion:framePosition="60"
+                motion:motionTarget="@id/playback_action_id2" />
+            <KeyAttribute
+                android:alpha="0"
+                motion:framePosition="40"
+                motion:motionTarget="@id/button_panel_background" />
+            <KeyAttribute
+                android:alpha="0"
+                motion:framePosition="60"
+                motion:motionTarget="@id/button_panel_background" />
+            <KeyTrigger
+                motion:framePosition="30"
+                motion:motionTarget="@id/button_panel_background"
+                motion:onPositiveCross="."
+                tools:ignore="MotionSceneFileValidationError">
+                <CustomMethod motion:methodName="setEnabled" motion:customBoolean="true"/>
+            </KeyTrigger>
+            <KeyTrigger
+                motion:framePosition="40"
+                motion:motionTarget="@id/button_panel_background"
+                motion:onNegativeCross="."
+                tools:ignore="MotionSceneFileValidationError">
+                <CustomMethod motion:methodName="setEnabled" motion:customBoolean="false"/>
+            </KeyTrigger>
+            <KeyTrigger
+                motion:framePosition="30"
+                motion:motionTarget="@id/playback_seek_bar"
+                motion:onPositiveCross="."
+                tools:ignore="MotionSceneFileValidationError">
+                <CustomMethod motion:methodName="setSelected" motion:customBoolean="true"/>
+            </KeyTrigger>
+            <KeyTrigger
+                motion:framePosition="40"
+                motion:motionTarget="@id/playback_seek_bar"
+                motion:onNegativeCross="."
+                tools:ignore="MotionSceneFileValidationError">
+                <CustomMethod motion:methodName="setSelected" motion:customBoolean="false"/>
+            </KeyTrigger>
+        </KeyFrameSet>
+    </Transition>
+
+    <ConstraintSet
+        android:id="@+id/start">
+        <ConstraintOverride
+            android:id="@id/media_widget_app_icon">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/title">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+            <CustomAttribute
+                motion:attributeName="lineHeight"
+                motion:customDimension="@dimen/media_card_title_default_line_height" />
+            <CustomAttribute
+                motion:attributeName="textSize"
+                motion:customDimension="@dimen/media_card_title_default_text_size" />
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/subtitle">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/content_format">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/playback_action_id1">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/playback_action_id2">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/album_art">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/playback_seek_bar">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+    </ConstraintSet>
+
+    <ConstraintSet
+        android:id="@+id/end">
+        <Constraint
+            android:id="@+id/play_pause_button"
+            android:layout_width="@dimen/media_card_large_button_size"
+            android:layout_height="@dimen/media_card_large_button_size"
+            android:src="@drawable/ic_play_pause_selector"
+            android:scaleType="center"
+            android:tint="@color/car_surface_container_high"
+            android:background="@drawable/pill_button_shape"
+            android:backgroundTint="@color/car_primary"
+            android:layout_marginStart="@dimen/media_card_horizontal_margin"
+            android:layout_marginTop="@dimen/media_card_margin_panel_open"
+            motion:layout_constraintStart_toStartOf="parent"
+            motion:layout_constraintTop_toTopOf="parent">
+        </Constraint>
+        <Constraint
+            android:id="@+id/button_panel_background"
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/media_card_bottom_panel_animated_size"
+            android:background="@drawable/media_card_button_panel_background"
+            android:backgroundTint="@color/car_surface_container_highest"
+            android:layout_marginStart="@dimen/media_card_horizontal_margin"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            android:layout_marginTop="@dimen/media_card_margin_panel_open"
+            motion:layout_constraintStart_toStartOf="parent"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintTop_toBottomOf="@id/play_pause_button">
+        </Constraint>
+        <Constraint
+            android:id="@+id/empty_panel"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            android:background="@color/car_surface_container_high"
+            motion:layout_constraintTop_toTopOf="parent">
+        </Constraint>
+        <ConstraintOverride
+            android:id="@id/media_widget_app_icon"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <Constraint
+            android:id="@+id/title"
+            android:layout_height="wrap_content"
+            android:layout_width="0dp"
+            android:text="@string/metadata_default_title"
+            android:textColor="@color/car_text_primary"
+            android:maxLines="1"
+            android:ellipsize="end"
+            android:layout_marginStart="@dimen/media_card_margin_panel_open"
+            android:layout_marginTop="@dimen/media_card_view_separation_margin"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            motion:layout_constraintStart_toEndOf="@id/play_pause_button"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintTop_toTopOf="@id/play_pause_button"
+            motion:layout_constraintBottom_toTopOf="@id/playback_seek_bar"
+            motion:layout_constraintVertical_bias="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+            <CustomAttribute
+                motion:attributeName="lineHeight"
+                motion:customDimension="@dimen/media_card_title_animated_line_height" />
+            <CustomAttribute
+                motion:attributeName="textSize"
+                motion:customDimension="@dimen/media_card_title_animated_text_size" />
+        </Constraint>
+        <ConstraintOverride
+            android:id="@id/subtitle"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/content_format"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <Constraint
+            android:id="@+id/playback_seek_bar"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:clickable="false"
+            android:paddingEnd="0dp"
+            android:paddingStart="0dp"
+            android:progressBackgroundTint="@color/car_surface_container_highest"
+            android:progressDrawable="@drawable/media_card_seekbar_progress"
+            android:progressTint="@color/car_primary"
+            android:splitTrack="false"
+            android:thumb="@drawable/media_card_seekbar_thumb"
+            android:thumbTint="@color/car_on_surface"
+            android:thumbOffset="0px"
+            android:layout_marginStart="@dimen/media_card_margin_panel_open"
+            android:layout_marginEnd="@dimen/media_card_horizontal_margin"
+            motion:layout_constraintStart_toEndOf="@id/play_pause_button"
+            motion:layout_constraintEnd_toEndOf="parent"
+            motion:layout_constraintBottom_toBottomOf="@id/play_pause_button"
+            motion:layout_constraintTop_toBottomOf="@id/title">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </Constraint>
+        <ConstraintOverride
+            android:id="@id/playback_action_id1"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/playback_action_id2"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+        <ConstraintOverride
+            android:id="@id/album_art"
+            android:alpha="0">
+            <PropertySet
+                motion:visibilityMode="ignore"/>
+        </ConstraintOverride>
+    </ConstraintSet>
+</MotionScene>
diff --git a/app/src/com/android/car/carlauncher/CarFullscreenTaskMonitorListener.java b/app/src/com/android/car/carlauncher/CarFullscreenTaskMonitorListener.java
deleted file mode 100644
index 2ac0eed..0000000
--- a/app/src/com/android/car/carlauncher/CarFullscreenTaskMonitorListener.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import android.app.ActivityManager;
-import android.car.app.CarActivityManager;
-import android.util.Log;
-import android.view.SurfaceControl;
-
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * The Car version of FullscreenTaskListener, which reports Task lifecycle to CarService.
- */
-public class CarFullscreenTaskMonitorListener extends FullscreenTaskListener {
-    private static final String TAG = CarFullscreenTaskMonitorListener.class.getSimpleName();
-    private final AtomicReference<CarActivityManager> mCarActivityManagerRef;
-
-    public CarFullscreenTaskMonitorListener(
-            AtomicReference<CarActivityManager> carActivityManagerRef,
-            SyncTransactionQueue syncQueue) {
-        super(syncQueue);
-        mCarActivityManagerRef = carActivityManagerRef;
-    }
-    @Override
-    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
-            SurfaceControl leash) {
-        super.onTaskAppeared(taskInfo, leash);
-        CarActivityManager carAM = mCarActivityManagerRef.get();
-        if (carAM != null) {
-            carAM.onTaskAppeared(taskInfo, leash);
-        } else {
-            Log.w(TAG, "CarActivityManager is null, skip onTaskAppeared: taskInfo=" + taskInfo);
-        }
-    }
-
-    @Override
-    public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-        super.onTaskInfoChanged(taskInfo);
-        CarActivityManager carAM = mCarActivityManagerRef.get();
-        if (carAM != null) {
-            carAM.onTaskInfoChanged(taskInfo);
-        } else {
-            Log.w(TAG, "CarActivityManager is null, skip onTaskInfoChanged: taskInfo=" + taskInfo);
-        }
-    }
-
-    @Override
-    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-        super.onTaskVanished(taskInfo);
-        CarActivityManager carAM = mCarActivityManagerRef.get();
-        if (carAM != null) {
-            carAM.onTaskVanished(taskInfo);
-        } else {
-            Log.w(TAG, "CarActivityManager is null, skip onTaskVanished: taskInfo=" + taskInfo);
-        }
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/CarLauncher.java b/app/src/com/android/car/carlauncher/CarLauncher.java
index c686584..d256a05 100644
--- a/app/src/com/android/car/carlauncher/CarLauncher.java
+++ b/app/src/com/android/car/carlauncher/CarLauncher.java
@@ -17,17 +17,16 @@
 package com.android.car.carlauncher;
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED;
+import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
 
+import static com.android.car.carlauncher.AppGridFragment.Mode.ALL_APPS;
 import static com.android.car.carlauncher.CarLauncherViewModel.CarLauncherViewModelFactory;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.TaskStackListener;
 import android.car.Car;
-import android.car.user.CarUserManager;
-import android.content.ComponentName;
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.database.ContentObserver;
@@ -37,6 +36,7 @@
 import android.provider.Settings;
 import android.util.Log;
 import android.view.Display;
+import android.view.View;
 import android.view.ViewGroup;
 import android.view.WindowManager;
 
@@ -46,6 +46,8 @@
 import androidx.lifecycle.ViewModelProvider;
 
 import com.android.car.carlauncher.homescreen.HomeCardModule;
+import com.android.car.carlauncher.homescreen.audio.IntentHandler;
+import com.android.car.carlauncher.homescreen.audio.media.MediaIntentRouter;
 import com.android.car.carlauncher.taskstack.TaskStackChangeListeners;
 import com.android.car.internal.common.UserHelperLite;
 import com.android.wm.shell.taskview.TaskView;
@@ -72,21 +74,18 @@
     public static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private ActivityManager mActivityManager;
-    private TaskViewManager mTaskViewManager;
-
     private Car mCar;
-    private CarTaskView mTaskView;
     private int mCarLauncherTaskId = INVALID_TASK_ID;
     private Set<HomeCardModule> mHomeCardModules;
 
     /** Set to {@code true} once we've logged that the Activity is fully drawn. */
     private boolean mIsReadyLogged;
     private boolean mUseSmallCanvasOptimizedMap;
-    private boolean mUseRemoteCarTaskView;
     private ViewGroup mMapsCard;
-    private CarLauncherViewModel mCarLauncherViewModel;
 
     @VisibleForTesting
+    CarLauncherViewModel mCarLauncherViewModel;
+    @VisibleForTesting
     ContentObserver mTosContentObserver;
 
     private final TaskStackListener mTaskStackListener = new TaskStackListener() {
@@ -107,20 +106,19 @@
                 // The embedded map component received an intent, therefore forcibly bringing the
                 // launcher to the foreground.
                 bringToForeground();
-                return;
             }
         }
     };
 
-    @VisibleForTesting
-    void setCarUserManager(CarUserManager carUserManager) {
-        if (mTaskViewManager == null) {
-            Log.w(TAG, "Task view manager is null, cannot set CarUserManager on taskview "
-                    + "manager");
-            return;
+    private final IntentHandler mMediaIntentHandler = new IntentHandler() {
+        @Override
+        public void handleIntent(Intent intent) {
+            if (intent != null) {
+                ActivityOptions options = ActivityOptions.makeBasic();
+                startActivity(intent, options.toBundle());
+            }
         }
-        mTaskViewManager.setCarUserManager(carUserManager);
-    }
+    };
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -129,56 +127,13 @@
         if (DEBUG) {
             Log.d(TAG, "onCreate(" + getUserId() + ") displayId=" + getDisplayId());
         }
-        // Since MUMD is introduced, CarLauncher can be called in the main display of visible users.
-        // In ideal shape, CarLauncher should handle both driver and passengers together.
-        // But, in the mean time, we have separate launchers for driver and passengers, so
-        // CarLauncher needs to reroute the request to Passenger launcher if it is invoked from
-        // the main display of passengers (not driver).
-        // For MUPAND, PassengerLauncher should be the default launcher.
-        // For non-main displays, ATM will invoke SECONDARY_HOME Intent, so the secondary launcher
-        // should handle them.
+        // Since MUMD/MUPAND is introduced, CarLauncher can be called in the main display of
+        // visible background users.
+        // For Passenger scenarios, replace the maps_card with AppGridActivity, as currently
+        // there is no maps use-case for passengers.
         UserManager um = getSystemService(UserManager.class);
         boolean isPassengerDisplay = getDisplayId() != Display.DEFAULT_DISPLAY
                 || um.isVisibleBackgroundUsersOnDefaultDisplaySupported();
-        if (isPassengerDisplay) {
-            String passengerLauncherName = getString(R.string.config_passengerLauncherComponent);
-            Intent passengerHomeIntent;
-            if (!passengerLauncherName.isEmpty()) {
-                ComponentName component = ComponentName.unflattenFromString(passengerLauncherName);
-                if (component == null) {
-                    throw new IllegalStateException(
-                            "Invalid passengerLauncher name=" + passengerLauncherName);
-                }
-                passengerHomeIntent = new Intent(Intent.ACTION_MAIN)
-                        // passenger launcher should be launched in home task in order to
-                        // fix TaskView layering issue
-                        .addCategory(Intent.CATEGORY_HOME)
-                        .setComponent(component);
-            } else {
-                // No passenger launcher is specified, then use AppsGrid as a fallback.
-                passengerHomeIntent = CarLauncherUtils.getAppsGridIntent();
-            }
-            ActivityOptions options = ActivityOptions
-                    // No animation for the trampoline.
-                    .makeCustomAnimation(this, /* enterResId=*/ 0, /* exitResId= */ 0)
-                    .setLaunchDisplayId(getDisplayId());
-            startActivity(passengerHomeIntent, options.toBundle());
-            finish();
-            return;
-        }
-
-        mUseSmallCanvasOptimizedMap =
-                CarLauncherUtils.isSmallCanvasOptimizedMapIntentConfigured(this);
-        mUseRemoteCarTaskView = getResources().getBoolean(R.bool.config_useRemoteCarTaskView);
-
-        mActivityManager = getSystemService(ActivityManager.class);
-        mCarLauncherTaskId = getTaskId();
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
-
-        // Setting as trusted overlay to let touches pass through.
-        getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
-        // To pass touches to the underneath task.
-        getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
 
         // Don't show the maps panel in multi window mode.
         // NOTE: CTS tests for split screen are not compatible with activity views on the default
@@ -187,78 +142,66 @@
             setContentView(R.layout.car_launcher_multiwindow);
         } else {
             setContentView(R.layout.car_launcher);
-            // We don't want to show Map card unnecessarily for the headless user 0.
-            if (!UserHelperLite.isHeadlessSystemUser(getUserId())) {
-                mMapsCard = findViewById(R.id.maps_card);
-                if (mMapsCard != null) {
-                    if (mUseRemoteCarTaskView) {
+            // Passenger displays do not require TaskView Embedding
+            if (!isPassengerDisplay) {
+                mUseSmallCanvasOptimizedMap =
+                        CarLauncherUtils.isSmallCanvasOptimizedMapIntentConfigured(this);
+
+                mActivityManager = getSystemService(ActivityManager.class);
+                mCarLauncherTaskId = getTaskId();
+                TaskStackChangeListeners.getInstance().registerTaskStackListener(
+                        mTaskStackListener);
+
+                // Setting as trusted overlay to let touches pass through.
+                getWindow().addPrivateFlags(PRIVATE_FLAG_TRUSTED_OVERLAY);
+                // To pass touches to the underneath task.
+                getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
+                // We don't want to show Map card unnecessarily for the headless user 0
+                if (!UserHelperLite.isHeadlessSystemUser(getUserId())) {
+                    mMapsCard = findViewById(R.id.maps_card);
+                    if (mMapsCard != null) {
                         setupRemoteCarTaskView(mMapsCard);
-                    } else {
-                        setUpTaskView(mMapsCard);
                     }
                 }
+            } else {
+                // For Passenger display show the AppGridFragment in place of the Maps view.
+                // Also we can skip initializing all the TaskView related objects as they are not
+                // used in this case.
+                getSupportFragmentManager().beginTransaction().replace(R.id.maps_card,
+                        AppGridFragment.newInstance(ALL_APPS)).commit();
+
             }
         }
+
+        MediaIntentRouter.getInstance().registerMediaIntentHandler(mMediaIntentHandler);
         initializeCards();
         setupContentObserversForTos();
     }
 
     private void setupRemoteCarTaskView(ViewGroup parent) {
         mCarLauncherViewModel = new ViewModelProvider(this,
-                new CarLauncherViewModelFactory(this, getMapsIntent()))
+                new CarLauncherViewModelFactory(this))
                 .get(CarLauncherViewModel.class);
+        mCarLauncherViewModel.initializeRemoteCarTaskView(getMapsIntent());
 
         getLifecycle().addObserver(mCarLauncherViewModel);
+        addOnNewIntentListener(mCarLauncherViewModel.getNewIntentListener());
 
         mCarLauncherViewModel.getRemoteCarTaskView().observe(this, taskView -> {
-            if (taskView != null && taskView.getParent() == null) {
-                parent.addView(taskView);
+            if (taskView == null || taskView.getParent() == parent) {
+                // Discard if the parent is still the same because it doesn't signify a config
+                // change.
+                return;
             }
+            if (taskView.getParent() != null) {
+                // Discard the previous parent as its invalid now.
+                ((ViewGroup) taskView.getParent()).removeView(taskView);
+            }
+            parent.removeAllViews(); // Just a defense against a dirty parent.
+            parent.addView(taskView);
         });
     }
 
-    private void setUpTaskView(ViewGroup parent) {
-        Set<String> taskViewPackages = new ArraySet<>(getResources().getStringArray(
-                R.array.config_taskViewPackages));
-        mTaskViewManager = new TaskViewManager(this, getMainThreadHandler());
-
-        mTaskViewManager.createControlledCarTaskView(
-                getMainExecutor(),
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(getMapsIntent())
-                        // TODO(b/263876526): Enable auto restart after ensuring no CTS failure.
-                        .setAutoRestartOnCrash(false)
-                        .build(),
-                new ControlledCarTaskViewCallbacks() {
-                    @Override
-                    public void onTaskViewCreated(CarTaskView taskView) {
-                        parent.addView(taskView);
-                        mTaskView = taskView;
-                    }
-
-                    @Override
-                    public void onTaskViewReady() {
-                        maybeLogReady();
-                    }
-
-                    @Override
-                    public Set<String> getDependingPackageNames() {
-                        return taskViewPackages;
-                    }
-                });
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        // The TaskViewManager might have been released if the user was switched to some other user
-        // and then switched back to the previous user before the previous user is stopped.
-        // In such a case, the TaskViewManager should be recreated.
-        if (!mUseRemoteCarTaskView && mMapsCard != null && mTaskViewManager.isReleased()) {
-            setUpTaskView(mMapsCard);
-        }
-    }
-
     @Override
     protected void onResume() {
         super.onResume();
@@ -269,18 +212,19 @@
     protected void onDestroy() {
         super.onDestroy();
         TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
+        unregisterTosContentObserver();
+        release();
+    }
+
+    private void unregisterTosContentObserver() {
         if (mTosContentObserver != null) {
             Log.i(TAG, "Unregister content observer for tos state");
             getContentResolver().unregisterContentObserver(mTosContentObserver);
             mTosContentObserver = null;
         }
-        release();
     }
 
     private int getTaskViewTaskId() {
-        if (mTaskView != null) {
-            return mTaskView.getTaskId();
-        }
         if (mCarLauncherViewModel != null) {
             return mCarLauncherViewModel.getRemoteCarTaskViewTaskId();
         }
@@ -288,11 +232,13 @@
     }
 
     private void release() {
-        mTaskView = null;
-        // When using a ViewModel for the RemoteCarTaskViews, the task view can still be attached
-        // to the mMapsCard due to which the CarLauncher activity does not get garbage collected
-        // during activity recreation.
-        mMapsCard = null;
+        if (mMapsCard != null) {
+            // This is important as the TaskView is preserved during config change in ViewModel and
+            // to avoid the memory leak, it should be plugged out of the View hierarchy.
+            mMapsCard.removeAllViews();
+            mMapsCard = null;
+        }
+
         if (mCar != null) {
             mCar.disconnect();
             mCar = null;
@@ -314,6 +260,11 @@
                     long reflectionStartTime = System.currentTimeMillis();
                     HomeCardModule cardModule = (HomeCardModule)
                             Class.forName(providerClassName).newInstance();
+                    if (Flags.mediaCardFullscreen()) {
+                        if (cardModule.getCardResId() == R.id.top_card) {
+                            findViewById(R.id.top_card).setVisibility(View.GONE);
+                        }
+                    }
                     cardModule.setViewModelProvider(new ViewModelProvider(/* owner= */this));
                     mHomeCardModules.add(cardModule);
                     if (DEBUG) {
@@ -337,13 +288,7 @@
     /** Logs that the Activity is ready. Used for startup time diagnostics. */
     private void maybeLogReady() {
         boolean isResumed = isResumed();
-        boolean taskViewInitialized = mTaskView != null && mTaskView.isInitialized();
-        if (DEBUG) {
-            Log.d(TAG, "maybeLogReady(" + getUserId() + "): mapsReady="
-                    + taskViewInitialized + ", started=" + isResumed + ", alreadyLogged: "
-                    + mIsReadyLogged);
-        }
-        if (taskViewInitialized && isResumed) {
+        if (isResumed) {
             // We should report every time - the Android framework will take care of logging just
             // when it's effectively drawn for the first time, but....
             reportFullyDrawn();
@@ -392,21 +337,36 @@
                 || !AppLauncherUtils.tosAccepted(/* context = */ this)) {
             Log.i(TAG, "TOS not accepted, setting up content observers for TOS state");
         } else {
-            Log.i(TAG, "TOS accepted, state will remain accepted, "
-                    + "don't need to observe this value");
+            Log.i(TAG,
+                    "TOS accepted, state will remain accepted, don't need to observe this value");
             return;
         }
         mTosContentObserver = new ContentObserver(new Handler()) {
             @Override
             public void onChange(boolean selfChange) {
                 super.onChange(selfChange);
-                // TODO (b/280077391): Release the remote task view and recreate the map activity
-                Log.i(TAG, "TOS state updated:" + AppLauncherUtils.tosAccepted(getBaseContext()));
-                recreate();
+                // Release the task view and re-initialize the remote car task view with the new
+                // maps intent whenever an onChange is received. This is because the TOS state
+                // can go from uninitialized to not accepted during which there could be a race
+                // condition in which the maps activity is from the uninitialized state.
+                Set<String> tosDisabledApps = AppLauncherUtils.getTosDisabledPackages(
+                        getBaseContext());
+                boolean tosAccepted = AppLauncherUtils.tosAccepted(getBaseContext());
+                Log.i(TAG, "TOS state updated:" + tosAccepted);
+                if (DEBUG) {
+                    Log.d(TAG, "TOS disabled apps:" + tosDisabledApps);
+                }
+                if (mCarLauncherViewModel.getRemoteCarTaskView().getValue() != null) {
+                    mCarLauncherViewModel.getRemoteCarTaskView().getValue().release();
+                    setupRemoteCarTaskView(mMapsCard);
+                }
+                if (tosAccepted) {
+                    unregisterTosContentObserver();
+                }
             }
         };
         getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(KEY_USER_TOS_ACCEPTED),
+                Settings.Secure.getUriFor(KEY_UNACCEPTED_TOS_DISABLED_APPS),
                 /* notifyForDescendants*/ false,
                 mTosContentObserver);
     }
diff --git a/app/src/com/android/car/carlauncher/CarLauncherViewModel.java b/app/src/com/android/car/carlauncher/CarLauncherViewModel.java
index 3bafbd5..3b2b081 100644
--- a/app/src/com/android/car/carlauncher/CarLauncherViewModel.java
+++ b/app/src/com/android/car/carlauncher/CarLauncherViewModel.java
@@ -40,6 +40,7 @@
 import android.os.Build;
 import android.util.Log;
 
+import androidx.core.util.Consumer;
 import androidx.lifecycle.DefaultLifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LiveData;
@@ -57,19 +58,30 @@
 
     private final CarActivityManager mCarActivityManager;
     private final Car mCar;
-    private final CarTaskViewControllerHostLifecycle mHostLifecycle;
     @SuppressLint("StaticFieldLeak") // We're not leaking this context as it is the window context.
     private final Context mWindowContext;
-    private final Intent mMapsIntent;
-    private final MutableLiveData<RemoteCarTaskView> mRemoteCarTaskView;
 
-    public CarLauncherViewModel(@UiContext Context context, @NonNull Intent mapsIntent) {
+    // Do not make this final because the maps intent can be changed based on the state of TOS.
+    private Intent mMapsIntent;
+    private CarTaskViewControllerHostLifecycle mHostLifecycle;
+    private MutableLiveData<RemoteCarTaskView> mRemoteCarTaskView;
+
+    public CarLauncherViewModel(@UiContext Context context) {
         mWindowContext = context.createWindowContext(TYPE_APPLICATION_STARTING, /* options */ null);
-        mMapsIntent = mapsIntent;
         mCar = Car.createCar(mWindowContext);
         mCarActivityManager = mCar.getCarManager(CarActivityManager.class);
-        mHostLifecycle = new CarTaskViewControllerHostLifecycle();
+    }
+
+    /**
+     * Initialize the remote car task view with the maps intent.
+     */
+    void initializeRemoteCarTaskView(@NonNull Intent mapsIntent) {
+        if (DEBUG) {
+            Log.d(TAG, "Maps intent in the task view = " + mapsIntent.getComponent());
+        }
+        mMapsIntent = mapsIntent;
         mRemoteCarTaskView = new MutableLiveData<>(null);
+        mHostLifecycle = new CarTaskViewControllerHostLifecycle();
         ControlledRemoteCarTaskViewCallback controlledRemoteCarTaskViewCallback =
                 new ControlledRemoteCarTaskViewCallbackImpl(mRemoteCarTaskView);
 
@@ -101,7 +113,15 @@
     @Override
     public void onResume(@NonNull LifecycleOwner owner) {
         DefaultLifecycleObserver.super.onResume(owner);
-        mHostLifecycle.hostAppeared();
+        // Do not trigger 'hostAppeared()' in onResume.
+        // If the host Activity was hidden by an Activity, the Activity is moved to the other
+        // display, what the system expects would be the new moved Activity becomes the top one.
+        // But, at the time, the host Activity became visible and 'onResume()' is triggered.
+        // If 'hostAppeared()' is called in onResume, which moves the embeddedTask to the top and
+        // breaks the contract (the newly moved Activity becomes top).
+        // The contract is maintained by android.server.wm.multidisplay.MultiDisplayClientTests.
+        // BTW, if we don't invoke 'hostAppeared()', which makes the embedded task invisible if
+        // the host Activity gets the new Intent, so we'd call 'hostAppeared()' in onNewIntent.
     }
 
     @Override
@@ -122,6 +142,17 @@
         super.onCleared();
     }
 
+    public Consumer<Intent> getNewIntentListener() {
+        return mNewIntentConsumer;
+    }
+
+    private final Consumer<Intent> mNewIntentConsumer = new Consumer<Intent>() {
+        @Override
+        public void accept(Intent intent) {
+            mHostLifecycle.hostAppeared();
+        }
+    };
+
     private static final class ControlledRemoteCarTaskViewCallbackImpl implements
             ControlledRemoteCarTaskViewCallback {
         private final MutableLiveData<RemoteCarTaskView> mRemoteCarTaskView;
@@ -198,17 +229,15 @@
 
     static final class CarLauncherViewModelFactory implements ViewModelProvider.Factory {
         private final Context mContext;
-        private final Intent mMapsIntent;
 
-        CarLauncherViewModelFactory(@UiContext Context context, @NonNull Intent mapsIntent) {
-            mMapsIntent = requireNonNull(mapsIntent);
+        CarLauncherViewModelFactory(@UiContext Context context) {
             mContext = requireNonNull(context);
         }
 
         @NonNull
         @Override
         public <T extends ViewModel> T create(Class<T> modelClass) {
-            return modelClass.cast(new CarLauncherViewModel(mContext, mMapsIntent));
+            return modelClass.cast(new CarLauncherViewModel(mContext));
         }
     }
 }
diff --git a/app/src/com/android/car/carlauncher/CarTaskView.java b/app/src/com/android/car/carlauncher/CarTaskView.java
deleted file mode 100644
index 5091ea2..0000000
--- a/app/src/com/android/car/carlauncher/CarTaskView.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-
-import static com.android.car.carlauncher.TaskViewManager.DBG;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Binder;
-import android.util.Log;
-import android.util.SparseArray;
-import android.view.InsetsSource;
-import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.taskview.TaskView;
-import com.android.wm.shell.taskview.TaskViewTaskController;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-
-/**
- * CarLauncher version of {@link TaskView} which solves some CarLauncher specific issues:
- * <ul>
- * <li>b/228092608: Clears the hidden flag to make it TopFocusedRootTask.</li>
- * <li>b/225388469: Moves the embedded task to the top to make it resumed.</li>
- * </ul>
- */
-
-public class CarTaskView extends TaskView {
-    private static final String TAG = CarTaskView.class.getSimpleName();
-    @Nullable
-    private WindowContainerToken mTaskToken;
-    private final SyncTransactionQueue mSyncQueue;
-    private final Binder mInsetsOwner = new Binder();
-    private final SparseArray<Rect> mInsets = new SparseArray<>();
-    private boolean mTaskViewReadySent;
-    private TaskViewTaskController mTaskViewTaskController;
-
-    public CarTaskView(Context context, ShellTaskOrganizer organizer,
-            TaskViewTransitions taskViewTransitions, SyncTransactionQueue syncQueue,
-            boolean shouldHideTask) {
-        this(context, syncQueue, shouldHideTask,
-                new TaskViewTaskController(context, organizer, taskViewTransitions, syncQueue));
-    }
-
-    public CarTaskView(Context context, SyncTransactionQueue syncQueue, boolean shouldHideTask,
-            TaskViewTaskController taskViewTaskController) {
-        super(context, taskViewTaskController);
-        mTaskViewTaskController = taskViewTaskController;
-        mTaskViewTaskController.setHideTaskWithSurface(shouldHideTask);
-        mSyncQueue = syncQueue;
-    }
-
-    /**
-     * Calls {@link TaskViewTaskController#onTaskAppeared(ActivityManager.RunningTaskInfo,
-     * SurfaceControl)}.
-     */
-    public void dispatchTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
-            SurfaceControl leash) {
-        mTaskViewTaskController.onTaskAppeared(taskInfo, leash);
-    }
-
-    /**
-     * Calls {@link TaskViewTaskController#onTaskVanished(ActivityManager.RunningTaskInfo)}.
-     */
-    public void dispatchTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-        mTaskViewTaskController.onTaskVanished(taskInfo);
-    }
-
-    /**
-     * Calls {@link TaskViewTaskController#onTaskInfoChanged(ActivityManager.RunningTaskInfo)}.
-     */
-    public void dispatchTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-        mTaskViewTaskController.onTaskInfoChanged(taskInfo);
-    }
-
-    @Override
-    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
-        mTaskToken = taskInfo.token;
-        super.onTaskAppeared(taskInfo, leash);
-
-        applyAllInsets();
-    }
-
-    /**
-     * Triggers the change in the WM bounds as per the {@code newBounds} received.
-     *
-     * Should be called when the surface has changed. Can also be called before an animation if
-     * the final bounds are already known.
-     */
-    public void setWindowBounds(Rect newBounds) {
-        mTaskViewTaskController.setWindowBounds(newBounds);
-    }
-
-    @Override
-    public void surfaceCreated(SurfaceHolder holder) {
-        super.surfaceCreated(holder);
-        if (mTaskViewReadySent) {
-            if (DBG) Log.i(TAG, "car task view ready already sent");
-            return;
-        }
-        onCarTaskViewInitialized();
-        mTaskViewReadySent = true;
-    }
-
-    /**
-     * Called only once when the {@link CarTaskView} is ready.
-     */
-    protected void onCarTaskViewInitialized() {}
-
-    /**
-     * Moves the embedded task over the embedding task to make it shown.
-     */
-    void showEmbeddedTask(WindowContainerTransaction wct) {
-        if (mTaskToken == null) {
-            return;
-        }
-        // Clears the hidden flag to make it TopFocusedRootTask: b/228092608
-        wct.setHidden(mTaskToken, /* hidden= */ false);
-        // Moves the embedded task to the top to make it resumed: b/225388469
-        wct.reorder(mTaskToken, /* onTop= */ true);
-    }
-
-    // TODO(b/238473897): Consider taking insets one by one instead of taking all insets.
-    /**
-     * Adds & applies the given insets on the Task.
-     *
-     * <p>
-     * The insets that were specified in an earlier call but not specified later, will remain
-     * applied to the task. Clients should explicitly call
-     * {@link #removeInsets(int, int)} to remove the insets from the underlying task.
-     * </p>
-     *
-     * @param index The caller might add multiple insets sources with the same type.
-     *              This identifies them.
-     * @param type  The insets type of the insets source.
-     * @param frame The rectangle area of the insets source.
-     */
-    public void addInsets(int index, int type, @NonNull Rect frame) {
-        mInsets.append(InsetsSource.createId(mInsetsOwner, index, type), frame);
-
-        if (mTaskToken == null) {
-            // The insets will be applied later as part of onTaskAppeared.
-            Log.w(TAG, "Cannot apply insets as the task token is not present.");
-            return;
-        }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.addInsetsSource(mTaskToken, mInsetsOwner, index, type, frame);
-        mSyncQueue.queue(wct);
-    }
-
-    /**
-     * Removes the given insets from the Task.
-     *
-     * @param index The caller might add multiple insets sources with the same type.
-     *              This identifies them.
-     * @param type  The insets type of the insets source.
-     */
-    public void removeInsets(int index, int type) {
-        if (mInsets.size() == 0) {
-            Log.w(TAG, "No insets set.");
-            return;
-        }
-        int id = InsetsSource.createId(mInsetsOwner, index, type);
-        if (!mInsets.contains(id)) {
-            Log.w(TAG, "Insets type: " + type + " can't be removed as it was not "
-                    + "applied as part of the last addInsets()");
-            return;
-        }
-        mInsets.remove(id);
-
-        if (mTaskToken == null) {
-            Log.w(TAG, "Cannot remove insets as the task token is not present.");
-            return;
-        }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.removeInsetsSource(mTaskToken, mInsetsOwner, index, type);
-        mSyncQueue.queue(wct);
-    }
-
-    private void applyAllInsets() {
-        if (mInsets.size() == 0) {
-            Log.w(TAG, "Cannot apply null or empty insets");
-            return;
-        }
-        if (mTaskToken == null) {
-            Log.w(TAG, "Cannot apply insets as the task token is not present.");
-            return;
-        }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        for (int i = 0; i < mInsets.size(); i++) {
-            final int id = mInsets.keyAt(i);
-            final Rect frame = mInsets.valueAt(i);
-            wct.addInsetsSource(mTaskToken, mInsetsOwner, InsetsSource.getIndex(id),
-                    InsetsSource.getType(id), frame);
-        }
-        mSyncQueue.queue(wct);
-    }
-
-    /**
-     * @return the taskId of the currently running task.
-     */
-    public int getTaskId() {
-        if (mTaskViewTaskController.getTaskInfo() == null) {
-            return INVALID_TASK_ID;
-        }
-        return mTaskViewTaskController.getTaskInfo().taskId;
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/CarTaskViewCallbacks.java b/app/src/com/android/car/carlauncher/CarTaskViewCallbacks.java
deleted file mode 100644
index 03edce5..0000000
--- a/app/src/com/android/car/carlauncher/CarTaskViewCallbacks.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-/**
- * A callback interface for the host activity that uses {@link CarTaskView} and its derivatives.
- */
-interface CarTaskViewCallbacks {
-    /**
-     * Called when the underlying {@link CarTaskView} instance is created.
-     *
-     * @param taskView the new newly created {@link CarTaskView} instance.
-     */
-    void onTaskViewCreated(CarTaskView taskView);
-
-    /**
-     * Called when the underlying {@link CarTaskView} is ready. A {@link CarTaskView} can be
-     * considered ready when it has completed all the set up that is required.
-     * This callback is only triggered once.
-     *
-     * For {@link LaunchRootCarTaskView}, this is called once the launch root task has been
-     * fully set up.
-     * For {@link SemiControlledCarTaskView} & {@link ControlledCarTaskView} this is called when
-     * the surface is created.
-     */
-    void onTaskViewReady();
-}
-
-
diff --git a/app/src/com/android/car/carlauncher/ControlledCarTaskView.java b/app/src/com/android/car/carlauncher/ControlledCarTaskView.java
deleted file mode 100644
index b2b0531..0000000
--- a/app/src/com/android/car/carlauncher/ControlledCarTaskView.java
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static com.android.car.carlauncher.TaskViewManager.DBG;
-
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityOptions;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.os.UserManager;
-import android.util.Log;
-import android.view.Display;
-import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-
-import java.util.Set;
-import java.util.concurrent.Executor;
-
-/**
- * A controlled {@link CarTaskView} is fully managed by the {@link TaskViewManager}.
- * The underlying task will be restarted if it is crashed.
- *
- * It should be used when:
- * <ul>
- *     <li>The underlying task is meant to be started by the host and be there forever.</li>
- * </ul>
- */
-final class ControlledCarTaskView extends CarTaskView {
-    private static final String TAG = ControlledCarTaskView.class.getSimpleName();
-
-    private final Executor mCallbackExecutor;
-    private final ControlledCarTaskViewCallbacks mCallbacks;
-    private final UserManager mUserManager;
-    private final TaskViewManager mTaskViewManager;
-    private final ControlledCarTaskViewConfig mConfig;
-    @Nullable private RunnerWithBackoff mStartActivityWithBackoff;
-
-    ControlledCarTaskView(
-            Activity context,
-            ShellTaskOrganizer organizer,
-            TaskViewTransitions taskViewTransitions,
-            SyncTransactionQueue syncQueue,
-            Executor callbackExecutor,
-            ControlledCarTaskViewConfig controlledCarTaskViewConfig,
-            ControlledCarTaskViewCallbacks callbacks,
-            UserManager userManager,
-            TaskViewManager taskViewManager) {
-        super(context, organizer, taskViewTransitions, syncQueue, true);
-        mCallbackExecutor = callbackExecutor;
-        mConfig = controlledCarTaskViewConfig;
-        mCallbacks = callbacks;
-        mUserManager = userManager;
-        mTaskViewManager = taskViewManager;
-
-        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this));
-        if (mConfig.mAutoRestartOnCrash) {
-            mStartActivityWithBackoff = new RunnerWithBackoff(this::startActivityInternal);
-        }
-    }
-
-    @Override
-    protected void onCarTaskViewInitialized() {
-        super.onCarTaskViewInitialized();
-        startActivity();
-        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady());
-    }
-
-    /**
-     * Starts the underlying activity.
-     */
-    public void startActivity() {
-        if (mStartActivityWithBackoff == null) {
-            startActivityInternal();
-            return;
-        }
-        mStartActivityWithBackoff.stop();
-        mStartActivityWithBackoff.start();
-    }
-
-    private void stopTheStartActivityBackoffIfExists() {
-        if (mStartActivityWithBackoff == null) {
-            if (DBG) {
-                Log.d(TAG, "mStartActivityWithBackoff is not present.");
-            }
-            return;
-        }
-        mStartActivityWithBackoff.stop();
-    }
-
-    private void startActivityInternal() {
-        if (!mUserManager.isUserUnlocked()) {
-            if (DBG) Log.d(TAG, "Can't start activity due to user is isn't unlocked");
-            return;
-        }
-
-        // Don't start activity when the display is off. This can happen when the taskview is not
-        // attached to a window.
-        if (getDisplay() == null) {
-            Log.w(TAG, "Can't start activity because display is not available in "
-                    + "taskview yet.");
-            return;
-        }
-        // Don't start activity when the display is off for ActivityVisibilityTests.
-        if (getDisplay().getState() != Display.STATE_ON) {
-            Log.w(TAG, "Can't start activity due to the display is off");
-            return;
-        }
-
-        ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext,
-                /* enterResId= */ 0, /* exitResId= */ 0);
-        Rect launchBounds = new Rect();
-        getBoundsOnScreen(launchBounds);
-        if (DBG) {
-            Log.d(TAG, "Starting (" + mConfig.mActivityIntent.getComponent() + ") on "
-                    + launchBounds);
-        }
-        Intent fillInIntent = null;
-        if ((mConfig.mActivityIntent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0) {
-            fillInIntent = new Intent().addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-        }
-        startActivity(
-                PendingIntent.getActivity(mContext, /* requestCode= */ 0,
-                        mConfig.mActivityIntent,
-                        PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT),
-                fillInIntent, options, launchBounds);
-    }
-
-    /** Gets the config used to build this controlled car task view. */
-    ControlledCarTaskViewConfig getConfig() {
-        return mConfig;
-    }
-
-    /**
-     * See {@link ControlledCarTaskViewCallbacks#getDependingPackageNames()}.
-     */
-    Set<String> getDependingPackageNames() {
-        return mCallbacks.getDependingPackageNames();
-    }
-
-    @Override
-    public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
-        super.onTaskAppeared(taskInfo, leash);
-        // Stop the start activity backoff because a task has already appeared.
-        stopTheStartActivityBackoffIfExists();
-    }
-
-    @Override
-    public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-        super.onTaskVanished(taskInfo);
-        if (mConfig.mAutoRestartOnCrash && mTaskViewManager.isHostVisible()) {
-            // onTaskVanished can be called when the host is in the background. In this case
-            // embedded activity should not be started.
-            Log.i(TAG, "Restarting task " + taskInfo.baseActivity
-                    + " in ControlledCarTaskView");
-            startActivity();
-        }
-    }
-
-    @Override
-    void showEmbeddedTask(WindowContainerTransaction wct) {
-        if (getTaskInfo() == null) {
-            if (DBG) {
-                Log.d(TAG, "Embedded task not available, starting it now.");
-            }
-            startActivity();
-            return;
-        }
-        super.showEmbeddedTask(wct);
-    }
-
-    @Override
-    public void release() {
-        super.release();
-        stopTheStartActivityBackoffIfExists();
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/ControlledCarTaskViewCallbacks.java b/app/src/com/android/car/carlauncher/ControlledCarTaskViewCallbacks.java
deleted file mode 100644
index 56ef8ea..0000000
--- a/app/src/com/android/car/carlauncher/ControlledCarTaskViewCallbacks.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import java.util.Collections;
-import java.util.Set;
-
-/**
- * A callback interface for {@link ControlledCarTaskView}.
- */
-public interface ControlledCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {
-    /**
-     * @return a set of package names which the task in the ControlledCarTaskView depends upon.
-     * When any of these packages are changed, it will lead to restart of the task.
-     */
-    default Set<String> getDependingPackageNames() {
-        return Collections.emptySet();
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/ControlledCarTaskViewConfig.java b/app/src/com/android/car/carlauncher/ControlledCarTaskViewConfig.java
deleted file mode 100644
index e07a0af..0000000
--- a/app/src/com/android/car/carlauncher/ControlledCarTaskViewConfig.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import android.content.Intent;
-
-/** This class provides the required configuration to create a {@link ControlledCarTaskView}. */
-public final class ControlledCarTaskViewConfig {
-    final Intent mActivityIntent;
-    // TODO(b/242861717): When mAutoRestartOnCrash is enabled, mPackagesThatCanRestart doesn't make
-    // a lot of sense. Consider removing it when there is more confidence with mAutoRestartOnCrash.
-    final boolean mAutoRestartOnCrash;
-    final boolean mCaptureGestures;
-    final boolean mCaptureLongPress;
-
-    private ControlledCarTaskViewConfig(
-            Intent activityIntent,
-            boolean autoRestartOnCrash,
-            boolean captureGestures,
-            boolean captureLongPress) {
-        mActivityIntent = activityIntent;
-        mAutoRestartOnCrash = autoRestartOnCrash;
-        mCaptureGestures = captureGestures;
-        mCaptureLongPress = captureLongPress;
-    }
-
-    /**
-     * Creates a {@link Builder} object that is used to create instances of {@link
-     * ControlledCarTaskViewConfig}.
-     */
-    public static Builder builder() {
-        return new Builder();
-    }
-
-    /** A builder class for {@link ControlledCarTaskViewConfig}. */
-    public static final class Builder {
-        private Intent mActivityIntent;
-        private boolean mAutoRestartOnCrash;
-        private boolean mCaptureGestures;
-        private boolean mCaptureLongPress;
-
-        private Builder() {}
-
-        /**
-         * The intent of the activity that is meant to be started in this {@link
-         * ControlledCarTaskView}.
-         */
-        public Builder setActivityIntent(Intent activityIntent) {
-            mActivityIntent = activityIntent;
-            return this;
-        }
-
-        /**
-         * Sets the auto restart functionality. If set, the {@link ControlledCarTaskView} will
-         * restart the task by re-launching the intent set via {@link #setActivityIntent(Intent)}
-         * when the task crashes.
-         */
-        public Builder setAutoRestartOnCrash(boolean autoRestartOnCrash) {
-            mAutoRestartOnCrash = autoRestartOnCrash;
-            return this;
-        }
-
-        /**
-         * Enables the swipe gesture capturing over {@link ControlledCarTaskView}. When enabled, the
-         * swipe gestures won't be sent to the embedded app and will instead be forwarded to the
-         * host activity.
-         */
-        public Builder setCaptureGestures(boolean captureGestures) {
-            mCaptureGestures = captureGestures;
-            return this;
-        }
-
-        /**
-         * Enables the long press capturing over {@link ControlledCarTaskView}. When enabled, the
-         * long press won't be sent to the embedded app and will instead be sent to the listener
-         * specified via {@link
-         * ControlledCarTaskView#setOnLongClickListener(View.OnLongClickListener)}.
-         *
-         * <p>If disabled, the listener supplied via {@link
-         * ControlledCarTaskView#setOnLongClickListener(View.OnLongClickListener)} won't be called.
-         */
-        public Builder setCaptureLongPress(boolean captureLongPress) {
-            mCaptureLongPress = captureLongPress;
-            return this;
-        }
-
-        /** Creates the {@link ControlledCarTaskViewConfig} object. */
-        public ControlledCarTaskViewConfig build() {
-            if (mActivityIntent == null) {
-                throw new IllegalArgumentException("mActivityIntent can't be null");
-            }
-            return new ControlledCarTaskViewConfig(
-                    mActivityIntent, mAutoRestartOnCrash, mCaptureGestures, mCaptureLongPress);
-        }
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskView.java b/app/src/com/android/car/carlauncher/LaunchRootCarTaskView.java
deleted file mode 100644
index 7f78a5c..0000000
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskView.java
+++ /dev/null
@@ -1,277 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.car.carlauncher.TaskViewManager.DBG;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.car.app.CarActivityManager;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * A {@link CarTaskView} that can act as a default app container. A default app container is the
- * container where all apps open by default.
- */
-final class LaunchRootCarTaskView extends CarTaskView {
-    private static final String TAG = LaunchRootCarTaskView.class.getSimpleName();
-
-    private final Executor mCallbackExecutor;
-    private final LaunchRootCarTaskViewCallbacks mCallbacks;
-    private final ShellTaskOrganizer mShellTaskOrganizer;
-    private final SyncTransactionQueue mSyncQueue;
-    // Linked hash map is used to keep the tasks ordered as per the actual stack inside the root
-    // task. Whenever a task becomes visible, it is bumped to the top of the stack.
-    private final LinkedHashMap<Integer, ActivityManager.RunningTaskInfo> mLaunchRootStack =
-            new LinkedHashMap<>();
-    private final AtomicReference<CarActivityManager> mCarActivityManagerRef;
-
-    private ActivityManager.RunningTaskInfo mLaunchRootTask;
-
-    private final ShellTaskOrganizer.TaskListener mRootTaskListener =
-            new ShellTaskOrganizer.TaskListener() {
-                @Override
-                public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
-                        SurfaceControl leash) {
-                    // The first call to onTaskAppeared() is always for the root-task.
-                    if (mLaunchRootTask == null && !taskInfo.hasParentTask()) {
-                        setRootTaskAsLaunchRoot(taskInfo);
-                        LaunchRootCarTaskView.this.dispatchTaskAppeared(taskInfo, leash);
-                        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady());
-                        if (DBG) {
-                            Log.d(TAG, "got onTaskAppeared for the launch root task. Not "
-                                    + "forwarding this to car activity manager");
-                        }
-                        return;
-                    }
-
-                    if (DBG) {
-                        Log.d(TAG, "launchRootCarTaskView onTaskAppeared " + taskInfo.taskId
-                                + " - " + taskInfo.baseActivity);
-                    }
-
-                    // TODO(b/228077499): Fix for the case when a task is started in the
-                    // launch-root-task right after the initialization of launch-root-task, it
-                    // remains blank.
-                    mSyncQueue.runInSync(t -> t.show(leash));
-
-                    CarActivityManager carAm = mCarActivityManagerRef.get();
-                    if (carAm != null) {
-                        carAm.onTaskAppeared(taskInfo);
-                        mLaunchRootStack.put(taskInfo.taskId, taskInfo);
-                    } else {
-                        Log.w(TAG, "CarActivityManager is null, skip onTaskAppeared: TaskInfo"
-                                + " = " + taskInfo);
-                    }
-                }
-
-                @Override
-                public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (mLaunchRootTask != null
-                            && mLaunchRootTask.taskId == taskInfo.taskId) {
-                        LaunchRootCarTaskView.this.dispatchTaskInfoChanged(taskInfo);
-                        if (DBG) {
-                            Log.d(TAG, "got onTaskInfoChanged for the launch root task. Not "
-                                    + "forwarding this to car activity manager");
-                        }
-                        return;
-                    }
-                    if (DBG) {
-                        Log.d(TAG, "launchRootCarTaskView onTaskInfoChanged "
-                                + taskInfo.taskId + " - base=" + taskInfo.baseActivity + " - top="
-                                + taskInfo.topActivity);
-                    }
-
-                    // Uncontrolled apps by default launch in the launch root so nothing needs to
-                    // be done here for them.
-                    CarActivityManager carAm = mCarActivityManagerRef.get();
-                    if (carAm != null) {
-                        carAm.onTaskInfoChanged(taskInfo);
-                        if (taskInfo.isVisible && mLaunchRootStack.containsKey(taskInfo.taskId)) {
-                            // Remove the task and insert again so that it jumps to the end of
-                            // the queue.
-                            mLaunchRootStack.remove(taskInfo.taskId);
-                            mLaunchRootStack.put(taskInfo.taskId, taskInfo);
-                        }
-                    } else {
-                        Log.w(TAG, "CarActivityManager is null, skip onTaskInfoChanged: TaskInfo"
-                                + " = " + taskInfo);
-                    }
-                }
-
-                @Override
-                public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (DBG) {
-                        Log.d(TAG, "launchRootCarTaskView onTaskVanished " + taskInfo.taskId
-                                + " - " + taskInfo.baseActivity);
-                    }
-                    if (mLaunchRootTask != null
-                            && mLaunchRootTask.taskId == taskInfo.taskId) {
-                        LaunchRootCarTaskView.this.dispatchTaskVanished(taskInfo);
-                        if (DBG) {
-                            Log.d(TAG, "got onTaskVanished for the launch root task. Not "
-                                    + "forwarding this to car activity manager");
-                        }
-                        return;
-                    }
-
-                    CarActivityManager carAm = mCarActivityManagerRef.get();
-                    if (carAm != null) {
-                        carAm.onTaskVanished(taskInfo);
-                        if (mLaunchRootStack.containsKey(taskInfo.taskId)) {
-                            mLaunchRootStack.remove(taskInfo.taskId);
-                        }
-                    } else {
-                        Log.w(TAG, "CarActivityManager is null, skip onTaskAppeared: TaskInfo"
-                                + " = " + taskInfo);
-                    }
-                }
-
-                @Override
-                public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (mLaunchRootStack.size() == 1) {
-                        Log.d(TAG, "Cannot remove last task from launch root.");
-                        return;
-                    }
-                    if (mLaunchRootStack.size() == 0) {
-                        Log.d(TAG, "Launch root is empty, do nothing.");
-                        return;
-                    }
-
-                    ActivityManager.RunningTaskInfo topTask = getTopTaskInLaunchRootTask();
-                    WindowContainerTransaction wct = new WindowContainerTransaction();
-                    // removeTask() will trigger onTaskVanished which will remove the task locally
-                    // from mLaunchRootStack
-                    wct.removeTask(topTask.token);
-                    mSyncQueue.queue(wct);
-                }
-            };
-
-    public LaunchRootCarTaskView(Activity context,
-            ShellTaskOrganizer organizer,
-            TaskViewTransitions taskViewTransitions,
-            SyncTransactionQueue syncQueue,
-            Executor callbackExecutor,
-            LaunchRootCarTaskViewCallbacks callbacks,
-            AtomicReference<CarActivityManager> carActivityManager) {
-        super(context, organizer, taskViewTransitions, syncQueue, false);
-        mCallbacks = callbacks;
-        mCallbackExecutor = callbackExecutor;
-        mShellTaskOrganizer = organizer;
-        mSyncQueue = syncQueue;
-        mCarActivityManagerRef = carActivityManager;
-
-        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this));
-    }
-
-    @Override
-    protected void onCarTaskViewInitialized() {
-        super.onCarTaskViewInitialized();
-        mShellTaskOrganizer.getExecutor().execute(() -> {
-            // removeWithTaskOrganizer should be true to signal the system that this root task is
-            // inside a TaskView and should not be animated by the core.
-            mShellTaskOrganizer.createRootTask(DEFAULT_DISPLAY,
-                    WINDOWING_MODE_MULTI_WINDOW,
-                    mRootTaskListener, /* removeWithTaskOrganizer= */ true);
-        });
-    }
-
-    @Override
-    public void release() {
-        super.release();
-        clearLaunchRootTask();
-    }
-
-    private void clearLaunchRootTask() {
-        if (mLaunchRootTask == null) {
-            Log.w(TAG, "Unable to clear launch root task because it is not created.");
-            return;
-        }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setLaunchRoot(mLaunchRootTask.token, null, null);
-        mSyncQueue.queue(wct);
-        // Should run on shell's executor
-        mShellTaskOrganizer.deleteRootTask(mLaunchRootTask.token);
-        mLaunchRootTask = null;
-    }
-
-    private void setRootTaskAsLaunchRoot(ActivityManager.RunningTaskInfo taskInfo) {
-        mLaunchRootTask = taskInfo;
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setLaunchRoot(taskInfo.token,
-                        new int[]{WINDOWING_MODE_FULLSCREEN, WINDOWING_MODE_UNDEFINED},
-                        new int[]{ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_RECENTS})
-                .reorder(taskInfo.token, true);
-        mSyncQueue.queue(wct);
-    }
-
-    public int getRootTaskCount() {
-        return mLaunchRootStack.size();
-    }
-
-    /**
-     * Returns the {@link android.app.ActivityManager.RunningTaskInfo} of the top task inside the
-     * launch root car task view.
-     */
-    public ActivityManager.RunningTaskInfo getTopTaskInLaunchRootTask() {
-        if (mLaunchRootStack.isEmpty()) {
-            return null;
-        }
-        ActivityManager.RunningTaskInfo topTask = null;
-        Iterator<ActivityManager.RunningTaskInfo> iterator = mLaunchRootStack.values().iterator();
-        while (iterator.hasNext()) {
-            topTask = iterator.next();
-        }
-        return topTask;
-    }
-
-    /**
-     * Updates the window visibility associated with the root task of this LaunchRootCarTaskView.
-     */
-    public void updateRootTaskVisibility(boolean visibility) {
-        if (mLaunchRootTask == null) {
-            return;
-        }
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setHidden(mLaunchRootTask.token, !visibility);
-        // TODO(304309584): remove showEmbeddedTask if better solution is found.
-        // Reorder the LaunchRootCarTaskView to avoid bringing host activity to the front when
-        // touching host activity.
-        if (!visibility) {
-            showEmbeddedTask(wct);
-        }
-        mSyncQueue.queue(wct);
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/RunnerWithBackoff.java b/app/src/com/android/car/carlauncher/RunnerWithBackoff.java
deleted file mode 100644
index 9e3ce14..0000000
--- a/app/src/com/android/car/carlauncher/RunnerWithBackoff.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import static com.android.car.carlauncher.TaskViewManager.DBG;
-
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Slog;
-
-/** A wrapper class for {@link Runnable} which retries in an exponential backoff manner. */
-final class RunnerWithBackoff {
-    private static final String TAG = RunnerWithBackoff.class.getSimpleName();
-    private static final int MAXIMUM_ATTEMPTS = 5;
-    private static final int FIRST_BACKOFF_TIME_MS = 1_000; // 1 second
-    private static final int MAXIMUM_BACKOFF_TIME_MS = 8000; // 8 seconds
-    private final Handler mHandler = new Handler(Looper.myLooper());
-    private final Runnable mAction;
-
-    private int mBackoffTimeMs;
-    private int mAttempts;
-
-    RunnerWithBackoff(Runnable action) {
-        mAction = action;
-    }
-
-    private final Runnable mRetryRunnable = new Runnable() {
-        @Override
-        public void run() {
-            if (mAttempts >= MAXIMUM_ATTEMPTS) {
-                Slog.e(TAG, "Failed to perform action, even after " + mAttempts + " attempts");
-                return;
-            }
-            if (DBG) {
-                Slog.d(TAG, "Executing the action. Attempt number " + mAttempts);
-            }
-            mAction.run();
-
-            mHandler.postDelayed(mRetryRunnable, mBackoffTimeMs);
-            increaseBackoff();
-            mAttempts++;
-        }
-    };
-
-    private void increaseBackoff() {
-        mBackoffTimeMs *= 2;
-        if (mBackoffTimeMs > MAXIMUM_BACKOFF_TIME_MS) {
-            mBackoffTimeMs = MAXIMUM_BACKOFF_TIME_MS;
-        }
-    }
-
-    /** Starts the retrying. The first try happens synchronously. */
-    public void start() {
-        if (DBG) {
-            Slog.d(TAG, "start backoff runner");
-        }
-        // Stop the existing retrying as a safeguard to prevent multiple starts.
-        stopInternal();
-
-        mBackoffTimeMs = FIRST_BACKOFF_TIME_MS;
-        mAttempts = 0;
-        // Call .run() instead of posting to handler so that first try can happen synchronously.
-        mRetryRunnable.run();
-    }
-
-    /** Stops the retrying. */
-    public void stop() {
-        if (DBG) {
-            Slog.d(TAG, "stop backoff runner");
-        }
-        stopInternal();
-    }
-
-    private void stopInternal() {
-        mHandler.removeCallbacks(mRetryRunnable);
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/SemiControlledCarTaskView.java b/app/src/com/android/car/carlauncher/SemiControlledCarTaskView.java
deleted file mode 100644
index 3a7b650..0000000
--- a/app/src/com/android/car/carlauncher/SemiControlledCarTaskView.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.android.car.carlauncher.TaskViewManager.DBG;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.car.app.CarActivityManager;
-import android.content.ComponentName;
-import android.util.Log;
-import android.view.SurfaceControl;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.VisibleForTesting;
-
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicReference;
-
-/**
- * A Semi-controlled {@link CarTaskView} is where the apps are meant to stay temporarily. It always
- * works when a {@link LaunchRootCarTaskView} has been set up.
- *
- * It serves these use-cases:
- * <ul>
- *     <li>Should be used when the apps that are meant to be in it can be started from anywhere
- *     in the system. i.e. when the host app has no control over their launching.</li>
- *     <li>Suitable for apps like Assistant or Setup-Wizard.</li>
- * </ul>
- */
-final class SemiControlledCarTaskView extends CarTaskView {
-    private static final String TAG = SemiControlledCarTaskView.class.getSimpleName();
-    private final Executor mCallbackExecutor;
-    private final SemiControlledCarTaskViewCallbacks mCallbacks;
-    private final ShellTaskOrganizer mShellTaskOrganizer;
-    private final SyncTransactionQueue mSyncQueue;
-    private final LinkedHashMap<Integer, ActivityManager.RunningTaskInfo> mChildrenTaskStack =
-            new LinkedHashMap<>();
-    private final ArrayList<ComponentName> mAllowListedActivities;
-    private final AtomicReference<CarActivityManager> mCarActivityManagerRef;
-
-    private ActivityManager.RunningTaskInfo mRootTask;
-
-    private final ShellTaskOrganizer.TaskListener mRootTaskListener =
-            new ShellTaskOrganizer.TaskListener() {
-                @Override
-                public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo,
-                        SurfaceControl leash) {
-                    // The first call to onTaskAppeared() is always for the root-task.
-                    if (mRootTask == null && !taskInfo.hasParentTask()) {
-                        setRootTask(taskInfo);
-                        SemiControlledCarTaskView.this.dispatchTaskAppeared(taskInfo, leash);
-
-                        CarActivityManager carAm = mCarActivityManagerRef.get();
-                        if (carAm != null) {
-                            carAm.setPersistentActivitiesOnRootTask(
-                                    mAllowListedActivities,
-                                    taskInfo.token.asBinder());
-                        } else {
-                            Log.wtf(TAG, "CarActivityManager is null, cannot call "
-                                    + "setPersistentActivitiesOnRootTask " + taskInfo);
-                        }
-                        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewReady());
-                        return;
-                    }
-
-                    if (DBG) {
-                        Log.d(TAG, "onTaskAppeared " + taskInfo.taskId + " - "
-                                + taskInfo.baseActivity);
-                    }
-
-                    mChildrenTaskStack.put(taskInfo.taskId, taskInfo);
-                }
-
-                @Override
-                public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (mRootTask != null && mRootTask.taskId == taskInfo.taskId) {
-                        SemiControlledCarTaskView.this.dispatchTaskInfoChanged(taskInfo);
-                    }
-                    if (DBG) {
-                        Log.d(TAG, "onTaskInfoChanged " + taskInfo.taskId + " - "
-                                + taskInfo.baseActivity);
-                    }
-                    if (taskInfo.isVisible && mChildrenTaskStack.containsKey(taskInfo.taskId)) {
-                        // Remove the task and insert again so that it jumps to the end of
-                        // the queue.
-                        mChildrenTaskStack.remove(taskInfo.taskId);
-                        mChildrenTaskStack.put(taskInfo.taskId, taskInfo);
-                    }
-                }
-
-                @Override
-                public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (mRootTask != null && mRootTask.taskId == taskInfo.taskId) {
-                        SemiControlledCarTaskView.this.dispatchTaskVanished(taskInfo);
-                    }
-                    if (DBG) {
-                        Log.d(TAG, "onTaskVanished " + taskInfo.taskId + " - "
-                                + taskInfo.baseActivity);
-                    }
-                    if (mChildrenTaskStack.containsKey(taskInfo.taskId)) {
-                        mChildrenTaskStack.remove(taskInfo.taskId);
-                    }
-                }
-
-                @Override
-                public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
-                    if (mChildrenTaskStack.size() == 0) {
-                        Log.d(TAG, "Root task is empty, do nothing.");
-                        return;
-                    }
-
-                    ActivityManager.RunningTaskInfo topTask = getTopTaskInTheRootTask();
-                    WindowContainerTransaction wct = new WindowContainerTransaction();
-                    // removeTask() will trigger onTaskVanished which will remove the task locally
-                    // from mChildrenTaskStack
-                    wct.removeTask(topTask.token);
-                    mSyncQueue.queue(wct);
-                }
-            };
-
-    public SemiControlledCarTaskView(Activity context,
-            ShellTaskOrganizer organizer,
-            TaskViewTransitions taskViewTransitions,
-            SyncTransactionQueue syncQueue,
-            Executor callbackExecutor,
-            List<ComponentName> allowListedActivities,
-            SemiControlledCarTaskViewCallbacks callbacks,
-            AtomicReference<CarActivityManager> carActivityManager) {
-        super(context, organizer, taskViewTransitions, syncQueue, true);
-        mCallbacks = callbacks;
-        mCallbackExecutor = callbackExecutor;
-        mCallbackExecutor.execute(() -> mCallbacks.onTaskViewCreated(this));
-        mShellTaskOrganizer = organizer;
-        mSyncQueue = syncQueue;
-        mAllowListedActivities = new ArrayList<>(allowListedActivities);
-        mCarActivityManagerRef = carActivityManager;
-    }
-
-    /**
-     * @return the underlying {@link SemiControlledCarTaskViewCallbacks}.
-     */
-    SemiControlledCarTaskViewCallbacks getCallbacks() {
-        return mCallbacks;
-    }
-
-    @Override
-    protected void onCarTaskViewInitialized() {
-        super.onCarTaskViewInitialized();
-        mShellTaskOrganizer.getExecutor().execute(() -> {
-            // removeWithTaskOrganizer should be true to signal the system that this root task is
-            // inside a TaskView and should not be animated by the core.
-            mShellTaskOrganizer.createRootTask(DEFAULT_DISPLAY,
-                    WINDOWING_MODE_MULTI_WINDOW,
-                    mRootTaskListener, /* removeWithTaskOrganizer= */ true);
-        });
-    }
-
-    @Override
-    public void release() {
-        super.release();
-        clearRootTask();
-    }
-
-    public ActivityManager.RunningTaskInfo getTopTaskInTheRootTask() {
-        if (mChildrenTaskStack.isEmpty()) {
-            return null;
-        }
-        ActivityManager.RunningTaskInfo topTask = null;
-        Iterator<ActivityManager.RunningTaskInfo> iterator = mChildrenTaskStack.values().iterator();
-        while (iterator.hasNext()) {
-            topTask = iterator.next();
-        }
-        return topTask;
-    }
-
-    private void clearRootTask() {
-        if (mRootTask == null) {
-            Log.w(TAG, "Unable to clear root task because it is not created.");
-            return;
-        }
-        // Should run on shell's executor
-        mShellTaskOrganizer.deleteRootTask(mRootTask.token);
-        mRootTask = null;
-    }
-
-    private void setRootTask(ActivityManager.RunningTaskInfo taskInfo) {
-        mRootTask = taskInfo;
-    }
-
-    /**
-     * Designates the given {@code activities} to be launched in this SemiControlledCarTaskView.
-     * <p>Note: If an activity is already associated with another SemiControlledCarTaskView, it's
-     * designates will be overridden.
-     *
-     * @param activities list of {@link ComponentName} of activities to be designated on the
-     *                   SemiControlledCarTaskView
-     */
-    public void addAllowListedActivities(List<ComponentName> activities) {
-        CarActivityManager carAm = mCarActivityManagerRef.get();
-        if (carAm == null) {
-            Log.wtf(TAG,
-                    "CarActivityManager is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " add activities");
-            return;
-        }
-        if (mRootTask == null) {
-            Log.wtf(TAG,
-                    "RootTask is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " add activities");
-            return;
-        }
-        List<ComponentName> activitiesToAdd = new ArrayList<>();
-        for (ComponentName activity : activities) {
-            if (!mAllowListedActivities.contains(activity)) {
-                activitiesToAdd.add(activity);
-            } else {
-                if (DBG) {
-                    Log.d(TAG, "Activity " + activity
-                            + " is already designated to this SemiControlledCarTaskView");
-                }
-            }
-        }
-        mAllowListedActivities.addAll(activitiesToAdd);
-        carAm.setPersistentActivitiesOnRootTask(activitiesToAdd, mRootTask.token.asBinder());
-    }
-
-    /**
-     * Remove the designation of the given {@code activities} to SemiControlledCarTaskView.
-     * <p>Note: If an activity is already associated with another SemiControlledCarTaskView, it's
-     * designates will be overridden.
-     *
-     * @param activities list of {@link ComponentName} of activities to be designated on the
-     *                   SemiControlledCarTaskView
-     */
-    public void removeAllowListedActivities(List<ComponentName> activities) {
-        CarActivityManager carAm = mCarActivityManagerRef.get();
-        if (carAm == null) {
-            Log.wtf(TAG,
-                    "CarActivityManager is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " remove activities");
-            return;
-        }
-        if (mRootTask == null) {
-            Log.wtf(TAG,
-                    "RootTask is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " add activities");
-            return;
-        }
-        List<ComponentName> activitiesToRemove = new ArrayList<>();
-        for (ComponentName activity : activities) {
-            if (mAllowListedActivities.contains(activity)) {
-                activitiesToRemove.add(activity);
-            } else {
-                if (DBG) {
-                    Log.d(TAG, "Activity " + activity
-                            + " was not designated to this SemiControlledCarTaskView");
-                }
-            }
-        }
-        mAllowListedActivities.removeAll(activitiesToRemove);
-        carAm.setPersistentActivitiesOnRootTask(activitiesToRemove, /* rootTaskToken= */ null);
-    }
-
-    /**
-     * Sets the designations for SemiControlledCarTaskView. Adds the designation of the given
-     * {@code activities} to SemiControlledCarTaskView and removes the designations of activities
-     * that are not in {@code activities}.
-     *
-     * <p>Note:
-     * If an activity is already associated with another SemiControlledCarTaskView, it's
-     * designates will be overridden.
-     *
-     * @param activities list of {@link ComponentName} of activities to be designated on the
-     *                   SemiControlledCarTaskView
-     */
-    public void setAllowListedActivities(List<ComponentName> activities) {
-        CarActivityManager carAm = mCarActivityManagerRef.get();
-        if (carAm == null) {
-            Log.wtf(TAG,
-                    "CarActivityManager is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " remove activities");
-            return;
-        }
-        if (mRootTask == null) {
-            Log.wtf(TAG,
-                    "RootTask is null, cannot call setPersistentActivitiesOnRootTask to"
-                            + " add activities");
-            return;
-        }
-        List<ComponentName> activitiesToRemove = new ArrayList<>(mAllowListedActivities);
-        carAm.setPersistentActivitiesOnRootTask(activitiesToRemove, /* rootTaskToken= */ null);
-        carAm.setPersistentActivitiesOnRootTask(activities, mRootTask.token.asBinder());
-        mAllowListedActivities.clear();
-        mAllowListedActivities.addAll(activities);
-    }
-
-    @VisibleForTesting
-    List<ComponentName> getPersistentActivities() {
-        return mAllowListedActivities;
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/SemiControlledCarTaskViewCallbacks.java b/app/src/com/android/car/carlauncher/SemiControlledCarTaskViewCallbacks.java
deleted file mode 100644
index 040356e..0000000
--- a/app/src/com/android/car/carlauncher/SemiControlledCarTaskViewCallbacks.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-/**
- * A callbacks interface for {@link SemiControlledCarTaskView}.
- */
-public interface SemiControlledCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {
-}
diff --git a/app/src/com/android/car/carlauncher/TaskViewInputInterceptor.java b/app/src/com/android/car/carlauncher/TaskViewInputInterceptor.java
deleted file mode 100644
index 12e771d..0000000
--- a/app/src/com/android/car/carlauncher/TaskViewInputInterceptor.java
+++ /dev/null
@@ -1,292 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.annotation.MainThread;
-import android.app.Activity;
-import android.app.Application;
-import android.content.Context;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.hardware.input.InputManager;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.GestureDetector;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.wm.shell.taskview.TaskView;
-
-import java.util.List;
-
-/**
- * This class is responsible to intercept the swipe gestures & long press over {@link
- * ControlledCarTaskView}.
- *
- * <ul>
- *   <li>The gesture interception will only occur when the corresponding {@link
- *       ControlledCarTaskViewConfig#mCaptureGestures} is set.
- *   <li>The long press interception will only occur when the corresponding {@link
- *       ControlledCarTaskViewConfig#mCaptureLongPress} is set.
- * </ul>
- */
-public final class TaskViewInputInterceptor {
-
-    private static final String TAG = TaskViewInputInterceptor.class.getSimpleName();
-    private static final boolean DBG = Log.isLoggable(CarLauncher.TAG, Log.DEBUG);
-
-    private static final Rect sTmpBounds = new Rect();
-
-    private final Activity mHostActivity;
-    private final InputManager mInputManager;
-    private final TaskViewManager mTaskViewManager;
-    private final WindowManager mWm;
-    private final GestureDetector mGestureDetector =
-            new GestureDetector(new TaskViewGestureListener());
-    private final Application.ActivityLifecycleCallbacks mActivityLifecycleCallbacks =
-            new ActivityLifecycleHandler();
-
-    private View mSpyWindow;
-    private boolean mInitialized = false;
-
-    TaskViewInputInterceptor(Activity hostActivity, TaskViewManager taskViewManager) {
-        mHostActivity = hostActivity;
-        mInputManager = hostActivity.getSystemService(InputManager.class);
-        mTaskViewManager = taskViewManager;
-        mWm = mHostActivity.getSystemService(WindowManager.class);
-    }
-
-    private static boolean isIn(MotionEvent event, TaskView taskView) {
-        taskView.getBoundsOnScreen(sTmpBounds);
-        return sTmpBounds.contains((int) event.getX(), (int) event.getY());
-    }
-
-    /** Initializes & starts intercepting gestures. Does nothing if already initialized. */
-    @MainThread
-    void init() {
-        if (mInitialized) {
-            Log.e(TAG, "Already initialized");
-            return;
-        }
-        mInitialized = true;
-        mHostActivity.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
-        startInterceptingGestures();
-    }
-
-    /**
-     * Releases the held resources and stops intercepting gestures. Does nothing if already
-     * released.
-     */
-    @MainThread
-    void release() {
-        if (!mInitialized) {
-            Log.e(TAG, "Failed to release as it is not initialized");
-            return;
-        }
-        mInitialized = false;
-        mHostActivity.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
-        stopInterceptingGestures();
-    }
-
-    private void startInterceptingGestures() {
-        if (DBG) {
-            Log.d(TAG, "Start intercepting gestures");
-        }
-        if (mSpyWindow != null) {
-            Log.d(TAG, "Already intercepting gestures");
-            return;
-        }
-        createAndAddSpyWindow();
-    }
-
-    private void stopInterceptingGestures() {
-        if (DBG) {
-            Log.d(TAG, "Stop intercepting gestures");
-        }
-        if (mSpyWindow == null) {
-            Log.d(TAG, "Already not intercepting gestures");
-            return;
-        }
-        removeSpyWindow();
-    }
-
-    private void createAndAddSpyWindow() {
-        mSpyWindow = new GestureSpyView(mHostActivity);
-        WindowManager.LayoutParams p =
-                new WindowManager.LayoutParams(
-                        ViewGroup.LayoutParams.MATCH_PARENT,
-                        ViewGroup.LayoutParams.MATCH_PARENT,
-                        TYPE_APPLICATION_OVERLAY,
-                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                                | FLAG_NOT_FOCUSABLE
-                                | FLAG_LAYOUT_IN_SCREEN,
-                        // LAYOUT_IN_SCREEN required so that event coordinate system matches the
-                        // taskview.getBoundsOnScreen coordinate system
-                        PixelFormat.TRANSLUCENT);
-        p.inputFeatures = INPUT_FEATURE_SPY;
-        p.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-        mWm.addView(mSpyWindow, p);
-    }
-
-    private void removeSpyWindow() {
-        if (mSpyWindow == null) {
-            Log.e(TAG, "Spy window is not present");
-            return;
-        }
-        mWm.removeView(mSpyWindow);
-        mSpyWindow = null;
-    }
-
-    private final class GestureSpyView extends View {
-        private boolean mConsumingCurrentEventStream = false;
-        private boolean mActionDownInsideTaskView = false;
-        private float mTouchDownX;
-        private float mTouchDownY;
-
-        GestureSpyView(Context context) {
-            super(context);
-        }
-
-        @Override
-        public boolean dispatchTouchEvent(MotionEvent event) {
-            boolean justToggled = false;
-            mGestureDetector.onTouchEvent(event);
-
-            if (event.getAction() == MotionEvent.ACTION_DOWN) {
-                mActionDownInsideTaskView = false;
-
-                List<ControlledCarTaskView> taskViewList =
-                        mTaskViewManager.getControlledTaskViews();
-                for (ControlledCarTaskView tv : taskViewList) {
-                    if (tv.getConfig().mCaptureGestures && isIn(event, tv)) {
-                        mTouchDownX = event.getX();
-                        mTouchDownY = event.getY();
-                        mActionDownInsideTaskView = true;
-                        break;
-                    }
-                }
-
-                // Stop consuming immediately on ACTION_DOWN
-                mConsumingCurrentEventStream = false;
-            }
-
-            if (event.getAction() == MotionEvent.ACTION_MOVE) {
-                if (!mConsumingCurrentEventStream && mActionDownInsideTaskView
-                        && Float.compare(mTouchDownX, event.getX()) != 0
-                        && Float.compare(mTouchDownY, event.getY()) != 0) {
-                    // Start consuming on ACTION_MOVE when ACTION_DOWN happened inside TaskView
-                    mConsumingCurrentEventStream = true;
-                    justToggled = true;
-                }
-
-                // Handling the events
-                if (mConsumingCurrentEventStream) {
-                    // Disable the propagation when consuming events.
-                    mInputManager.pilferPointers(getViewRootImpl().getInputToken());
-
-                    if (justToggled) {
-                        // When just toggled from DOWN to MOVE, dispatch a DOWN event as DOWN event
-                        // is meant to be the first event in an event stream.
-                        MotionEvent cloneEvent = MotionEvent.obtain(event);
-                        cloneEvent.setAction(MotionEvent.ACTION_DOWN);
-                        TaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(cloneEvent);
-                        cloneEvent.recycle();
-                    }
-                    TaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(event);
-                }
-            }
-
-            if (event.getAction() == MotionEvent.ACTION_UP) {
-                // Handling the events
-                if (mConsumingCurrentEventStream) {
-                    // Disable the propagation when handling manually.
-                    mInputManager.pilferPointers(getViewRootImpl().getInputToken());
-                    TaskViewInputInterceptor.this.mHostActivity.dispatchTouchEvent(event);
-                }
-                mConsumingCurrentEventStream = false;
-            }
-            return false;
-        }
-    }
-
-    private final class TaskViewGestureListener extends GestureDetector.SimpleOnGestureListener {
-        @Override
-        public void onLongPress(@NonNull MotionEvent e) {
-            List<ControlledCarTaskView> taskViewList = mTaskViewManager.getControlledTaskViews();
-            for (ControlledCarTaskView tv : taskViewList) {
-                if (tv.getConfig().mCaptureLongPress && isIn(e, tv)) {
-                    if (DBG) {
-                        Log.d(TAG, "Long press captured for taskView: " + tv);
-                    }
-                    mInputManager.pilferPointers(mSpyWindow.getViewRootImpl().getInputToken());
-                    if (tv.getOnLongClickListener() != null) {
-                        tv.getOnLongClickListener().onLongClick(tv);
-                    }
-                    return;
-                }
-            }
-            if (DBG) {
-                Log.d(TAG, "Long press not captured");
-            }
-        }
-    }
-
-    private final class ActivityLifecycleHandler implements Application.ActivityLifecycleCallbacks {
-        @Override
-        public void onActivityCreated(
-                @NonNull Activity activity, @Nullable Bundle savedInstanceState) {}
-
-        @Override
-        public void onActivityStarted(@NonNull Activity activity) {
-            if (!mInitialized) {
-                return;
-            }
-            startInterceptingGestures();
-        }
-
-        @Override
-        public void onActivityResumed(@NonNull Activity activity) {}
-
-        @Override
-        public void onActivityPaused(@NonNull Activity activity) {}
-
-        @Override
-        public void onActivityStopped(@NonNull Activity activity) {
-            if (!mInitialized) {
-                return;
-            }
-            stopInterceptingGestures();
-        }
-
-        @Override
-        public void onActivitySaveInstanceState(
-                @NonNull Activity activity, @NonNull Bundle outState) {}
-
-        @Override
-        public void onActivityDestroyed(@NonNull Activity activity) {}
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/TaskViewManager.java b/app/src/com/android/car/carlauncher/TaskViewManager.java
deleted file mode 100644
index 9d29856..0000000
--- a/app/src/com/android/car/carlauncher/TaskViewManager.java
+++ /dev/null
@@ -1,610 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.car.carlauncher;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
-import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
-
-import static com.android.car.carlauncher.CarLauncher.TAG;
-import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.Application.ActivityLifecycleCallbacks;
-import android.app.TaskInfo;
-import android.app.TaskStackListener;
-import android.car.Car;
-import android.car.app.CarActivityManager;
-import android.car.user.CarUserManager;
-import android.car.user.UserLifecycleEventFilter;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.UserManager;
-import android.util.Log;
-import android.util.Slog;
-import android.view.WindowManagerGlobal;
-import android.window.TaskAppearedInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.car.carlauncher.taskstack.TaskStackChangeListeners;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.HandlerExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.common.TransactionPool;
-import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.fullscreen.FullscreenTaskListener;
-import com.android.wm.shell.startingsurface.StartingWindowController;
-import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.taskview.TaskViewTransitions;
-import com.android.wm.shell.transition.HomeTransitionObserver;
-import com.android.wm.shell.transition.Transitions;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicReference;
-
-
-/**
- * A manager for creating {@link ControlledCarTaskView}, {@link LaunchRootCarTaskView} &
- * {@link SemiControlledCarTaskView}.
- */
-public final class TaskViewManager {
-    static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-    private static final String SCHEME_PACKAGE = "package";
-
-    private final AtomicReference<CarActivityManager> mCarActivityManagerRef =
-            new AtomicReference<>();
-    @ShellMainThread
-    private final HandlerExecutor mShellExecutor;
-    private final SyncTransactionQueue mSyncQueue;
-    private final Transitions mTransitions;
-    private final TaskViewTransitions mTaskViewTransitions;
-    private final ShellTaskOrganizer mTaskOrganizer;
-    private final int mHostTaskId;
-
-    // All TaskView are bound to the Host Activity if it exists.
-    @ShellMainThread
-    private final List<ControlledCarTaskView> mControlledTaskViews = new ArrayList<>();
-    @ShellMainThread
-    private final List<SemiControlledCarTaskView> mSemiControlledTaskViews = new ArrayList<>();
-    @ShellMainThread
-    private LaunchRootCarTaskView mLaunchRootCarTaskView = null;
-
-    private TaskViewInputInterceptor mTaskViewInputInterceptor;
-    private CarUserManager mCarUserManager;
-    private Activity mContext;
-    private Car mCar;
-    private boolean mReleased = false;
-
-    private final TaskStackListener mTaskStackListener = new TaskStackListener() {
-        @Override
-        public void onTaskFocusChanged(int taskId, boolean focused) {
-            boolean hostFocused = taskId == mHostTaskId && focused;
-            if (DBG) {
-                Log.d(TAG, "onTaskFocusChanged: taskId=" + taskId
-                        + ", hostFocused=" + hostFocused);
-            }
-            if (!hostFocused) {
-                return;
-            }
-
-            for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
-                ControlledCarTaskView taskView = mControlledTaskViews.get(i);
-                if (taskView.getTaskId() == INVALID_TASK_ID) {
-                    // If the task in TaskView is crashed when host is in background,
-                    // We'd like to restart it when host becomes foreground and focused.
-                    taskView.startActivity();
-                }
-            }
-        }
-
-        @Override
-        public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
-                boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
-            if (DBG) {
-                Log.d(TAG, "onActivityRestartAttempt: taskId=" + task.taskId
-                        + ", homeTaskVisible=" + homeTaskVisible + ", wasVisible=" + wasVisible);
-            }
-            if (mHostTaskId != task.taskId) {
-                return;
-            }
-            showEmbeddedTasks();
-        }
-    };
-
-    private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
-        if (DBG) {
-            Log.d(TAG, "UserLifecycleListener.onEvent: For User "
-                    + mContext.getUserId()
-                    + ", received an event " + event);
-        }
-
-        // When user-unlocked, if task isn't launched yet, then try to start it.
-        if (event.getEventType() == USER_LIFECYCLE_EVENT_TYPE_UNLOCKED
-                && mContext.getUserId() == event.getUserId()) {
-            for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
-                ControlledCarTaskView taskView = mControlledTaskViews.get(i);
-                if (taskView.getTaskId() == INVALID_TASK_ID) {
-                    taskView.startActivity();
-                }
-            }
-        }
-
-        // When user-switching, onDestroy in the previous user's Host app isn't called.
-        // So try to release the resource explicitly.
-        if (event.getEventType() == USER_LIFECYCLE_EVENT_TYPE_SWITCHING
-                && mContext.getUserId() == event.getPreviousUserId()) {
-            release();
-        }
-    };
-
-    private final BroadcastReceiver mPackageBroadcastReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DBG) Log.d(TAG, "onReceive: intent=" + intent);
-
-            if (!isHostVisible()) {
-                return;
-            }
-
-            String packageName = intent.getData().getSchemeSpecificPart();
-            for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
-                ControlledCarTaskView taskView = mControlledTaskViews.get(i);
-                if (taskView.getTaskId() == INVALID_TASK_ID
-                        && taskView.getDependingPackageNames().contains(packageName)) {
-                    taskView.startActivity();
-                }
-            }
-        }
-    };
-
-    public TaskViewManager(Activity context, Handler mainHandler) {
-        this(context, mainHandler, new HandlerExecutor(mainHandler));
-    }
-
-    private TaskViewManager(Activity context, Handler mainHandler,
-            HandlerExecutor handlerExecutor) {
-        this(context, mainHandler, handlerExecutor, new ShellTaskOrganizer(handlerExecutor),
-                new TransactionPool(), new ShellCommandHandler(), new ShellInit(handlerExecutor));
-    }
-
-    private TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor,
-            ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool,
-            ShellCommandHandler shellCommandHandler, ShellInit shellinit) {
-        this(context, mainHandler, handlerExecutor, taskOrganizer,
-                transactionPool,
-                shellinit,
-                new ShellController(context, shellinit, shellCommandHandler, handlerExecutor),
-                new DisplayController(context,
-                        WindowManagerGlobal.getWindowManagerService(), shellinit, handlerExecutor)
-        );
-    }
-
-    private TaskViewManager(Activity context, Handler mainHandler, HandlerExecutor handlerExecutor,
-            ShellTaskOrganizer taskOrganizer, TransactionPool transactionPool, ShellInit shellinit,
-            ShellController shellController, DisplayController dc) {
-        this(context, handlerExecutor, taskOrganizer,
-                new SyncTransactionQueue(transactionPool, handlerExecutor),
-                new Transitions(context, shellinit, shellController, taskOrganizer,
-                        transactionPool, dc, handlerExecutor, mainHandler, handlerExecutor,
-                        new HomeTransitionObserver(context, handlerExecutor)),
-                shellinit,
-                shellController,
-                new StartingWindowController(context, shellinit,
-                        shellController,
-                        taskOrganizer,
-                        handlerExecutor,
-                        new PhoneStartingWindowTypeAlgorithm(),
-                        new IconProvider(context),
-                        transactionPool));
-    }
-
-    @VisibleForTesting
-    TaskViewManager(Activity context, HandlerExecutor handlerExecutor,
-            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
-            Transitions transitions, ShellInit shellInit, ShellController shellController,
-            StartingWindowController startingWindowController) {
-        if (DBG) Slog.d(TAG, "TaskViewManager(), u=" + context.getUserId());
-        mContext = context;
-        mShellExecutor = handlerExecutor;
-        mTaskOrganizer = shellTaskOrganizer;
-        mHostTaskId = mContext.getTaskId();
-        mSyncQueue = syncQueue;
-        mTransitions = transitions;
-        mTaskViewTransitions = new TaskViewTransitions(mTransitions);
-        mTaskViewInputInterceptor = new TaskViewInputInterceptor(context, this);
-
-        initCar();
-        shellInit.init();
-        initTaskOrganizer(mCarActivityManagerRef);
-        mContext.registerActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
-    }
-
-    private void initCar() {
-        mCar = Car.createCar(/* context= */ mContext, /* handler= */ null,
-                Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
-                (car, ready) -> {
-                    if (!ready) {
-                        Log.w(TAG, "CarService looks crashed");
-                        mCarActivityManagerRef.set(null);
-                        return;
-                    }
-                    setCarUserManager((CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE));
-                    UserLifecycleEventFilter filter = new UserLifecycleEventFilter.Builder()
-                            .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED)
-                            .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING).build();
-                    mCarUserManager.addListener(mContext.getMainExecutor(), filter,
-                            mUserLifecycleListener);
-                    CarActivityManager carAM = (CarActivityManager) car.getCarManager(
-                            Car.CAR_ACTIVITY_SERVICE);
-                    mCarActivityManagerRef.set(carAM);
-
-                    carAM.registerTaskMonitor();
-                });
-
-        TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
-
-        IntentFilter packageIntentFilter = new IntentFilter(Intent.ACTION_PACKAGE_REPLACED);
-        packageIntentFilter.addDataScheme(SCHEME_PACKAGE);
-        mContext.registerReceiver(mPackageBroadcastReceiver, packageIntentFilter);
-    }
-
-    // TODO(b/239958124A): Remove this method when unit tests for TaskViewManager have been added.
-    /**
-     * This method only exists for the container activity to set mock car user manager in tests.
-     */
-    void setCarUserManager(CarUserManager carUserManager) {
-        mCarUserManager = carUserManager;
-    }
-
-    private Transitions initTransitions(ShellInit shellInit, TransactionPool txPool,
-            ShellController shellController, Handler mainHandler) {
-        DisplayController dc = new DisplayController(mContext,
-                WindowManagerGlobal.getWindowManagerService(), shellInit, mShellExecutor);
-        return new Transitions(mContext, shellInit, shellController, mTaskOrganizer,
-                txPool, dc, mShellExecutor, mainHandler, mShellExecutor,
-                new HomeTransitionObserver(mContext, mShellExecutor));
-    }
-
-    private void initTaskOrganizer(AtomicReference<CarActivityManager> carActivityManagerRef) {
-        FullscreenTaskListener fullscreenTaskListener = new CarFullscreenTaskMonitorListener(
-                carActivityManagerRef, mSyncQueue);
-        mTaskOrganizer.addListenerForType(fullscreenTaskListener, TASK_LISTENER_TYPE_FULLSCREEN);
-        List<TaskAppearedInfo> taskAppearedInfos = mTaskOrganizer.registerOrganizer();
-        cleanUpExistingTaskViewTasks(taskAppearedInfos);
-    }
-
-    /**
-     * Creates a {@link ControlledCarTaskView}.
-     *
-     * @param callbackExecutor the executor which the {@link ControlledCarTaskViewCallbacks} will
-     *                         be executed on.
-     * @param controlledCarTaskViewConfig the configuration for the underlying
-     * {@link ControlledCarTaskView}.
-     * @param taskViewCallbacks the callbacks for the underlying TaskView.
-     */
-    public void createControlledCarTaskView(
-            Executor callbackExecutor,
-            ControlledCarTaskViewConfig controlledCarTaskViewConfig,
-            ControlledCarTaskViewCallbacks taskViewCallbacks) {
-        mShellExecutor.execute(() -> {
-            ControlledCarTaskView taskView = new ControlledCarTaskView(mContext, mTaskOrganizer,
-                    mTaskViewTransitions, mSyncQueue, callbackExecutor, controlledCarTaskViewConfig,
-                    taskViewCallbacks, mContext.getSystemService(UserManager.class), this);
-            mControlledTaskViews.add(taskView);
-
-            if (controlledCarTaskViewConfig.mCaptureGestures
-                    || controlledCarTaskViewConfig.mCaptureLongPress) {
-                mTaskViewInputInterceptor.init();
-            }
-        });
-
-    }
-
-    /**
-     * Creates a {@link LaunchRootCarTaskView}.
-     *
-     * @param callbackExecutor the executor which the {@link LaunchRootCarTaskViewCallbacks} will be
-     *                         executed on.
-     * @param taskViewCallbacks the callbacks for the underlying TaskView.
-     */
-    public void createLaunchRootTaskView(Executor callbackExecutor,
-            LaunchRootCarTaskViewCallbacks taskViewCallbacks) {
-        mShellExecutor.execute(() -> {
-            if (mLaunchRootCarTaskView != null) {
-                throw new IllegalStateException("Cannot create more than one launch root task");
-            }
-            mLaunchRootCarTaskView = new LaunchRootCarTaskView(mContext, mTaskOrganizer,
-                    mTaskViewTransitions, mSyncQueue,
-                    callbackExecutor, taskViewCallbacks, mCarActivityManagerRef);
-        });
-    }
-
-    /**
-     * Updates the window visibility associated with {@link WindowContainerToken}.
-     *
-     * @param token {@link WindowContainerToken} of the window that needs to be hidden
-     * @param visibility {true} if window needs to be displayed {false} otherwise
-     */
-    public void updateTaskVisibility(WindowContainerToken token, boolean visibility) {
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        wct.setHidden(token, !visibility);
-        mSyncQueue.queue(wct);
-    }
-
-    /**
-     * Updates the window visibility associated with the launch root task of
-     * {@link mLaunchRootCarTaskView}.
-     *
-     * @param visibility {true} if window needs to be displayed {false} otherwise
-     */
-    public void updateLaunchRootCarTaskVisibility(boolean visibility) {
-        if (mLaunchRootCarTaskView == null) {
-            return;
-        }
-        mLaunchRootCarTaskView.updateRootTaskVisibility(visibility);
-    }
-
-    /**
-     * Creates a {@link SemiControlledCarTaskView}.
-     *
-     * @param callbackExecutor the executor which the {@link SemiControlledCarTaskViewCallbacks}
-     *                         will be executed on.
-     * @param allowListedActivities the list of activities that will always be started in this
-     *                              taskview.
-     * @param taskViewCallbacks the callbacks for the underlying TaskView.
-     */
-    public void createSemiControlledTaskView(Executor callbackExecutor,
-            List<ComponentName> allowListedActivities,
-            SemiControlledCarTaskViewCallbacks taskViewCallbacks) {
-        mShellExecutor.execute(() -> {
-            SemiControlledCarTaskView taskView = new SemiControlledCarTaskView(mContext,
-                    mTaskOrganizer, mTaskViewTransitions, mSyncQueue,
-                    callbackExecutor, allowListedActivities, taskViewCallbacks,
-                    mCarActivityManagerRef);
-            mSemiControlledTaskViews.add(taskView);
-        });
-    }
-
-    /**
-     * Releases {@link TaskViewManager} and unregisters the underlying {@link ShellTaskOrganizer}.
-     * It also removes all TaskViews which are created by this {@link TaskViewManager}.
-     */
-    void release() {
-        mShellExecutor.execute(() -> {
-            if (DBG) Slog.d(TAG, "TaskViewManager.release, u=" + mContext.getUser());
-
-            if (mCarUserManager != null) {
-                mCarUserManager.removeListener(mUserLifecycleListener);
-            }
-            TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
-            mContext.unregisterReceiver(mPackageBroadcastReceiver);
-
-            CarActivityManager carAM = mCarActivityManagerRef.get();
-            if (carAM != null) {
-                carAM.unregisterTaskMonitor();
-                mCarActivityManagerRef.set(null);
-            }
-
-            for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
-                mControlledTaskViews.get(i).release();
-            }
-            mControlledTaskViews.clear();
-
-            for (int i = mSemiControlledTaskViews.size() - 1; i >= 0; --i) {
-                mSemiControlledTaskViews.get(i).release();
-            }
-            mSemiControlledTaskViews.clear();
-
-            if (mLaunchRootCarTaskView != null) {
-                mLaunchRootCarTaskView.release();
-                mLaunchRootCarTaskView = null;
-            }
-
-            mContext.unregisterActivityLifecycleCallbacks(mActivityLifecycleCallbacks);
-            mTaskOrganizer.unregisterOrganizer();
-            mTaskViewInputInterceptor.release();
-
-            if (mCar != null) {
-                mCar.disconnect();
-            }
-            mReleased = true;
-        });
-    }
-
-    /**
-     * Shows all the embedded tasks. If the tasks are
-     */
-    public void showEmbeddedTasks() {
-        WindowContainerTransaction wct = new WindowContainerTransaction();
-        for (int i = mControlledTaskViews.size() - 1; i >= 0; --i) {
-            // showEmbeddedTasks() will restart the crashed tasks too.
-            mControlledTaskViews.get(i).showEmbeddedTask(wct);
-        }
-        if (mLaunchRootCarTaskView != null) {
-            mLaunchRootCarTaskView.showEmbeddedTask(wct);
-        }
-        for (int i = mSemiControlledTaskViews.size() - 1; i >= 0; --i) {
-            mSemiControlledTaskViews.get(i).showEmbeddedTask(wct);
-        }
-        mSyncQueue.queue(wct);
-    }
-
-    /**
-     * @return {@code true} if the host activity is in resumed or started state, {@code false}
-     * otherwise.
-     */
-    boolean isHostVisible() {
-        // This code relies on Activity#isVisibleForAutofill() instead of maintaining a custom
-        // activity state.
-        return mContext.isVisibleForAutofill();
-    }
-
-    private final ActivityLifecycleCallbacks mActivityLifecycleCallbacks =
-            new ActivityLifecycleCallbacks() {
-                @Override
-                public void onActivityCreated(@NonNull Activity activity,
-                        @Nullable Bundle savedInstanceState) {}
-
-                @Override
-                public void onActivityStarted(@NonNull Activity activity) {}
-
-                @Override
-                public void onActivityResumed(@NonNull Activity activity) {}
-
-                @Override
-                public void onActivityPaused(@NonNull Activity activity) {}
-
-                @Override
-                public void onActivityStopped(@NonNull Activity activity) {}
-
-                @Override
-                public void onActivitySaveInstanceState(@NonNull Activity activity,
-                        @NonNull Bundle outState) {}
-
-                @Override
-                public void onActivityDestroyed(@NonNull Activity activity) {
-                    release();
-                }
-            };
-
-    private static void cleanUpExistingTaskViewTasks(List<TaskAppearedInfo> taskAppearedInfos) {
-        ActivityTaskManager atm = ActivityTaskManager.getInstance();
-        for (TaskAppearedInfo taskAppearedInfo : taskAppearedInfos) {
-            TaskInfo taskInfo = taskAppearedInfo.getTaskInfo();
-            // Only TaskView tasks have WINDOWING_MODE_MULTI_WINDOW.
-            if (taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW) {
-                if (DBG) Slog.d(TAG, "Found the dangling task, removing: " + taskInfo.taskId);
-                atm.removeTask(taskInfo.taskId);
-            }
-        }
-    }
-
-    @VisibleForTesting
-    List<ControlledCarTaskView> getControlledTaskViews() {
-        return mControlledTaskViews;
-    }
-
-    @VisibleForTesting
-    LaunchRootCarTaskView getLaunchRootCarTaskView() {
-        return mLaunchRootCarTaskView;
-    }
-
-    @VisibleForTesting
-    List<SemiControlledCarTaskView> getSemiControlledTaskViews() {
-        return mSemiControlledTaskViews;
-    }
-
-    @VisibleForTesting
-    BroadcastReceiver getPackageBroadcastReceiver() {
-        return mPackageBroadcastReceiver;
-    }
-
-    @VisibleForTesting
-    /** Only meant for testing, should not be used by real code. */
-    void setTaskViewInputInterceptor(TaskViewInputInterceptor taskViewInputInterceptor) {
-        mTaskViewInputInterceptor = taskViewInputInterceptor;
-    }
-
-    public int getRootTaskCount() {
-        return mLaunchRootCarTaskView != null ? mLaunchRootCarTaskView.getRootTaskCount() : 0;
-    }
-
-    /**
-     * Returns the {@link android.app.ActivityManager.RunningTaskInfo} of the top task inside the
-     * launch root car task view.
-     */
-    public ActivityManager.RunningTaskInfo getTopTaskInLaunchRootTask() {
-        return mLaunchRootCarTaskView != null
-                ? mLaunchRootCarTaskView.getTopTaskInLaunchRootTask() : null;
-    }
-
-    boolean isReleased() {
-        return mReleased;
-    }
-
-    /**
-     * Adds {@code activities} to allowed list of {@code carTaskView} if this car task view is a
-     * known {@link SemiControlledCarTaskView}.
-     */
-    public void addAllowListedActivities(@NonNull CarTaskView carTaskView,
-            List<ComponentName> activities) {
-        if (activities.size() == 0) {
-            if (DBG) {
-                Log.d(TAG, "No activity to add to allowlist");
-            }
-            return;
-        }
-        for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
-            if (semiControlledCarTaskView.equals(carTaskView)) {
-                semiControlledCarTaskView.addAllowListedActivities(activities);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Removes {@code activities} from allowed list of {@code carTaskView} if this CarTaskView is a
-     * known SemiControlledCarTaskView.
-     */
-    public void removeAllowListedActivities(@NonNull CarTaskView carTaskView,
-            List<ComponentName> activities) {
-        if (activities.size() == 0) {
-            if (DBG) {
-                Log.d(TAG, "No activity to remove from allowlist");
-            }
-            return;
-        }
-        for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
-            if (semiControlledCarTaskView.equals(carTaskView)) {
-                semiControlledCarTaskView.removeAllowListedActivities(activities);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Sets {@code activities} to be the allowed list of {@code carTaskView} if this CarTaskView
-     * is a known SemiControlledCarTaskView.
-     */
-    public void setAllowListedActivities(CarTaskView carTaskView, List<ComponentName> activities) {
-        for (SemiControlledCarTaskView semiControlledCarTaskView: mSemiControlledTaskViews) {
-            if (semiControlledCarTaskView.equals(carTaskView)) {
-                semiControlledCarTaskView.setAllowListedActivities(activities);
-                return;
-            }
-        }
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/calmmode/CalmModeActivity.java b/app/src/com/android/car/carlauncher/calmmode/CalmModeActivity.java
index ce0f7d6..69a15ad 100644
--- a/app/src/com/android/car/carlauncher/calmmode/CalmModeActivity.java
+++ b/app/src/com/android/car/carlauncher/calmmode/CalmModeActivity.java
@@ -17,7 +17,10 @@
 package com.android.car.carlauncher.calmmode;
 
 import android.os.Bundle;
+import android.view.View;
+import android.view.WindowInsets;
 
+import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 
@@ -29,5 +32,15 @@
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.calm_mode_activity);
+
+        getWindow().getDecorView().setOnApplyWindowInsetsListener(
+                new View.OnApplyWindowInsetsListener() {
+                    @NonNull
+                    @Override
+                    public WindowInsets onApplyWindowInsets(
+                            @NonNull View v, @NonNull WindowInsets insets) {
+                        return insets;
+                    }
+                });
     }
 }
diff --git a/app/src/com/android/car/carlauncher/calmmode/CalmModeFragment.java b/app/src/com/android/car/carlauncher/calmmode/CalmModeFragment.java
index 7af6fdd..124cffa 100644
--- a/app/src/com/android/car/carlauncher/calmmode/CalmModeFragment.java
+++ b/app/src/com/android/car/carlauncher/calmmode/CalmModeFragment.java
@@ -20,43 +20,53 @@
 
 import android.os.Build;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.widget.ImageView;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
-import androidx.constraintlayout.widget.Group;
+import androidx.core.animation.Animator;
+import androidx.core.animation.AnimatorInflater;
+import androidx.core.animation.AnimatorSet;
 import androidx.fragment.app.Fragment;
 import androidx.lifecycle.LiveData;
-import androidx.lifecycle.Transformations;
 import androidx.lifecycle.ViewModelProvider;
 
 import com.android.car.carlauncher.R;
+import com.android.car.media.common.MediaItemMetadata;
 import com.android.car.media.common.playback.PlaybackViewModel;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public final class CalmModeFragment extends Fragment {
     private static final String TAG = CalmModeFragment.class.getSimpleName();
     private static final boolean DEBUG = Build.isDebuggable();
     private View mContainerView;
-    private Group mMediaGroup;
-    private Group mNavGroup;
-    private Group mTemperatureGroup;
     private TextView mMediaTitleView;
     private TextView mNavStateView;
     private TextView mTemperatureView;
     private TextView mClockView;
     private TextView mDateView;
+    private ImageView mNavStateIconView;
+    private TextView mTemperatureIconView;
     private ViewModelProvider mViewModelProvider;
     private TemperatureViewModel mTemperatureViewModel;
     private LiveData<TemperatureData> mTemperatureData;
     private PlaybackViewModel mPlaybackViewModel;
-
     @Nullable
     private NavigationStateViewModel mNavigationStateViewModel;
+    private boolean mShowEntryAnimations;
+    private final List<View> mViewsPendingAnimation = new ArrayList<>();
+    private final AnimatorSet mAnimatorSet = new AnimatorSet();
 
     @Override
     public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@@ -72,23 +82,35 @@
         mContainerView = rootView.findViewById(R.id.calm_mode_container);
         mClockView = rootView.findViewById(R.id.clock);
         mDateView = rootView.findViewById(R.id.date);
-        mNavGroup = rootView.findViewById(R.id.nav_group);
         mNavStateView = rootView.findViewById(R.id.nav_state);
-        mTemperatureGroup = rootView.findViewById(R.id.temperature_group);
+        mNavStateIconView = rootView.findViewById(R.id.nav_state_icon);
+        mTemperatureIconView = rootView.findViewById(R.id.temperature_icon);
         mTemperatureView = rootView.findViewById(R.id.temperature);
-        mMediaGroup = rootView.findViewById(R.id.media_group);
         mMediaTitleView = rootView.findViewById(R.id.media_title);
+        mShowEntryAnimations = true;
 
+        hideStatusBar();
         initExitOnClick();
-        initClock();
         initDate();
         initNavState();
         initTemperature();
         initMediaTitle();
+        initClockAndPlayEntryAnimations();
 
         return rootView;
     }
 
+    private void hideStatusBar() {
+        final Handler handler = new Handler(Looper.getMainLooper());
+        handler.postDelayed(
+                () -> {
+                    if (getActivity() == null) return;
+                    getActivity().getWindow().getDecorView().getWindowInsetsController()
+                            .hide(WindowInsets.Type.statusBars());
+                },
+                getResources().getInteger(R.integer.calm_mode_activity_fade_duration));
+    }
+
     private void initMediaTitle() {
         if (DEBUG) {
             Log.v(TAG, "initMediaTitle()");
@@ -96,12 +118,7 @@
         if (shouldShowMedia()) {
             mPlaybackViewModel = PlaybackViewModel.get(requireActivity().getApplication(),
                     MEDIA_SOURCE_MODE_PLAYBACK);
-            // Transform LiveData from MediaItemMetadata into a media title CharSequence
-            // If MediaItemMetadata is null, media title value will be null
-            LiveData<CharSequence> mediaTitleLiveData = Transformations.map(
-                    mPlaybackViewModel.getMetadata(), mediaItemMetadata ->
-                            mediaItemMetadata == null ? null : mediaItemMetadata.getTitle());
-            mediaTitleLiveData.observe(this, this::updateMediaTitle);
+            mPlaybackViewModel.getMetadata().observe(this, this::updateMediaTitle);
         }
     }
 
@@ -120,17 +137,96 @@
         if (DEBUG) {
             Log.v(TAG, "initDate()");
         }
-        if (shouldShowDate()) {
+        if (!shouldShowDate()) {
+            return;
+        }
+        if (mShowEntryAnimations) {
+            mDateView.setVisibility(View.INVISIBLE);
+            mViewsPendingAnimation.add(mDateView);
+        } else {
             mDateView.setVisibility(View.VISIBLE);
         }
+
     }
 
-    private void initClock() {
+    private void playEntryAnimations() {
+        mShowEntryAnimations = false;
+        List<Animator> animList = new ArrayList<>();
+
+        for (View view : mViewsPendingAnimation) {
+            if (getContext() == null) {
+                return;
+            }
+            Animator animator = AnimatorInflater.loadAnimator(getContext(),
+                    R.animator.calm_mode_enter);
+            animator.setTarget(view);
+            Animator.AnimatorListener animatorListener = new Animator.AnimatorListener() {
+                @Override
+                public void onAnimationStart(@NonNull Animator animation) {
+                    view.setVisibility(View.VISIBLE);
+                }
+
+                @Override
+                public void onAnimationEnd(@NonNull Animator animation) {
+
+                }
+
+                @Override
+                public void onAnimationCancel(@NonNull Animator animation) {
+
+                }
+
+                @Override
+                public void onAnimationRepeat(@NonNull Animator animation) {
+
+                }
+            };
+            animator.addListener(animatorListener);
+            animList.add(animator);
+        }
+        mViewsPendingAnimation.clear();
+        mAnimatorSet.setDuration(getResources().getInteger(
+                R.integer.calm_mode_content_fade_duration));
+        mAnimatorSet.playTogether(animList);
+        mAnimatorSet.start();
+    }
+    private void initClockAndPlayEntryAnimations() {
         if (DEBUG) {
             Log.v(TAG, "initClock()");
         }
         if (shouldShowClock()) {
-            mClockView.setVisibility(View.VISIBLE);
+            mClockView.setVisibility(View.INVISIBLE);
+            if (getContext() == null) {
+                return;
+            }
+            Animator animator = AnimatorInflater.loadAnimator(getContext(),
+                    R.animator.calm_mode_enter);
+            animator.setStartDelay(getResources().getInteger(
+                    R.integer.calm_mode_activity_fade_duration));
+            animator.setTarget(mClockView);
+
+            animator.addListener(new Animator.AnimatorListener() {
+                @Override
+                public void onAnimationStart(@NonNull Animator animation) {
+                    mClockView.setVisibility(View.VISIBLE);
+                }
+
+                @Override
+                public void onAnimationEnd(@NonNull Animator animation) {
+                    playEntryAnimations();
+                }
+
+                @Override
+                public void onAnimationCancel(@NonNull Animator animation) {
+
+                }
+
+                @Override
+                public void onAnimationRepeat(@NonNull Animator animation) {
+
+                }
+            });
+            animator.start();
         }
     }
 
@@ -183,14 +279,24 @@
 
     private void updateTemperatureData(@Nullable TemperatureData temperatureData) {
         if (temperatureData == null) {
-            mTemperatureGroup.setVisibility(View.GONE);
+            mTemperatureView.setVisibility(View.GONE);
+            mTemperatureIconView.setVisibility(View.GONE);
             mTemperatureView.setText("");
             return;
         }
-        mTemperatureGroup.setVisibility(View.VISIBLE);
+        if (mShowEntryAnimations) {
+            mTemperatureView.setVisibility(View.INVISIBLE);
+            mTemperatureIconView.setVisibility(View.INVISIBLE);
+            mViewsPendingAnimation.add(mTemperatureView);
+            mViewsPendingAnimation.add(mTemperatureIconView);
+        } else {
+            mTemperatureView.setVisibility(View.VISIBLE);
+            mTemperatureIconView.setVisibility(View.VISIBLE);
+        }
         mTemperatureView.setText(
                 TemperatureData.buildTemperatureString(
-                        temperatureData, getResources().getConfiguration().getLocales().get(0)));
+                        temperatureData, getResources().getConfiguration().getLocales().get(0),
+                        /* showUnit = */ false));
     }
 
     private void updateNavigationState(NavigationStateData navState) {
@@ -199,29 +305,74 @@
         }
 
         if (navState == null) {
-            mNavGroup.setVisibility(View.GONE);
-            mNavStateView.setText(null);
+            mNavStateView.setVisibility(View.GONE);
+            mNavStateIconView.setVisibility(View.GONE);
+            mNavStateView.setText("");
             return;
         }
 
-        mNavGroup.setVisibility(View.VISIBLE);
+        if (mShowEntryAnimations) {
+            mNavStateView.setVisibility(View.INVISIBLE);
+            mNavStateIconView.setVisibility(View.INVISIBLE);
+            mViewsPendingAnimation.add(mNavStateView);
+            mViewsPendingAnimation.add(mNavStateIconView);
+        } else {
+            mNavStateView.setVisibility(View.VISIBLE);
+            mNavStateIconView.setVisibility(View.VISIBLE);
+        }
         mNavStateView.setText(
                 NavigationStateData.buildTripStatusString(navState,
-                        getResources().getConfiguration().getLocales().get(0)));
+                        getResources().getConfiguration().getLocales().get(0),
+                        getResources().getString(R.string.calm_mode_separator)));
     }
 
     @VisibleForTesting
-    void updateMediaTitle(CharSequence mediaTitle) {
+    void updateMediaTitle(MediaItemMetadata mediaItemMetadata) {
         if (DEBUG) {
-            Log.v(TAG, "updateMediaTitle mediaTitle = " + mediaTitle);
+            Log.v(TAG, "updateMediaTitle mediaItemMetadata = " + mediaItemMetadata);
         }
-
-        if (mediaTitle == null || mediaTitle.length() == 0) {
-            mMediaGroup.setVisibility(View.GONE);
+        if (mediaItemMetadata == null || mediaItemMetadata.getTitle() == null
+                || mediaItemMetadata.getTitle().length() == 0) {
+            mMediaTitleView.setVisibility(View.GONE);
+            mClockView.setTranslationY(0f);
             mMediaTitleView.setText("");
             return;
         }
-        mMediaGroup.setVisibility(View.VISIBLE);
-        mMediaTitleView.setText(mediaTitle);
+
+        StringBuilder medaTitleBuilder = new StringBuilder();
+        medaTitleBuilder.append(mediaItemMetadata.getTitle());
+
+        if (mediaItemMetadata.getSubtitle() != null
+                && mediaItemMetadata.getSubtitle().length() > 0) {
+            medaTitleBuilder.append(getResources().getString(R.string.calm_mode_separator));
+            medaTitleBuilder.append(mediaItemMetadata.getSubtitle());
+        }
+
+        mClockView.setTranslationY(
+                -getResources().getDimension(R.dimen.calm_mode_clock_translationY));
+        mContainerView.requestLayout();
+        if (mShowEntryAnimations) {
+            mMediaTitleView.setVisibility(View.INVISIBLE);
+            mViewsPendingAnimation.add(mMediaTitleView);
+        } else {
+            mMediaTitleView.setVisibility(View.VISIBLE);
+        }
+        mMediaTitleView.setText(medaTitleBuilder.toString());
     }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        int launchType = requireActivity().getIntent().getIntExtra(
+                CalmModeStatsLogHelper.INTENT_EXTRA_CALM_MODE_LAUNCH_TYPE,
+                CalmModeStatsLogHelper.CalmModeLaunchType.UNSPECIFIED_LAUNCH_TYPE);
+        CalmModeStatsLogHelper.getInstance().logSessionStarted(launchType);
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        CalmModeStatsLogHelper.getInstance().logSessionFinished();
+    }
+
 }
diff --git a/app/src/com/android/car/carlauncher/calmmode/CalmModeQCProvider.java b/app/src/com/android/car/carlauncher/calmmode/CalmModeQCProvider.java
index a4be84a..00306be 100644
--- a/app/src/com/android/car/carlauncher/calmmode/CalmModeQCProvider.java
+++ b/app/src/com/android/car/carlauncher/calmmode/CalmModeQCProvider.java
@@ -16,7 +16,9 @@
 
 package com.android.car.carlauncher.calmmode;
 
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -35,6 +37,7 @@
 import com.android.car.qc.provider.BaseQCProvider;
 
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Remote Quick Control provider for Calm mode in CarLauncher.
@@ -53,6 +56,7 @@
             .build();
     private Set<String> mAllowListedPackages;
     private Context mContext;
+    private AtomicInteger mPendingIntentRequestCode = new AtomicInteger(0);
     @VisibleForTesting
     QCItem mQCItem;
 
@@ -103,17 +107,22 @@
     @VisibleForTesting
     QCItem getQCItem() {
         Resources resources = mContext.getResources();
-        String packageName = resources.getString(R.string.config_calmMode_packageName);
-        String activityName = resources.getString(R.string.config_calmMode_activityName);
-
+        ComponentName componentName = ComponentName.unflattenFromString(
+                resources.getString(R.string.config_calmMode_componentName));
         Intent intent = new Intent();
-        intent.setClassName(packageName, activityName);
-        PendingIntent ambientModeIntent = PendingIntent.getActivity(mContext, 0, intent,
-                PendingIntent.FLAG_IMMUTABLE);
+        intent.setComponent(componentName);
+        intent.putExtra(CalmModeStatsLogHelper.INTENT_EXTRA_CALM_MODE_LAUNCH_TYPE,
+                CalmModeStatsLogHelper.CalmModeLaunchType.QUICK_CONTROLS);
+        ActivityOptions activityOptions = ActivityOptions.makeBasic()
+                .setPendingIntentCreatorBackgroundActivityStartMode(
+                        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
+        PendingIntent calmModeIntent = PendingIntent.getActivity(mContext,
+                mPendingIntentRequestCode.getAndAdd(1), intent,
+                PendingIntent.FLAG_IMMUTABLE, activityOptions.toBundle());
 
         QCRow calmModeRow = new QCRow.Builder()
                 .setTitle(mContext.getString(R.string.calm_mode_title))
-                .setPrimaryAction(ambientModeIntent)
+                .setPrimaryAction(calmModeIntent)
                 .build();
 
         return new QCList.Builder().addRow(calmModeRow).build();
diff --git a/app/src/com/android/car/carlauncher/calmmode/CalmModeStatsLogHelper.java b/app/src/com/android/car/carlauncher/calmmode/CalmModeStatsLogHelper.java
new file mode 100644
index 0000000..5a437fd
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/calmmode/CalmModeStatsLogHelper.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.calmmode;
+
+import android.annotation.IntDef;
+import android.os.Build;
+import android.util.Log;
+
+import com.android.car.carlauncher.CarLauncherStatsLog;
+
+import java.util.UUID;
+
+/**
+ * Helper class that directly interacts with {@link CarLauncherStatsLog}, a generated class that
+ * contains logging methods for CalmModeActivity.
+ * logging methods for CalmModeActivity.
+ */
+public class CalmModeStatsLogHelper {
+    private static final String TAG = CalmModeStatsLogHelper.class.getSimpleName();
+    public static final String INTENT_EXTRA_CALM_MODE_LAUNCH_TYPE =
+            CalmModeLaunchType.class.getSimpleName();
+    private static CalmModeStatsLogHelper sInstance;
+    private long mSessionId;
+
+    /**
+     * IntDef representing enum values of CarCalmModeEventReported.event_type.
+     */
+    @IntDef({
+            CalmModeEventType.UNSPECIFIED_EVENT_TYPE,
+            CalmModeEventType.SESSION_STARTED,
+            CalmModeEventType.SESSION_FINISHED,
+    })
+    public @interface CalmModeEventType {
+        int UNSPECIFIED_EVENT_TYPE =
+                CarLauncherStatsLog
+                        .CAR_CALM_MODE_EVENT_REPORTED__EVENT_TYPE__UNSPECIFIED_EVENT_TYPE;
+        int SESSION_STARTED =
+                CarLauncherStatsLog.CAR_CALM_MODE_EVENT_REPORTED__EVENT_TYPE__SESSION_STARTED;
+        int SESSION_FINISHED =
+                CarLauncherStatsLog.CAR_CALM_MODE_EVENT_REPORTED__EVENT_TYPE__SESSION_FINISHED;
+    }
+
+    /**
+     * IntDef representing enum values of CarCalmModeEventReported.launch_type.
+     */
+    @IntDef({
+            CalmModeLaunchType.UNSPECIFIED_LAUNCH_TYPE,
+            CalmModeLaunchType.SETTINGS,
+            CalmModeLaunchType.QUICK_CONTROLS,
+    })
+    public @interface CalmModeLaunchType {
+        int UNSPECIFIED_LAUNCH_TYPE =
+                CarLauncherStatsLog
+                        .CAR_CALM_MODE_EVENT_REPORTED__LAUNCH_TYPE__UNSPECIFIED_LAUNCH_TYPE;
+        int SETTINGS =
+                CarLauncherStatsLog.CAR_CALM_MODE_EVENT_REPORTED__LAUNCH_TYPE__SETTINGS;
+        int QUICK_CONTROLS =
+                CarLauncherStatsLog.CAR_CALM_MODE_EVENT_REPORTED__LAUNCH_TYPE__QUICK_CONTROLS;
+    }
+
+    /**
+     * Returns the current logging instance of CalmModeStatsLogHelper to write this devices'
+     * CarLauncherStatsModule.
+     *
+     * @return the logging instance of CalmModeStatsLogHelper.
+     */
+    public static CalmModeStatsLogHelper getInstance() {
+        if (sInstance == null) {
+            sInstance = new CalmModeStatsLogHelper();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Logs that a new Calm mode session has started. Additionally, resets measurements and IDs such
+     * as session ID and start time.
+     */
+    public void logSessionStarted(@CalmModeLaunchType int launchType) {
+        mSessionId = UUID.randomUUID().getMostSignificantBits();
+        writeCarCalmModeEventReported(CalmModeEventType.SESSION_STARTED, launchType);
+    }
+
+    /**
+     * Logs that the current Calm mode session has finished.
+     */
+    public void logSessionFinished() {
+        writeCarCalmModeEventReported(CalmModeEventType.SESSION_FINISHED);
+    }
+
+    /**
+     * Writes to CarCalmModeEvent atom with {@code eventType} as the only field, and log all other
+     * fields as unspecified.
+     *
+     * @param eventType one of {@link CalmModeEventType}
+     */
+    private void writeCarCalmModeEventReported(int eventType) {
+        writeCarCalmModeEventReported(
+                eventType, /* launchType */ CalmModeLaunchType.UNSPECIFIED_LAUNCH_TYPE);
+    }
+
+    /**
+     * Writes to CarCalmModeEvent atom with all the optional fields filled.
+     *
+     * @param eventType one of {@link CalmModeEventType}
+     * @param launchType one of {@link CalmModeLaunchType}
+     */
+    private void writeCarCalmModeEventReported(int eventType, int launchType) {
+        long eventId = UUID.randomUUID().getMostSignificantBits();
+        if (Build.isDebuggable()) {
+            Log.v(TAG, "writing CAR_CALM_MODE_EVENT_REPORTED. sessionId=" + mSessionId
+                    + ", eventId=" + eventId + "eventType= " + eventType
+                    + ", launchType=" + launchType);
+        }
+        CarLauncherStatsLog.write(
+                /* atomId */ CarLauncherStatsLog.CAR_CALM_MODE_EVENT_REPORTED,
+                /* sessionId */ mSessionId,
+                /* eventId */ eventId,
+                /* eventType */ eventType,
+                /* launchType */ launchType);
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/calmmode/NavigationStateData.java b/app/src/com/android/car/carlauncher/calmmode/NavigationStateData.java
index 632a112..87cfafb 100644
--- a/app/src/com/android/car/carlauncher/calmmode/NavigationStateData.java
+++ b/app/src/com/android/car/carlauncher/calmmode/NavigationStateData.java
@@ -32,7 +32,6 @@
 public class NavigationStateData {
     private static final boolean DEBUG = Build.isDebuggable();
     private static final String TAG = NavigationStateData.class.getSimpleName();
-    private static final String SEPARATOR = "  ";
     @NonNull
     private final String mTimeToDestination;
 
@@ -49,7 +48,7 @@
     }
 
     /**
-     * @return new Builder instance for creating NavigationStateData
+     * @return new {@link Builder} instance for creating {@link NavigationStateData}
      */
     @NonNull
     public static Builder newBuilder() {
@@ -57,14 +56,18 @@
     }
 
     /**
-     * @param navigationState NavigationStateData to use for building Trip Status string
-     * @param locale Locale to build Trip Status string
-     * @return String representing trip status with time and distance to next destination,
-     * returns null if arguments are invalid, uses default locale if input Locale is null
+     * Trip Status is a string containing time and distance remaining to reach next destination
+     * Example: "1hr 52 min | 100mi"
+     * @param navigationState {@link NavigationStateData} to use for building Trip Status string
+     * @param locale          {@link Locale} to build Trip Status string
+     * @param separator       Separator used to separate time and distance
+     * @return String representing trip status with time and distance to next destination, returns
+     * null if arguments are invalid, uses {@link Locale#getDefault()} if {@code locale} is null
      */
     @Nullable
     public static String buildTripStatusString(
-            @NonNull NavigationStateData navigationState, @NonNull Locale locale) {
+            @NonNull NavigationStateData navigationState, @NonNull Locale locale,
+            @NonNull String separator) {
 
         if (navigationState == null
                 || navigationState.getTimeToDestination() == null
@@ -86,7 +89,7 @@
         }
         StringBuilder navStateTextBuilder = new StringBuilder();
         navStateTextBuilder.append(navigationState.getTimeToDestination());
-        navStateTextBuilder.append(SEPARATOR);
+        navStateTextBuilder.append(separator);
         navStateTextBuilder.append(NumberFormatter.withLocale(locale)
                         .notation(Notation.compactShort())
                         .precision(Precision.integer())
diff --git a/app/src/com/android/car/carlauncher/calmmode/NavigationStateViewModel.java b/app/src/com/android/car/carlauncher/calmmode/NavigationStateViewModel.java
index 225207e..622703e 100644
--- a/app/src/com/android/car/carlauncher/calmmode/NavigationStateViewModel.java
+++ b/app/src/com/android/car/carlauncher/calmmode/NavigationStateViewModel.java
@@ -65,7 +65,7 @@
 
 
     @Override
-    public void onNavigationState(byte[] navigationStateByteArr) {
+    public void onNavigationStateChanged(byte[] navigationStateByteArr) {
         if (DEBUG) {
             Log.v(TAG, "ClusterNavigationStateListener onNavigationState");
         }
diff --git a/app/src/com/android/car/carlauncher/calmmode/TemperatureData.java b/app/src/com/android/car/carlauncher/calmmode/TemperatureData.java
index a26ba0a..08d8e94 100644
--- a/app/src/com/android/car/carlauncher/calmmode/TemperatureData.java
+++ b/app/src/com/android/car/carlauncher/calmmode/TemperatureData.java
@@ -52,11 +52,11 @@
      */
     @NonNull
     public static String buildTemperatureString(
-            @NonNull TemperatureData temperatureData, @NonNull Locale locale) {
+            @NonNull TemperatureData temperatureData, @NonNull Locale locale, boolean showUnit) {
         return NumberFormatter.withLocale(locale)
                 .notation(Notation.compactShort())
                 .precision(Precision.integer())
-                .unit(temperatureData.getUnit())
+                .unit(showUnit ? temperatureData.getUnit() : MeasureUnit.GENERIC_TEMPERATURE)
                 .format(temperatureData.getValue())
                 .toString();
     }
diff --git a/app/src/com/android/car/carlauncher/homescreen/HomeCardFragment.java b/app/src/com/android/car/carlauncher/homescreen/HomeCardFragment.java
index 51a1eb3..fc01e07 100644
--- a/app/src/com/android/car/carlauncher/homescreen/HomeCardFragment.java
+++ b/app/src/com/android/car/carlauncher/homescreen/HomeCardFragment.java
@@ -123,6 +123,7 @@
         mRootView = inflater.inflate(R.layout.card_fragment, container, false);
         mCardTitle = mRootView.findViewById(R.id.card_name);
         mCardIcon = mRootView.findViewById(R.id.card_icon);
+        mCardBackgroundImage = mRootView.findViewById(R.id.card_background_image);
         return mRootView;
     }
 
@@ -174,16 +175,6 @@
     }
 
     /**
-     * Returns the size of the card or null if the view hasn't yet been laid out
-     */
-    protected Size getCardSize() {
-        if (mSize == null && mRootView.isLaidOut()) {
-            mSize = new Size(mRootView.getWidth(), mRootView.getHeight());
-        }
-        return mSize;
-    }
-
-    /**
      * Removes the audio card from view
      */
     @Override
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragment.java b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragment.java
new file mode 100644
index 0000000..25f0ad8
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.dialer.DialerCardFragment;
+import com.android.car.carlauncher.homescreen.audio.media.MediaCardFragment;
+
+/**
+ * Fragment used to display the audio related controls.
+ */
+public class AudioCardFragment extends Fragment implements HomeCardInterface.View {
+    private View mRootView;
+
+    private MediaCardFragment mMediaFragment;
+    private DialerCardFragment mInCallFragment;
+    private boolean mViewCreated;
+
+    private HomeCardFragment.OnViewLifecycleChangeListener mOnViewLifecycleChangeListener;
+
+    /**
+     * Register a callback to be invoked when the fragment lifecycle changes.
+     *
+     * @param onViewLifecycleChangeListener The callback that will run
+     */
+    public void setOnViewLifecycleChangeListener(
+            HomeCardFragment.OnViewLifecycleChangeListener onViewLifecycleChangeListener) {
+        mOnViewLifecycleChangeListener = onViewLifecycleChangeListener;
+        if (mViewCreated) {
+            mOnViewLifecycleChangeListener.onViewCreated();
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        mRootView = inflater.inflate(R.layout.card_fragment_audio_card, container, false);
+        return mRootView;
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+
+        mViewCreated = true;
+        mMediaFragment = createMediaFragment();
+        mInCallFragment = new DialerCardFragment();
+
+        FragmentTransaction ft = getChildFragmentManager().beginTransaction();
+        ft.replace(R.id.media_fragment_container, mMediaFragment, mMediaFragment.getTag());
+        ft.replace(R.id.in_call_fragment_container, mInCallFragment);
+        ft.commitNow();
+
+        FragmentTransaction transaction = getChildFragmentManager().beginTransaction();
+        transaction.hide(mInCallFragment);
+        transaction.commit();
+
+        if (mOnViewLifecycleChangeListener != null) {
+            mOnViewLifecycleChangeListener.onViewCreated();
+        }
+    }
+
+    protected MediaCardFragment createMediaFragment() {
+        return new MediaCardFragment();
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mViewCreated = false;
+        if (mOnViewLifecycleChangeListener != null) {
+            mOnViewLifecycleChangeListener.onViewDestroyed();
+        }
+    }
+
+    @Override
+    public Fragment getFragment() {
+        return this;
+    }
+
+    @Override
+    public void hideCard() {
+        mRootView.setVisibility(View.GONE);
+    }
+
+    public MediaCardFragment getMediaFragment() {
+        return mMediaFragment;
+    }
+
+    public DialerCardFragment getInCallFragment() {
+        return mInCallFragment;
+    }
+
+    /** Does a fragment transaction to show the media card and hide the dialer card */
+    public void showMediaCard() {
+        FragmentManager fragmentManager = getChildFragmentManager();
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        transaction.show(mMediaFragment);
+        transaction.hide(mInCallFragment);
+        transaction.commitAllowingStateLoss();
+    }
+
+    /** Does a fragment transaction to show the dialer card and hide the media card */
+    public void showInCallCard() {
+        FragmentManager fragmentManager = getChildFragmentManager();
+        FragmentTransaction transaction = fragmentManager.beginTransaction();
+        transaction.hide(mMediaFragment);
+        transaction.show(mInCallFragment);
+        transaction.commitAllowingStateLoss();
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModel.java b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModel.java
new file mode 100644
index 0000000..f24281c
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModel.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio;
+
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.ViewModelProvider;
+
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.dialer.DialerCardModel;
+
+/** A wrapper around {@code MediaViewModel} and {@code InCallModel}. */
+public class AudioCardModel implements HomeCardInterface.Model {
+
+    private final MediaViewModel mMediaViewModel;
+    private final InCallModel mInCallViewModel;
+
+    public AudioCardModel(@NonNull ViewModelProvider viewModelProvider) {
+        mMediaViewModel = viewModelProvider.get(MediaViewModel.class);
+        mInCallViewModel = new DialerCardModel(SystemClock.elapsedRealtimeClock());
+    }
+
+    MediaViewModel getMediaViewModel() {
+        return mMediaViewModel;
+    }
+
+    InCallModel getInCallViewModel() {
+        return mInCallViewModel;
+    }
+
+    @Override
+    public void setOnModelUpdateListener(OnModelUpdateListener onModelUpdateListener) {
+        // No-op
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModule.java b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModule.java
index 6ffb692..fec78a8 100644
--- a/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModule.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -16,37 +16,34 @@
 
 package com.android.car.carlauncher.homescreen.audio;
 
-import android.os.SystemClock;
-import android.util.Log;
-
 import androidx.lifecycle.ViewModelProvider;
 
 import com.android.car.carlauncher.R;
-import com.android.car.carlauncher.homescreen.CardPresenter;
-import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
 import com.android.car.carlauncher.homescreen.HomeCardModule;
-
-import java.util.Arrays;
-import java.util.Collections;
+import com.android.car.carlauncher.homescreen.audio.dialer.DialerCardPresenter;
+import com.android.car.carlauncher.homescreen.audio.media.MediaCardPresenter;
 
 /**
- * Home screen card that displays audio related content
+ * The Audio card module. This class initializes the necessary components to present an audio card
+ * as a home module.
  */
 public class AudioCardModule implements HomeCardModule {
-
-    private static final String TAG = "HomeScreenAudioCard";
-
-    private ViewModelProvider mViewModelProvider;
-    private HomeAudioCardPresenter mAudioCardPresenter;
-    private AudioFragment mAudioCardView;
-
+    protected AudioCardPresenter mAudioCardPresenter;
+    protected HomeCardInterface.View mAudioCardView;
+    protected ViewModelProvider mViewModelProvider;
     @Override
     public void setViewModelProvider(ViewModelProvider viewModelProvider) {
+        if (mViewModelProvider != null) {
+            throw new IllegalStateException("Cannot reset the view model provider");
+        }
         mViewModelProvider = viewModelProvider;
-    }
 
-    protected ViewModelProvider getViewModelProvider() {
-        return mViewModelProvider;
+        mAudioCardPresenter = new AudioCardPresenter(
+                new DialerCardPresenter(), new MediaCardPresenter());
+        mAudioCardPresenter.setModel(new AudioCardModel(mViewModelProvider));
+        mAudioCardView = new AudioCardFragment();
+        mAudioCardPresenter.setView(mAudioCardView);
     }
 
     @Override
@@ -55,29 +52,12 @@
     }
 
     @Override
-    public CardPresenter getCardPresenter() {
-        if (mAudioCardPresenter == null) {
-            mAudioCardPresenter = new HomeAudioCardPresenter();
-            if (mViewModelProvider == null) {
-                Log.w(TAG, "No ViewModelProvider set. Cannot get MediaViewModel");
-                mAudioCardPresenter.setModels(
-                        Collections.unmodifiableList(Collections.singletonList(
-                                new InCallModel(SystemClock.elapsedRealtimeClock()))));
-            } else {
-                mAudioCardPresenter.setModels(Collections.unmodifiableList(Arrays.asList(
-                        mViewModelProvider.get(MediaViewModel.class),
-                        new InCallModel(SystemClock.elapsedRealtimeClock()))));
-            }
-        }
+    public HomeCardInterface.Presenter getCardPresenter() {
         return mAudioCardPresenter;
     }
 
     @Override
-    public HomeCardFragment getCardView() {
-        if (mAudioCardView == null) {
-            mAudioCardView = new AudioFragment();
-            getCardPresenter().setView(mAudioCardView);
-        }
+    public HomeCardInterface.View getCardView() {
         return mAudioCardView;
     }
 }
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardPresenter.java b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardPresenter.java
new file mode 100644
index 0000000..a03522c
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/AudioCardPresenter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio;
+
+import com.android.car.carlauncher.Flags;
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.dialer.DialerCardPresenter;
+import com.android.car.carlauncher.homescreen.audio.media.MediaCardPresenter;
+
+import java.util.List;
+
+/**
+ * Presenter used to coordinate the binding between the audio card model and presentation
+ */
+public class AudioCardPresenter extends CardPresenter {
+
+    // Presenter for the dialer card
+    private final DialerCardPresenter mDialerPresenter;
+
+    // Presenter for the media card
+    private final MediaCardPresenter mMediaPresenter;
+
+    // The fragment controlled by this presenter.
+    private AudioCardFragment mFragment;
+
+    private final HomeCardFragment.OnViewLifecycleChangeListener mOnViewLifecycleChangeListener =
+            new HomeCardFragment.OnViewLifecycleChangeListener() {
+                @Override
+                public void onViewCreated() {
+                    mDialerPresenter.setView(mFragment.getInCallFragment());
+                    if (!Flags.mediaCardFullscreen()) {
+                        mMediaPresenter.setView(mFragment.getMediaFragment());
+                    }
+                }
+
+                @Override
+                public void onViewDestroyed() {
+                }
+            };
+
+    public AudioCardPresenter(DialerCardPresenter dialerPresenter,
+            MediaCardPresenter mediaPresenter) {
+        mDialerPresenter = dialerPresenter;
+        mMediaPresenter = mediaPresenter;
+
+        mDialerPresenter.setOnInCallStateChangeListener(hasActiveCall -> {
+            if (hasActiveCall) {
+                if (!Flags.mediaCardFullscreen()) {
+                    mMediaPresenter.setShowMedia(false);
+                }
+                mFragment.showInCallCard();
+            } else {
+                if (!Flags.mediaCardFullscreen()) {
+                    mMediaPresenter.setShowMedia(true);
+                }
+                mFragment.showMediaCard();
+            }
+        });
+    }
+
+    // Deprecated. Use setModel instead.
+    @Override
+    public void setModels(List<HomeCardInterface.Model> models) {
+        // No-op
+    }
+
+    /** Sets the model for this presenter. */
+    public void setModel(AudioCardModel viewModel) {
+        mDialerPresenter.setModel(viewModel.getInCallViewModel());
+        if (!Flags.mediaCardFullscreen()) {
+            mMediaPresenter.setModel(viewModel.getMediaViewModel());
+        }
+    }
+
+    @Override
+    public void setView(HomeCardInterface.View view) {
+        super.setView(view);
+        mFragment = (AudioCardFragment) view;
+        mFragment.setOnViewLifecycleChangeListener(mOnViewLifecycleChangeListener);
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenter.java b/app/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenter.java
deleted file mode 100644
index 9579975..0000000
--- a/app/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenter.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * Copyright (C) 2020 Google Inc.
- *
- * 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.carlauncher.homescreen.audio;
-
-import android.app.ActivityOptions;
-import android.content.Intent;
-import android.view.Display;
-
-import com.android.car.carlauncher.homescreen.CardPresenter;
-import com.android.car.carlauncher.homescreen.HomeCardFragment;
-import com.android.car.carlauncher.homescreen.HomeCardInterface;
-import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
-import com.android.internal.annotations.VisibleForTesting;
-
-import java.util.List;
-
-/**
- * The {@link CardPresenter} for an audio card.
- *
- * For the audio card, the {@link AudioFragment} implements the View and displays information on
- * media from a {@link MediaViewModel}.
- */
-public class HomeAudioCardPresenter extends CardPresenter {
-
-    private AudioFragment mAudioFragment;
-
-    private AudioModel mCurrentModel;
-    private List<HomeCardInterface.Model> mModelList;
-    private MediaViewModel mMediaViewModel;
-
-    private HomeCardFragment.OnViewClickListener mOnViewClickListener =
-            new HomeCardFragment.OnViewClickListener() {
-                @Override
-                public void onViewClicked() {
-                    Intent intent = mCurrentModel.getIntent();
-                    if (intent != null) {
-                        ActivityOptions options = ActivityOptions.makeBasic();
-                        options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
-                        mAudioFragment.getContext().startActivity(intent, options.toBundle());
-                    }
-                }
-            };
-
-    private HomeCardFragment.OnViewLifecycleChangeListener mOnViewLifecycleChangeListener =
-            new HomeCardFragment.OnViewLifecycleChangeListener() {
-                @Override
-                public void onViewCreated() {
-                    for (HomeCardInterface.Model model : mModelList) {
-                        if (model.getClass() == MediaViewModel.class) {
-                            mMediaViewModel = (MediaViewModel) model;
-                            mMediaViewModel.setOnProgressUpdateListener(
-                                    mOnProgressUpdateListener);
-                        }
-                        model.setOnModelUpdateListener(mOnModelUpdateListener);
-                        model.onCreate(getFragment().requireContext());
-                    }
-                }
-
-                @Override
-                public void onViewDestroyed() {
-                    if (mModelList != null) {
-                        for (HomeCardInterface.Model model : mModelList) {
-                            model.onDestroy(getFragment().requireContext());
-                        }
-                    }
-                }
-            };
-
-    @VisibleForTesting
-    HomeCardInterface.Model.OnModelUpdateListener mOnModelUpdateListener =
-            new HomeCardInterface.Model.OnModelUpdateListener() {
-                @Override
-                public void onModelUpdate(HomeCardInterface.Model model) {
-                    AudioModel audioModel = (AudioModel) model;
-                    // Null card header indicates the model has no content to display
-                    if (audioModel.getCardHeader() == null) {
-                        if (mCurrentModel != null
-                                && audioModel.getClass() == mCurrentModel.getClass()) {
-                            // If the model currently on display is updating to empty content,
-                            // check if there
-                            // is media content to display. If there is no media content the
-                            // super method is
-                            // called with empty content, which hides the card.
-                            if (mMediaViewModel != null
-                                    && mMediaViewModel.getCardHeader() != null) {
-                                mCurrentModel = mMediaViewModel;
-                                updateCurrentModelInFragment();
-                                return;
-                            }
-                        } else {
-                            // Otherwise, another model is already on display, so don't update
-                            // with this
-                            // empty content since that would hide the card.
-                            return;
-                        }
-                    } else if (mCurrentModel != null
-                            && mCurrentModel.getClass() == InCallModel.class
-                            && audioModel.getClass() != InCallModel.class) {
-                        // If the Model has content, check if currentModel on display is an
-                        // ongoing phone call.
-                        // If there is any ongoing phone call, do not update the View
-                        // if the model trying to update View is NOT a phone call.
-                        return;
-                    }
-                    mCurrentModel = audioModel;
-                    updateCurrentModelInFragment();
-                }
-            };
-
-    @VisibleForTesting
-    AudioModel.OnProgressUpdateListener mOnProgressUpdateListener =
-            new AudioModel.OnProgressUpdateListener() {
-                @Override
-                public void onProgressUpdate(AudioModel model, boolean updateProgress) {
-                    if (model == null || model.getCardContent() == null
-                            || model.getCardHeader() == null) {
-                        return;
-                    }
-                    DescriptiveTextWithControlsView descriptiveTextWithControlsContent =
-                            (DescriptiveTextWithControlsView) model.getCardContent();
-                    mAudioFragment.updateProgress(
-                            descriptiveTextWithControlsContent.getSeekBarViewModel(),
-                            updateProgress);
-                }
-            };
-
-    private AudioFragment.OnMediaViewInitializedListener mOnMediaViewInitializedListener =
-            new AudioFragment.OnMediaViewInitializedListener() {
-                @Override
-                public void onMediaViewInitialized() {
-                    // set playbackviewmodel on playback control actions view
-                    mAudioFragment.getPlaybackControlsActionBar().setModel(
-                            mMediaViewModel.getPlaybackViewModel(),
-                            mAudioFragment.getViewLifecycleOwner());
-                }
-            };
-
-    @Override
-    public void setView(HomeCardInterface.View view) {
-        super.setView(view);
-        mAudioFragment = (AudioFragment) view;
-        mAudioFragment.setOnViewLifecycleChangeListener(mOnViewLifecycleChangeListener);
-        mAudioFragment.setOnViewClickListener(mOnViewClickListener);
-        mAudioFragment.setOnMediaViewInitializedListener(mOnMediaViewInitializedListener);
-    }
-
-    @Override
-    public void setModels(List<HomeCardInterface.Model> models) {
-        mModelList = models;
-    }
-
-    protected List<HomeCardInterface.Model> getModels() {
-        return mModelList;
-    }
-
-    protected AudioModel getCurrentModel() {
-        return mCurrentModel;
-    }
-
-    private void updateCurrentModelInFragment() {
-        if (mCurrentModel != null && mCurrentModel.getCardHeader() != null) {
-            mAudioFragment.updateHeaderView(mCurrentModel.getCardHeader());
-            if (mCurrentModel.getCardContent() != null) {
-                mAudioFragment.updateContentView(mCurrentModel.getCardContent());
-            }
-        } else {
-            mAudioFragment.hideCard();
-        }
-    }
-}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/InCallModel.java b/app/src/com/android/car/carlauncher/homescreen/audio/InCallModel.java
index a36c040..9f49eca 100644
--- a/app/src/com/android/car/carlauncher/homescreen/audio/InCallModel.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/InCallModel.java
@@ -26,7 +26,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
-import android.os.Bundle;
 import android.telecom.Call;
 import android.telecom.CallAudioState;
 import android.telecom.PhoneAccountHandle;
@@ -169,24 +168,16 @@
     public Intent getIntent() {
         Intent intent = null;
         if (isSelfManagedCall()) {
-            Bundle extras = mCurrentCall.getDetails().getExtras();
-            ComponentName componentName = extras == null ? null : extras.getParcelable(
-                    Intent.EXTRA_COMPONENT_NAME, ComponentName.class);
-            if (componentName != null) {
-                intent = new Intent();
-                intent.setComponent(componentName);
-            } else {
-                String callingAppPackageName = getCallingAppPackageName();
-                if (!TextUtils.isEmpty(callingAppPackageName)) {
-                    if (isCarAppCallingService(callingAppPackageName)) {
-                        intent = new Intent();
-                        intent.setComponent(
-                                new ComponentName(
-                                        callingAppPackageName, CAR_APP_ACTIVITY_INTERFACE));
-                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    } else {
-                        intent = mPackageManager.getLaunchIntentForPackage(callingAppPackageName);
-                    }
+            String callingAppPackageName = getCallingAppPackageName();
+            if (!TextUtils.isEmpty(callingAppPackageName)) {
+                if (isCarAppCallingService(callingAppPackageName)) {
+                    intent = new Intent();
+                    intent.setComponent(
+                             new ComponentName(
+                                    callingAppPackageName, CAR_APP_ACTIVITY_INTERFACE));
+                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                } else {
+                    intent = mPackageManager.getLaunchIntentForPackage(callingAppPackageName);
                 }
             }
         } else {
@@ -477,10 +468,21 @@
     }
 
     private boolean isCarAppCallingService(String packageName) {
-        Intent intent =
+        // Check that app is integrated with CAL and handles calls
+        Intent serviceIntent =
                 new Intent(CAR_APP_SERVICE_INTERFACE)
                         .setPackage(packageName)
                         .addCategory(CAR_APP_CATEGORY_CALLING);
-        return !mPackageManager.queryIntentServices(intent, GET_RESOLVED_FILTER).isEmpty();
+
+        if (mPackageManager.queryIntentServices(serviceIntent, GET_RESOLVED_FILTER).isEmpty()) {
+            return false;
+        }
+
+        // Check that app has CAl activity
+        Intent activityIntent = new Intent();
+        activityIntent.setComponent(new ComponentName(packageName, CAR_APP_ACTIVITY_INTERFACE));
+
+        return mPackageManager
+                .resolveActivity(activityIntent, PackageManager.MATCH_DEFAULT_ONLY) != null;
     }
 }
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java b/app/src/com/android/car/carlauncher/homescreen/audio/IntentHandler.java
similarity index 66%
copy from app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
copy to app/src/com/android/car/carlauncher/homescreen/audio/IntentHandler.java
index e30ffe5..753dec3 100644
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/IntentHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher;
+package com.android.car.carlauncher.homescreen.audio;
+
+import android.content.Intent;
 
 /**
- * A callbacks interface for {@link LaunchRootCarTaskView}.
+ * Handles an {@link Intent}
  */
-public interface LaunchRootCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {}
+public interface IntentHandler {
+    /**
+     * Handle {@link Intent}.
+     */
+    void handleIntent(Intent intent);
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java b/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
index 5becb9f..e320c1c 100644
--- a/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/MediaViewModel.java
@@ -16,11 +16,9 @@
 
 package com.android.car.carlauncher.homescreen.audio;
 
-import static android.car.media.CarMediaIntents.EXTRA_MEDIA_COMPONENT;
 import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK;
 
 import android.app.Application;
-import android.car.media.CarMediaIntents;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -33,6 +31,8 @@
 import androidx.lifecycle.Observer;
 
 import com.android.car.apps.common.imaging.ImageBinder;
+import com.android.car.carlauncher.Flags;
+import com.android.car.carlauncher.MediaSessionUtils;
 import com.android.car.carlauncher.homescreen.HomeCardInterface;
 import com.android.car.carlauncher.homescreen.ui.CardContent;
 import com.android.car.carlauncher.homescreen.ui.CardHeader;
@@ -42,12 +42,12 @@
 import com.android.car.media.common.R;
 import com.android.car.media.common.playback.PlaybackProgress;
 import com.android.car.media.common.playback.PlaybackViewModel;
+import com.android.car.media.common.source.MediaModels;
 import com.android.car.media.common.source.MediaSource;
 import com.android.car.media.common.source.MediaSourceColors;
 import com.android.car.media.common.source.MediaSourceViewModel;
 import com.android.internal.annotations.VisibleForTesting;
 
-
 /**
  * ViewModel for media. Uses both a {@link MediaSourceViewModel} and a {@link PlaybackViewModel}
  * for data on the audio source and audio metadata (such as song title), respectively.
@@ -136,13 +136,24 @@
 
     @Override
     public void onCreate(@NonNull Context context) {
-        if (mSourceViewModel == null) {
-            mSourceViewModel = MediaSourceViewModel.get(getApplication(),
-                    MEDIA_SOURCE_MODE_PLAYBACK);
-        }
-        if (mPlaybackViewModel == null) {
-            mPlaybackViewModel = PlaybackViewModel.get(getApplication(),
-                    MEDIA_SOURCE_MODE_PLAYBACK);
+        // Initialize media data with media session sources or mbt sources
+        if (Flags.mediaSessionCard()) {
+            MediaModels mediaModels = MediaSessionUtils.getMediaModels(context);
+            if (mSourceViewModel == null) {
+                mSourceViewModel = mediaModels.getMediaSourceViewModel();
+            }
+            if (mPlaybackViewModel == null) {
+                mPlaybackViewModel = mediaModels.getPlaybackViewModel();
+            }
+        } else {
+            if (mSourceViewModel == null) {
+                mSourceViewModel = MediaSourceViewModel.get(getApplication(),
+                        MEDIA_SOURCE_MODE_PLAYBACK);
+            }
+            if (mPlaybackViewModel == null) {
+                mPlaybackViewModel = PlaybackViewModel.get(getApplication(),
+                        MEDIA_SOURCE_MODE_PLAYBACK);
+            }
         }
 
         mContext = context;
@@ -177,20 +188,21 @@
     @Override
     protected void onCleared() {
         super.onCleared();
-        mSourceViewModel.getPrimaryMediaSource().removeObserver(mMediaSourceObserver);
-        mPlaybackViewModel.getMetadata().removeObserver(mMetadataObserver);
-        mPlaybackViewModel.getPlaybackStateWrapper().removeObserver(mPlaybackStateWrapperObserver);
+        if (mSourceViewModel != null) {
+            mSourceViewModel.getPrimaryMediaSource().removeObserver(mMediaSourceObserver);
+        }
+        if (mPlaybackViewModel != null) {
+            mPlaybackViewModel.getMetadata().removeObserver(mMetadataObserver);
+            mPlaybackViewModel.getPlaybackStateWrapper().removeObserver(
+                    mPlaybackStateWrapperObserver);
+        }
     }
 
     @Override
     public Intent getIntent() {
         MediaSource mediaSource = getMediaSourceViewModel().getPrimaryMediaSource().getValue();
-        Intent intent = new Intent(CarMediaIntents.ACTION_MEDIA_TEMPLATE);
-        if (mediaSource != null) {
-            intent.putExtra(EXTRA_MEDIA_COMPONENT,
-                    mediaSource.getBrowseServiceComponentName().flattenToString());
-        }
-        return intent;
+
+        return mediaSource != null ? mediaSource.getIntent() : null;
     }
 
     @Override
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardFragment.java b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardFragment.java
new file mode 100644
index 0000000..e0c4108
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardFragment.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.homescreen.audio.dialer;
+
+import static android.graphics.Shader.TileMode.MIRROR;
+
+import android.graphics.Bitmap;
+import android.graphics.RenderEffect;
+import android.os.Bundle;
+import android.util.Size;
+import android.view.View;
+import android.widget.Chronometer;
+
+import com.android.car.apps.common.BitmapUtils;
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.ui.CardContent;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
+
+/** A fragment for in-call controls. Displays and controls an ongoing phone call */
+public class DialerCardFragment extends HomeCardFragment {
+    private Chronometer mChronometer;
+    private View mChronometerSeparator;
+    private float mBlurRadius;
+    private CardContent.CardBackgroundImage mDefaultCardBackgroundImage;
+    private CardContent.CardBackgroundImage mCardBackgroundImage;
+    private Size mDialerSize;
+    private View.OnLayoutChangeListener mOnRootLayoutChangeListener =
+            (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
+                boolean isWidthChanged = left - right != oldLeft - oldRight;
+                boolean isHeightChanged = top - bottom != oldTop - oldBottom;
+                boolean isSizeChanged = isWidthChanged || isHeightChanged;
+                if (isSizeChanged) {
+                    mDialerSize = new Size(right - left, bottom - top);
+                    resizeCardBackgroundImage(mDialerSize);
+                }
+            };
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mBlurRadius = getResources().getFloat(R.dimen.card_background_image_blur_radius);
+        mDefaultCardBackgroundImage = new CardContent.CardBackgroundImage(
+                getContext().getDrawable(R.drawable.default_audio_background),
+                getContext().getDrawable(R.drawable.control_bar_image_background));
+    }
+
+    @Override
+    public void onViewCreated(View view, Bundle savedInstanceState) {
+        super.onViewCreated(view, savedInstanceState);
+        getRootView().addOnLayoutChangeListener(mOnRootLayoutChangeListener);
+        getDescriptiveTextWithControlsLayoutView();
+        getRootView().setVisibility(View.VISIBLE);
+        getRootView().findViewById(R.id.optional_seek_bar_with_times_container).setVisibility(
+                View.GONE);
+
+    }
+
+    @Override
+    public void updateContentViewInternal(CardContent content) {
+        if (content.getType() == CardContent.HomeCardContentType.DESCRIPTIVE_TEXT_WITH_CONTROLS) {
+            DescriptiveTextWithControlsView audioContent =
+                    (DescriptiveTextWithControlsView) content;
+            updateBackgroundImage(audioContent.getImage());
+            updateDescriptiveTextWithControlsView(audioContent.getTitle(),
+                    audioContent.getSubtitle(),
+                    /* optionalImage= */ null, audioContent.getLeftControl(),
+                    audioContent.getCenterControl(), audioContent.getRightControl());
+            updateAudioDuration(audioContent);
+        } else {
+            super.updateContentViewInternal(content);
+        }
+    }
+
+    @Override
+    protected void hideAllViews() {
+        super.hideAllViews();
+        getCardBackground().setVisibility(View.GONE);
+    }
+
+    private Chronometer getChronometer() {
+        if (mChronometer == null) {
+            mChronometer = getDescriptiveTextWithControlsLayoutView().findViewById(
+                    R.id.optional_timer);
+            mChronometerSeparator = getDescriptiveTextWithControlsLayoutView().findViewById(
+                    R.id.optional_timer_separator);
+        }
+        return mChronometer;
+    }
+
+    private void resizeCardBackgroundImage(Size cardSize) {
+        if (mCardBackgroundImage == null || mCardBackgroundImage.getForeground() == null) {
+            mCardBackgroundImage = mDefaultCardBackgroundImage;
+        }
+        int maxDimen = Math.max(getCardBackgroundImage().getWidth(),
+                getCardBackgroundImage().getHeight());
+        // Prioritize size of background image view. Otherwise, use size of whole card
+        if (maxDimen == 0) {
+            // This function may be called before a non-null cardSize is ready. Instead of waiting
+            // for the next CardContent update to trigger resizeCardBackgroundImage(), resize the
+            // card as soon as mOnRootChangeLayoutListener sets the mDialerSize.
+            if (cardSize == null) {
+                return;
+            }
+            maxDimen = Math.max(cardSize.getWidth(), cardSize.getHeight());
+        }
+
+        if (maxDimen == 0) {
+            return;
+        }
+        Size scaledSize = new Size(maxDimen, maxDimen);
+        Bitmap imageBitmap = BitmapUtils.fromDrawable(mCardBackgroundImage.getForeground(),
+                scaledSize);
+        RenderEffect blur = RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, MIRROR);
+        getCardBackgroundImage().setRenderEffect(blur);
+
+        if (mCardBackgroundImage.getBackground() != null) {
+            getCardBackgroundImage().setBackground(mCardBackgroundImage.getBackground());
+            getCardBackgroundImage().setClipToOutline(true);
+        }
+        getCardBackgroundImage().setImageBitmap(imageBitmap, /* showAnimation= */ true);
+        getCardBackground().setVisibility(View.VISIBLE);
+    }
+
+    private void updateBackgroundImage(CardContent.CardBackgroundImage cardBackgroundImage) {
+        mCardBackgroundImage = cardBackgroundImage;
+        resizeCardBackgroundImage(mDialerSize);
+    }
+
+    private void updateAudioDuration(DescriptiveTextWithControlsView content) {
+        if (content.getStartTime() > 0) {
+            getChronometer().setVisibility(View.VISIBLE);
+            getChronometer().setBase(content.getStartTime());
+            getChronometer().start();
+            mChronometerSeparator.setVisibility(View.VISIBLE);
+        } else {
+            getChronometer().setVisibility(View.GONE);
+            mChronometerSeparator.setVisibility(View.GONE);
+        }
+    }
+}
+
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardModel.java b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardModel.java
new file mode 100644
index 0000000..8f1372e
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardModel.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.homescreen.audio.dialer;
+
+import android.telecom.Call;
+
+import com.android.car.carlauncher.homescreen.audio.InCallModel;
+import com.android.car.telephony.common.CallDetail;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.time.Clock;
+import java.util.List;
+
+/** A wrapper around InCallModel to track when an active call is in progress. */
+public class DialerCardModel extends InCallModel {
+
+    private boolean mHasActiveCall;
+    private List<Integer> mAvailableRoutes;
+    private int mActiveRoute;
+
+    public DialerCardModel(Clock elapsedTimeClock) {
+        super(elapsedTimeClock);
+    }
+
+    /** Indicates whether there is an active call or not. */
+    public boolean hasActiveCall() {
+        return mHasActiveCall;
+    }
+
+    @Override
+    public void onCallAdded(Call call) {
+        mHasActiveCall = call != null;
+        super.onCallAdded(call);
+    }
+
+    @Override
+    public void onCallRemoved(Call call) {
+        mHasActiveCall = false;
+        super.onCallRemoved(call);
+    }
+
+    @Override
+    protected void handleActiveCall(@NotNull Call call) {
+        CallDetail callDetails = CallDetail.fromTelecomCallDetail(call.getDetails());
+        mAvailableRoutes = sInCallServiceManager.getSupportedAudioRoute(callDetails);
+        mActiveRoute = sInCallServiceManager.getAudioRoute(
+                CallDetail.fromTelecomCallDetail(call.getDetails()).getScoState());
+        super.handleActiveCall(call);
+    }
+
+    /**
+     * Returns audio routes supported by current call.
+     */
+    public List<Integer> getAvailableAudioRoutes() {
+        return mAvailableRoutes;
+    }
+
+    /**
+     * Returns current call audio state.
+     */
+    public int getActiveAudioRoute() {
+        return mActiveRoute;
+    }
+
+    /**
+     * Sets current call audio route.
+     */
+    public void setActiveAudioRoute(int audioRoute) {
+        if (getCurrentCall() == null) {
+            // AudioRouteButton is disabled if it is null. Simply ignore it.
+            return;
+        }
+        sInCallServiceManager.setAudioRoute(audioRoute, getCurrentCall());
+        mActiveRoute = audioRoute;
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenter.java b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenter.java
new file mode 100644
index 0000000..aaaf5cd
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenter.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.homescreen.audio.dialer;
+
+import android.app.ActivityOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.view.Display;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewClickListener;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewLifecycleChangeListener;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.InCallModel;
+
+import java.util.List;
+
+/**
+ * A presenter for the in-call controls.
+ */
+public class DialerCardPresenter extends CardPresenter {
+
+    /** A listener to notify when an in-call state changes. */
+    public interface OnInCallStateChangeListener {
+
+        /** Notifies when an in-call state changes. */
+        void onInCallStateChanged(boolean hasActiveCall);
+    }
+
+    private InCallModel mViewModel;
+    private DialerCardFragment mFragment;
+
+    @VisibleForTesting
+    boolean mHasActiveCall;
+
+    public void setOnInCallStateChangeListener(
+            OnInCallStateChangeListener onInCallStateChangeListener) {
+        mOnInCallStateChangeListener = onInCallStateChangeListener;
+    }
+
+    OnInCallStateChangeListener mOnInCallStateChangeListener;
+
+    private final OnViewClickListener mOnViewClickListener =
+            new OnViewClickListener() {
+                @Override
+                public void onViewClicked() {
+                    ActivityOptions options = ActivityOptions.makeBasic();
+                    options.setLaunchDisplayId(Display.DEFAULT_DISPLAY);
+                    Intent intent = mViewModel.getIntent();
+                    Context context = mFragment.getContext();
+                    if (context != null) {
+                        context.startActivity(intent, options.toBundle());
+                    }
+                }
+            };
+    @VisibleForTesting
+    final HomeCardInterface.Model.OnModelUpdateListener mOnInCallModelUpdateListener =
+            new HomeCardInterface.Model.OnModelUpdateListener() {
+                @Override
+                public void onModelUpdate(HomeCardInterface.Model model) {
+                    DialerCardModel dialerCardModel = (DialerCardModel) model;
+                    if (dialerCardModel.getCardHeader() != null) {
+                        mFragment.updateHeaderView(dialerCardModel.getCardHeader());
+                    }
+                    if (dialerCardModel.getCardContent() != null) {
+                        mFragment.updateContentView(dialerCardModel.getCardContent());
+                    }
+                    boolean hasActiveCall = dialerCardModel.hasActiveCall();
+                    if (mHasActiveCall != hasActiveCall) {
+                        mHasActiveCall = hasActiveCall;
+                        mOnInCallStateChangeListener.onInCallStateChanged(hasActiveCall);
+                    }
+                }
+            };
+
+    private final OnViewLifecycleChangeListener mOnInCallViewLifecycleChangeListener =
+            new OnViewLifecycleChangeListener() {
+                @Override
+                public void onViewCreated() {
+                    mViewModel.setOnModelUpdateListener(mOnInCallModelUpdateListener);
+                    mViewModel.onCreate(mFragment.requireContext());
+                }
+
+                @Override
+                public void onViewDestroyed() {
+                    mViewModel.onDestroy(getFragment().requireContext());
+                }
+            };
+
+    // Deprecated. Use setModel instead.
+    @Override
+    public void setModels(List<HomeCardInterface.Model> models) {
+        // No-op
+    }
+
+    public void setModel(InCallModel viewModel) {
+        mViewModel = viewModel;
+    }
+
+    @Override
+    public void setView(HomeCardInterface.View view) {
+        super.setView(view);
+        mFragment = (DialerCardFragment) view;
+        mFragment.setOnViewLifecycleChangeListener(mOnInCallViewLifecycleChangeListener);
+        mFragment.setOnViewClickListener(mOnViewClickListener);
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardController.java b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardController.java
new file mode 100644
index 0000000..655428a
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardController.java
@@ -0,0 +1,627 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio.media;
+
+import static com.android.car.media.common.ui.PlaybackCardControllerUtilities.getFirstCustomActionInSet;
+import static com.android.car.media.common.ui.PlaybackCardControllerUtilities.skipBackStandardActions;
+import static com.android.car.media.common.ui.PlaybackCardControllerUtilities.skipForwardStandardActions;
+import static com.android.car.media.common.ui.PlaybackCardControllerUtilities.updatePlayButtonWithPlaybackState;
+
+import static java.lang.Integer.max;
+
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import androidx.constraintlayout.motion.widget.MotionLayout;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.android.car.apps.common.RoundedDrawable;
+import com.android.car.apps.common.util.ViewUtils;
+import com.android.car.carlauncher.R;
+import com.android.car.media.common.CustomPlaybackAction;
+import com.android.car.media.common.MediaItemMetadata;
+import com.android.car.media.common.playback.PlaybackProgress;
+import com.android.car.media.common.playback.PlaybackViewModel;
+import com.android.car.media.common.playback.PlaybackViewModel.PlaybackController;
+import com.android.car.media.common.playback.PlaybackViewModel.PlaybackStateWrapper;
+import com.android.car.media.common.source.MediaSource;
+import com.android.car.media.common.ui.PlaybackCardController;
+import com.android.car.media.common.ui.PlaybackHistoryController;
+import com.android.car.media.common.ui.PlaybackQueueController;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaCardController extends PlaybackCardController implements
+        MediaCardPanelViewPagerAdapter.ViewPagerQueueCreator,
+        MediaCardPanelViewPagerAdapter.ViewPagerHistoryCreator {
+
+    private static final int SWIPE_MAX_OFF_PATH = 75;
+    private static final int SWIPE_THRESHOLD_VELOCITY = 200;
+
+    private final MediaIntentRouter mMediaIntentRouter = MediaIntentRouter.getInstance();
+    private Resources mViewResources;
+    private View mPanelHandlebar;
+    private LinearLayout mPanel;
+    private MotionLayout mMotionLayout;
+    private MediaCardFragment.MediaCardViewModel mCardViewModel;
+    private ImageButton mSkipPrevButton;
+    private ImageButton mSkipNextButton;
+    private int mSkipPrevVisibility;
+    private int mSkipNextVisibility;
+    private int mAlbumCoverVisibility;
+    private int mSubtitleVisibility;
+    private int mLogoVisibility;
+
+    private PlaybackQueueController mPlaybackQueueController;
+    private PlaybackHistoryController mPlaybackHistoryController;
+
+    private ViewPager2 mPager;
+    private MediaCardPanelViewPagerAdapter mPagerAdapter;
+    private Handler mHandler;
+
+    @Override
+    public void createQueueController(ViewGroup queueContainer) {
+        mPlaybackQueueController = new PlaybackQueueController(
+                queueContainer, /* queueResource */ Resources.ID_NULL,
+                R.layout.media_card_queue_item, R.layout.media_card_queue_header_item,
+                getViewLifecycleOwner(), mDataModel, mCardViewModel.getMediaItemsRepository(),
+                /* uxrContentLimiter */ null, /* uxrConfigurationId */ 0);
+        mPlaybackQueueController.setShowTimeForActiveQueueItem(false);
+        mPlaybackQueueController.setShowIconForActiveQueueItem(false);
+        mPlaybackQueueController.setShowThumbnailForQueueItem(true);
+        mPlaybackQueueController.setShowSubtitleForQueueItem(true);
+    }
+
+    @Override
+    public void createHistoryController(ViewGroup historyContainer) {
+        mPlaybackHistoryController = new PlaybackHistoryController(getViewLifecycleOwner(),
+                mCardViewModel, historyContainer, R.layout.media_card_history_item,
+                R.layout.media_card_history_header_item, /* uxrConfigurationId */ 0);
+        mPlaybackHistoryController.setupView();
+    }
+
+    /** Builder for {@link MediaCardController}. Overrides build() method to return
+     * NowPlayingController rather than base {@link PlaybackCardController}
+     */
+    public static class Builder extends PlaybackCardController.Builder {
+
+        @Override
+        public MediaCardController build() {
+            MediaCardController controller = new MediaCardController(this);
+            controller.setupController();
+            return controller;
+        }
+    }
+
+    public MediaCardController(Builder builder) {
+        super(builder);
+
+        mCardViewModel = (MediaCardFragment.MediaCardViewModel) mViewModel;
+        mViewResources = mView.getContext().getResources();
+
+        mView.setOnClickListener(view -> {
+            launchMediaAppOrClosePanel();
+        });
+        mView.findViewById(R.id.empty_panel).setOnClickListener(view -> {
+            launchMediaAppOrClosePanel();
+        });
+
+        mPager = mView.findViewById(R.id.view_pager);
+        mPagerAdapter = new MediaCardPanelViewPagerAdapter(mView.getContext());
+        mPager.setAdapter(mPagerAdapter);
+        mPagerAdapter.setQueueControllerProvider(this);
+        mPagerAdapter.setHistoryControllerProvider(this);
+        mPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+
+            @Override
+            public void onPageSelected(int position) {
+                super.onPageSelected(position);
+                if (!mCardViewModel.getPanelExpanded()) {
+                    return;
+                }
+                selectOverflow(position == getOverflowTabIndex());
+                selectQueue(position == getQueueTabIndex());
+                selectHistory(position == getHistoryTabIndex());
+            }
+        });
+
+        mMotionLayout = mView.findViewById(R.id.motion_layout);
+
+        mPanel = mView.findViewById(R.id.button_panel_background);
+        mPanelHandlebar = mView.findViewById(R.id.media_card_panel_handlebar);
+
+        mSkipPrevButton = mView.findViewById(R.id.playback_action_id1);
+        mSkipNextButton = mView.findViewById(R.id.playback_action_id2);
+
+        mMotionLayout.addTransitionListener(new MotionLayout.TransitionListener() {
+            @Override
+            public void onTransitionStarted(MotionLayout motionLayout, int i, int i1) {
+            }
+
+            @Override
+            public void onTransitionChange(MotionLayout motionLayout, int i, int i1, float v) {
+            }
+
+            @Override
+            public void onTransitionCompleted(MotionLayout motionLayout, int i) {
+                if (mCardViewModel.getPanelExpanded()) {
+                    mSkipPrevButton.setVisibility(View.GONE);
+                    mSkipNextButton.setVisibility(View.GONE);
+                    mLogo.setVisibility(View.GONE);
+                }
+            }
+
+            @Override
+            public void onTransitionTrigger(MotionLayout motionLayout, int i, boolean b, float v) {
+            }
+        });
+
+        GestureDetector mCloseGestureDetector = new GestureDetector(mView.getContext(),
+                new GestureDetector.SimpleOnGestureListener() {
+                    @Override
+                    public boolean onFling(MotionEvent event1, MotionEvent event2,
+                            float velocityX, float velocityY) {
+                        if (Math.abs(event1.getX() - event2.getX()) > SWIPE_MAX_OFF_PATH
+                                || Math.abs(velocityY) < SWIPE_THRESHOLD_VELOCITY) {
+                            // swipe was not vertical or was not fast enough
+                            return false;
+                        }
+                        boolean isInClosingDirection = velocityY > 0;
+                        if (isInClosingDirection) {
+                            animateClosePanel();
+                            return true;
+                        }
+                        return false;
+                    }
+                }
+        );
+        mPanelHandlebar.setOnClickListener((view) -> animateClosePanel());
+        mPanelHandlebar.setOnTouchListener((view, event) ->
+                mCloseGestureDetector.onTouchEvent(event));
+
+        mHandler = new Handler(Looper.getMainLooper());
+    }
+
+    @Override
+    protected void setupController() {
+        super.setupController();
+
+        mSkipPrevVisibility = mSkipPrevButton.getVisibility();
+        mSkipNextVisibility = mSkipNextButton.getVisibility();
+        mAlbumCoverVisibility = mAlbumCover.getVisibility();
+        mSubtitleVisibility = mSubtitle.getVisibility();
+        mLogoVisibility = mLogo.getVisibility();
+    }
+
+    @Override
+    protected void updateMetadata(MediaItemMetadata metadata) {
+        super.updateMetadata(metadata);
+        if (mCardViewModel.getPanelExpanded()) {
+            mSubtitleVisibility = mSubtitle.getVisibility();
+            mSubtitle.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void updateAlbumCoverWithDrawable(Drawable drawable) {
+        RoundedDrawable roundedDrawable = new RoundedDrawable(drawable, mView.getResources()
+                .getFloat(R.dimen.media_card_album_art_drawable_corner_ratio));
+        super.updateAlbumCoverWithDrawable(roundedDrawable);
+
+        if (mCardViewModel.getPanelExpanded()) {
+            mAlbumCoverVisibility = mAlbumCover.getVisibility();
+            mAlbumCover.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    protected void updateLogoWithDrawable(Drawable drawable) {
+        super.updateLogoWithDrawable(drawable);
+        if (mCardViewModel.getPanelExpanded()) {
+            mLogoVisibility = mLogo.getVisibility();
+            mLogo.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void updateMediaSource(MediaSource mediaSource) {
+        super.updateMediaSource(mediaSource);
+        if (mCardViewModel.getPanelExpanded()) {
+            mAppIcon.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    @Override
+    protected void updateProgress(PlaybackProgress progress) {
+        super.updateProgress(progress);
+        ViewUtils.setVisible(mSeekBar, progress != null && progress.hasTime());
+        if (progress == null || !progress.hasTime()) {
+            mLogo.setVisibility(View.GONE);
+        } else if (mDataModel.getMetadata().getValue() != null) {
+            Uri logoUri = mLogo.prepareToDisplay(mDataModel.getMetadata().getValue());
+            if (logoUri != null && !mCardViewModel.getPanelExpanded()) {
+                mLogo.setVisibility(View.VISIBLE);
+            }
+        }
+    }
+
+    @Override
+    protected void updatePlaybackState(PlaybackViewModel.PlaybackStateWrapper playbackState) {
+        PlaybackController playbackController = mDataModel.getPlaybackController().getValue();
+        if (playbackState != null) {
+            updatePlayButtonWithPlaybackState(mPlayPauseButton, playbackState, playbackController);
+            List<PlaybackViewModel.RawCustomPlaybackAction> usedCustomActions =
+                    updateSkipButtonsAndReturnUsedStandardCustomActions(
+                            playbackState, playbackController);
+
+            boolean hasCustomActions = playbackState.getCustomActions().size() != 0;
+            boolean isPreviouslyVisible = ViewUtils.isVisible(mActionOverflowButton);
+            ViewUtils.setVisible(mActionOverflowButton, hasCustomActions);
+            mPagerAdapter.setHasOverflow(hasCustomActions);
+            if (mCardViewModel.getPanelExpanded() && isPreviouslyVisible != hasCustomActions) {
+                animateClosePanel();
+            }
+            mPagerAdapter.notifyPlaybackStateChanged(playbackState,
+                    playbackController, usedCustomActions);
+        } else {
+            mSkipPrevButton.setVisibility(View.GONE);
+            mSkipNextButton.setVisibility(View.GONE);
+        }
+
+        if (mCardViewModel.getPanelExpanded()) {
+            mSkipPrevVisibility = mSkipPrevButton.getVisibility();
+            mSkipNextVisibility = mSkipNextButton.getVisibility();
+            mSkipPrevButton.setVisibility(View.GONE);
+            mSkipNextButton.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void setUpActionsOverflowButton() {
+        super.setUpActionsOverflowButton();
+        setOverflowState(mCardViewModel.getOverflowExpanded(), false);
+    }
+
+    @Override
+    protected void handleCustomActionsOverflowButtonClicked(View overflow) {
+        super.handleCustomActionsOverflowButtonClicked(overflow);
+        setOverflowState(mCardViewModel.getOverflowExpanded(), true);
+    }
+
+    @Override
+    protected void setUpQueueButton() {
+        super.setUpQueueButton();
+        setQueueState(mCardViewModel.getQueueVisible(), false);
+    }
+
+    @Override
+    protected void updateQueueState(boolean hasQueue, boolean isQueueVisible) {
+        super.updateQueueState(hasQueue, isQueueVisible);
+        mPagerAdapter.setHasQueue(hasQueue);
+        ViewUtils.setVisible(mQueueButton, hasQueue);
+        if (mCardViewModel.getPanelExpanded() && !hasQueue) {
+            animateClosePanel();
+        }
+    }
+
+    @Override
+    protected void handleQueueButtonClicked(View queue) {
+        super.handleQueueButtonClicked(queue);
+        setQueueState(mCardViewModel.getQueueVisible(), true);
+    }
+
+    @Override
+    protected void setUpHistoryButton() {
+        super.setUpHistoryButton();
+        setHistoryState(mCardViewModel.getHistoryVisible(), false);
+    }
+
+    @Override
+    protected void handleHistoryButtonClicked(View history) {
+        super.handleHistoryButtonClicked(history);
+        setHistoryState(mCardViewModel.getHistoryVisible(), true);
+    }
+
+    private void setOverflowState(boolean isExpanded, boolean stateSetThroughClick) {
+        if (mActionOverflowButton == null) {
+            return;
+        }
+        if (!mCardViewModel.getPanelExpanded()) {
+            if (stateSetThroughClick) {
+                saveViewVisibilityBeforeAnimation();
+                mCardViewModel.setPanelExpanded(true);
+
+                mPager.setCurrentItem(getOverflowTabIndex());
+
+                mHandler.post(() -> mMotionLayout.transitionToEnd());
+
+                selectOverflow(true);
+            } else {
+                unselectPanel();
+            }
+        } else {
+            // If the panel is already open and overflow is clicked again,
+            // always switch to overflow tab
+            mPager.setCurrentItem(getOverflowTabIndex(), true);
+            mPanel.setEnabled(true);
+
+            selectOverflow(true);
+
+            selectQueue(false);
+            selectHistory(false);
+        }
+    }
+
+    private void setQueueState(boolean isVisible, boolean stateSetThroughClick) {
+        if (mQueueButton == null) {
+            return;
+        }
+        if (!mCardViewModel.getPanelExpanded()) {
+            if (stateSetThroughClick) {
+                saveViewVisibilityBeforeAnimation();
+                mCardViewModel.setPanelExpanded(true);
+                mPager.setCurrentItem(getQueueTabIndex());
+
+                mHandler.post(() -> mMotionLayout.transitionToEnd());
+
+                selectQueue(true);
+            } else {
+                unselectPanel();
+            }
+        } else {
+            // If the panel is already open and queue is clicked again,
+            // always switch to queue tab
+            mPager.setCurrentItem(getQueueTabIndex(), true);
+
+            mPanel.setEnabled(true);
+
+            selectQueue(true);
+
+            selectOverflow(false);
+            selectHistory(false);
+        }
+    }
+
+    private void setHistoryState(boolean isVisible, boolean stateSetThroughClick) {
+        if (mHistoryButton == null) {
+            return;
+        }
+        int historyPos = getHistoryTabIndex();
+        if (!mCardViewModel.getPanelExpanded()) {
+            if (stateSetThroughClick) {
+                saveViewVisibilityBeforeAnimation();
+                mCardViewModel.setPanelExpanded(true);
+                mPager.setCurrentItem(historyPos);
+
+                mHandler.post(() -> mMotionLayout.transitionToEnd());
+
+                selectHistory(true);
+            } else {
+                unselectPanel();
+            }
+        } else {
+            // If the panel is already open and history is clicked again,
+            // always switch to history tab
+            mPager.setCurrentItem(historyPos, true);
+
+            mPanel.setEnabled(true);
+
+            selectHistory(true);
+
+            selectOverflow(false);
+            selectQueue(false);
+        }
+    }
+
+    private void launchMediaAppOrClosePanel() {
+        if (mCardViewModel.getPanelExpanded()) {
+            animateClosePanel();
+        } else {
+            MediaSource mediaSource = mDataModel.getMediaSource().getValue();
+            Intent intent = mediaSource != null ? mediaSource.getIntent() : null;
+            mMediaIntentRouter.handleMediaIntent(intent);
+        }
+    }
+
+    private void animateClosePanel() {
+        mCardViewModel.setPanelExpanded(false);
+        mMotionLayout.transitionToStart();
+        restoreExtraViewsWhenPanelClosed();
+        unselectAllPanelButtons();
+    }
+
+    private void unselectPanel() {
+        mPanel.setEnabled(false);
+        unselectAllPanelButtons();
+    }
+
+    private void selectQueue(boolean shouldSelect) {
+        mCardViewModel.setQueueVisible(shouldSelect);
+        mQueueButton.setSelected(shouldSelect);
+    }
+
+    private void selectOverflow(boolean shouldSelect) {
+        mCardViewModel.setOverflowExpanded(shouldSelect);
+        mActionOverflowButton.setSelected(shouldSelect);
+    }
+
+    private void selectHistory(boolean shouldSelect) {
+        mCardViewModel.setHistoryVisible(shouldSelect);
+        mHistoryButton.setSelected(shouldSelect);
+    }
+
+    private void unselectAllPanelButtons() {
+        selectOverflow(false);
+        selectQueue(false);
+        selectHistory(false);
+    }
+
+    private void saveViewVisibilityBeforeAnimation() {
+        mSubtitleVisibility = mSubtitle.getVisibility();
+        mLogoVisibility = mLogo.getVisibility();
+        mSkipPrevVisibility = mSkipPrevButton.getVisibility();
+        mSkipNextVisibility = mSkipNextButton.getVisibility();
+        mAlbumCoverVisibility = mAlbumCover.getVisibility();
+    }
+
+    private void restoreExtraViewsWhenPanelClosed() {
+        mAlbumCover.setVisibility(mAlbumCoverVisibility);
+        mAppIcon.setVisibility(View.VISIBLE);
+        mSkipPrevButton.setVisibility(mSkipPrevVisibility);
+        mSkipNextButton.setVisibility(mSkipNextVisibility);
+        mSubtitle.setVisibility(mSubtitleVisibility);
+        mLogo.setVisibility(mLogoVisibility);
+    }
+
+    /**
+     * Set the mSkipNextButton and mSkipPrevButton with a skip action Drawable if sent by the
+     * playbackState, otherwise with a skipForwardStandardAction and skipBackStandardAction
+     * respectively. If none exist, hide the button.
+     */
+    private List<PlaybackViewModel.RawCustomPlaybackAction>
+            updateSkipButtonsAndReturnUsedStandardCustomActions(PlaybackStateWrapper playbackState,
+            PlaybackController playbackController) {
+        List<PlaybackViewModel.RawCustomPlaybackAction> usedCustomActions =
+                new ArrayList<PlaybackViewModel.RawCustomPlaybackAction>();
+        updateSkipNextButtonWithSkipOrStandardAction(playbackState, playbackController,
+                usedCustomActions);
+        updateSkipPrevButtonWithSkipOrStandardAction(playbackState, playbackController,
+                usedCustomActions);
+        return usedCustomActions;
+    }
+
+    private void updateSkipNextButtonWithSkipOrStandardAction(
+            PlaybackStateWrapper playbackState, PlaybackController playbackController,
+            List<PlaybackViewModel.RawCustomPlaybackAction> usedCustomActions) {
+        boolean isSkipNextEnabled = playbackState.isSkipNextEnabled();
+        boolean isSkipNextReserved = playbackState.isSkipNextReserved();
+        if ((isSkipNextEnabled || isSkipNextReserved)) {
+            updateButton(mSkipNextButton, mView.getContext().getDrawable(
+                    com.android.car.media.common.R.drawable.ic_skip_next),
+                    mView.getContext().getDrawable(R.drawable.circle_button_background),
+                    true, isSkipNextEnabled, (v) -> {
+                if (playbackController != null) {
+                    playbackController.skipToNext();
+                }
+            });
+        } else {
+            PlaybackViewModel.RawCustomPlaybackAction skipForwardCustomAction =
+                    getFirstCustomActionInSet(playbackState.getCustomActions(),
+                            skipForwardStandardActions);
+            if (skipForwardCustomAction != null) {
+                boolean isCustomActionUsed =
+                        updateButtonWithCustomAction(mSkipNextButton, skipForwardCustomAction,
+                                playbackController);
+                if (isCustomActionUsed) {
+                    usedCustomActions.add(skipForwardCustomAction);
+                }
+            } else {
+                updateButton(mSkipNextButton, null, null, false, false, null);
+            }
+        }
+    }
+
+    private void updateSkipPrevButtonWithSkipOrStandardAction(
+            PlaybackStateWrapper playbackState, PlaybackController playbackController,
+            List<PlaybackViewModel.RawCustomPlaybackAction> usedCustomActions) {
+        boolean isSkipPrevEnabled = playbackState.isSkipPreviousEnabled();
+        boolean isSkipPrevReserved = playbackState.iSkipPreviousReserved();
+        if ((isSkipPrevEnabled || isSkipPrevReserved)) {
+            updateButton(mSkipPrevButton, mView.getContext().getDrawable(
+                    com.android.car.media.common.R.drawable.ic_skip_previous),
+                    mView.getContext().getDrawable(R.drawable.circle_button_background),
+                    true, isSkipPrevEnabled, (v) -> {
+                    if (playbackController != null) {
+                        playbackController.skipToPrevious();
+                    }
+                });
+        } else {
+            PlaybackViewModel.RawCustomPlaybackAction skipBackCustomAction =
+                    getFirstCustomActionInSet(playbackState.getCustomActions(),
+                            skipBackStandardActions);
+            if (skipBackCustomAction != null) {
+                boolean isCustomActionUsed =
+                        updateButtonWithCustomAction(mSkipPrevButton, skipBackCustomAction,
+                                playbackController);
+                if (isCustomActionUsed) {
+                    usedCustomActions.add(skipBackCustomAction);
+                }
+            } else {
+                updateButton(mSkipPrevButton, null, null, false, false, null);
+            }
+        }
+    }
+
+    private void updateButton(ImageButton button, Drawable imageDrawable,
+            Drawable backgroundDrawable, boolean isVisible, boolean isEnabled,
+            View.OnClickListener listener) {
+        button.setImageDrawable(imageDrawable);
+        button.setBackground(backgroundDrawable);
+        ViewUtils.setVisible(button, isVisible);
+        button.setEnabled(isEnabled);
+        button.setOnClickListener(listener);
+    }
+
+    private boolean updateButtonWithCustomAction(ImageButton button,
+            PlaybackViewModel.RawCustomPlaybackAction rawCustomAction,
+            PlaybackController playbackController) {
+        CustomPlaybackAction customAction = rawCustomAction
+                .fetchDrawable(mView.getContext());
+        if (customAction != null) {
+            updateButton(button, customAction.mIcon, mView.getContext().getDrawable(
+                    R.drawable.circle_button_background), true, true, (v) -> {
+                if (playbackController != null) {
+                        playbackController.doCustomAction(
+                                customAction.mAction, customAction.mExtras);
+                }
+            });
+            return true;
+        } else {
+            updateButton(button, null, null, false, false, null);
+            return false;
+        }
+    }
+
+    private int getOverflowTabIndex() {
+        return hasOverflow() ? 0 : -1;
+    }
+
+    private int getQueueTabIndex() {
+        if (!getMediaHasQueue()) return -1;
+        return getOverflowTabIndex() + 1;
+    }
+
+    private int getHistoryTabIndex() {
+        return max(getOverflowTabIndex(), getQueueTabIndex()) + 1;
+    }
+
+    private boolean hasOverflow() {
+        PlaybackStateWrapper playbackState = mDataModel.getPlaybackStateWrapper().getValue();
+        return playbackState != null && playbackState.getCustomActions().size() != 0;
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/AudioFragment.java b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardFragment.java
similarity index 75%
rename from app/src/com/android/car/carlauncher/homescreen/audio/AudioFragment.java
rename to app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardFragment.java
index af9db40..e5e3ebe 100644
--- a/app/src/com/android/car/carlauncher/homescreen/audio/AudioFragment.java
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardFragment.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2020 Google Inc.
+ * Copyright (C) 2023 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.
@@ -14,14 +14,19 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher.homescreen.audio;
+package com.android.car.carlauncher.homescreen.audio.media;
 
+import static android.graphics.Shader.TileMode.MIRROR;
+
+import android.app.Application;
 import android.content.res.ColorStateList;
 import android.graphics.Bitmap;
+import android.graphics.RenderEffect;
 import android.os.Bundle;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Size;
+import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewStub;
@@ -30,23 +35,29 @@
 import android.widget.SeekBar;
 import android.widget.TextView;
 
+import androidx.annotation.NonNull;
 import androidx.fragment.app.FragmentActivity;
+import androidx.lifecycle.ViewModelProvider;
 
 import com.android.car.apps.common.BitmapUtils;
-import com.android.car.apps.common.ImageUtils;
+import com.android.car.carlauncher.Flags;
+import com.android.car.carlauncher.MediaSessionUtils;
 import com.android.car.carlauncher.R;
 import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
 import com.android.car.carlauncher.homescreen.ui.CardContent;
 import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
 import com.android.car.carlauncher.homescreen.ui.SeekBarViewModel;
 import com.android.car.media.common.PlaybackControlsActionBar;
-
+import com.android.car.media.common.source.MediaModels;
+import com.android.car.media.common.ui.PlaybackCardViewModel;
 
 /**
- * {@link HomeCardInterface.View} for the audio card. Displays and controls the current audio source
- * such as the currently playing (or last played) media item or an ongoing phone call.
+ * {@link HomeCardInterface.View} for the media audio card. Displays and controls the current
+ * audio source such as the currently playing (or last played) media item.
  */
-public class AudioFragment extends HomeCardFragment {
+public class MediaCardFragment extends HomeCardFragment {
 
     /**
      * Interface definition for a callback to be invoked when a media layout is inflated.
@@ -59,7 +70,7 @@
         void onMediaViewInitialized();
     }
 
-    private static final String TAG = AudioFragment.class.getSimpleName();
+    private static final String TAG = MediaCardFragment.class.getSimpleName();
 
     private Chronometer mChronometer;
     private View mChronometerSeparator;
@@ -104,30 +115,62 @@
             };
 
     private CardContent.CardBackgroundImage mCardBackgroundImage;
+    private Size mMediaSize;
     private View.OnLayoutChangeListener mOnRootLayoutChangeListener =
             (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                 boolean isWidthChanged = left - right != oldLeft - oldRight;
                 boolean isHeightChanged = top - bottom != oldTop - oldBottom;
                 boolean isSizeChanged = isWidthChanged || isHeightChanged;
                 if (isSizeChanged) {
-                    resizeCardBackgroundImage();
+                    mMediaSize = new Size(right - left, bottom - top);
+                    resizeCardBackgroundImage(mMediaSize);
                 }
             };
 
+    private MediaCardController mMediaCardController;
+    protected MediaCardViewModel mViewModel;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mBlurRadius = getResources().getFloat(R.dimen.card_background_image_blur_radius);
-        mDefaultCardBackgroundImage = new CardContent.CardBackgroundImage(
-                getContext().getDrawable(R.drawable.default_audio_background),
-                getContext().getDrawable(R.drawable.control_bar_image_background));
-        mShowSeekBar = getResources().getBoolean(R.bool.show_seek_bar);
+        if (!Flags.mediaCardFullscreen()) {
+            mBlurRadius = getResources().getFloat(R.dimen.card_background_image_blur_radius);
+            mDefaultCardBackgroundImage = new CardContent.CardBackgroundImage(
+                    getContext().getDrawable(R.drawable.default_audio_background),
+                    getContext().getDrawable(R.drawable.control_bar_image_background));
+            mShowSeekBar = getResources().getBoolean(R.bool.show_seek_bar);
+        } else {
+            mViewModel = new ViewModelProvider(requireActivity()).get(MediaCardViewModel.class);
+            if (mViewModel.needsInitialization()) {
+                MediaModels models = MediaSessionUtils.getMediaModels(getContext());
+                mViewModel.init(models);
+            }
+        }
+    }
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        if (!Flags.mediaCardFullscreen()) {
+            return super.onCreateView(inflater, container, savedInstanceState);
+        } else {
+            return inflater.inflate(R.layout.media_card_fullscreen, container, false);
+        }
     }
 
     @Override
     public void onViewCreated(View view, Bundle savedInstanceState) {
-        super.onViewCreated(view, savedInstanceState);
-        getRootView().addOnLayoutChangeListener(mOnRootLayoutChangeListener);
+        if (!Flags.mediaCardFullscreen()) {
+            super.onViewCreated(view, savedInstanceState);
+            getRootView().addOnLayoutChangeListener(mOnRootLayoutChangeListener);
+        } else {
+            mMediaCardController = (MediaCardController) new MediaCardController.Builder()
+                    .setModels(mViewModel.getPlaybackViewModel(),
+                            mViewModel,
+                            mViewModel.getMediaItemsRepository())
+                    .setViewGroup((ViewGroup) view)
+                    .build();
+        }
     }
 
     @Override
@@ -186,7 +229,7 @@
         return (PlaybackControlsActionBar) mMediaControlBarView;
     }
 
-    private void resizeCardBackgroundImage() {
+    private void resizeCardBackgroundImage(Size cardSize) {
         if (mCardBackgroundImage == null || mCardBackgroundImage.getForeground() == null) {
             mCardBackgroundImage = mDefaultCardBackgroundImage;
         }
@@ -194,8 +237,9 @@
                 getCardBackgroundImage().getHeight());
         // Prioritize size of background image view. Otherwise, use size of whole card
         if (maxDimen == 0) {
-            Size cardSize = getCardSize();
-
+            // This function may be called before a non-null cardSize is ready. Instead of waiting
+            // for the next CardContent update to trigger resizeCardBackgroundImage(), resize the
+            // card as soon as mOnRootChangeLayoutListener sets the mMediaSize.
             if (cardSize == null) {
                 return;
             }
@@ -206,22 +250,23 @@
             return;
         }
         Size scaledSize = new Size(maxDimen, maxDimen);
+
         Bitmap imageBitmap = BitmapUtils.fromDrawable(mCardBackgroundImage.getForeground(),
                 scaledSize);
-        Bitmap blurredBackground = ImageUtils.blur(getContext(), imageBitmap, scaledSize,
-                mBlurRadius);
+        RenderEffect blur = RenderEffect.createBlurEffect(mBlurRadius, mBlurRadius, MIRROR);
+        getCardBackgroundImage().setRenderEffect(blur);
 
         if (mCardBackgroundImage.getBackground() != null) {
             getCardBackgroundImage().setBackground(mCardBackgroundImage.getBackground());
             getCardBackgroundImage().setClipToOutline(true);
         }
-        getCardBackgroundImage().setImageBitmap(blurredBackground, /* showAnimation= */ true);
+        getCardBackgroundImage().setImageBitmap(imageBitmap, /* showAnimation= */ true);
         getCardBackground().setVisibility(View.VISIBLE);
     }
 
     private void updateBackgroundImage(CardContent.CardBackgroundImage cardBackgroundImage) {
         mCardBackgroundImage = cardBackgroundImage;
-        resizeCardBackgroundImage();
+        resizeCardBackgroundImage(mMediaSize);
     }
 
     private void updateMediaView(CharSequence title, CharSequence subtitle) {
@@ -351,4 +396,21 @@
         }
         return mSeekBarWithTimesContainer;
     }
+
+    public static class MediaCardViewModel extends PlaybackCardViewModel {
+
+        private boolean mPanelExpanded = false;
+
+        public MediaCardViewModel(@NonNull Application application) {
+            super(application);
+        }
+
+        public void setPanelExpanded(boolean expanded) {
+            mPanelExpanded = expanded;
+        }
+
+        public boolean getPanelExpanded() {
+            return mPanelExpanded;
+        }
+    }
 }
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPanelViewPagerAdapter.java b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPanelViewPagerAdapter.java
new file mode 100644
index 0000000..007bbf2
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPanelViewPagerAdapter.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio.media;
+
+import static com.android.car.carlauncher.homescreen.audio.media.MediaCardPanelViewPagerAdapter.Tab.HistoryTab;
+import static com.android.car.carlauncher.homescreen.audio.media.MediaCardPanelViewPagerAdapter.Tab.OverflowTab;
+import static com.android.car.carlauncher.homescreen.audio.media.MediaCardPanelViewPagerAdapter.Tab.QueueTab;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageButton;
+import android.widget.TableLayout;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.car.apps.common.util.ViewUtils;
+import com.android.car.carlauncher.R;
+import com.android.car.media.common.CustomPlaybackAction;
+import com.android.car.media.common.playback.PlaybackViewModel;
+import com.android.car.media.common.playback.PlaybackViewModel.PlaybackController;
+import com.android.car.media.common.playback.PlaybackViewModel.PlaybackStateWrapper;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MediaCardPanelViewPagerAdapter extends
+        RecyclerView.Adapter<MediaCardPanelViewPagerAdapter.PanelViewHolder> {
+
+    private final Context mContext;
+    private boolean mHasQueue;
+    private ViewPagerQueueCreator mQueueCreator;
+    private ViewPagerHistoryCreator mHistoryCreator;
+
+    private boolean mHasOverflow;
+    private PlaybackStateWrapper mPlaybackState;
+    private PlaybackController mPlaybackController;
+    private List<PlaybackViewModel.RawCustomPlaybackAction> mCustomActionsToExclude =
+            new ArrayList<PlaybackViewModel.RawCustomPlaybackAction>();
+
+    enum Tab { OverflowTab, QueueTab, HistoryTab };
+
+    public MediaCardPanelViewPagerAdapter(Context context) {
+        this.mContext = context;
+    }
+
+    @NonNull
+    @Override
+    public PanelViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return new PanelViewHolder(LayoutInflater.from(mContext).inflate(
+                R.layout.media_card_panel_content_item, parent, false));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull PanelViewHolder holder, int position) {
+        TableLayout overflowGrid = holder.itemView.findViewById(R.id.overflow_grid);
+        FrameLayout queue = holder.itemView.findViewById(R.id.queue_list_container);
+        FrameLayout history = holder.itemView.findViewById(R.id.history_list_container);
+
+        Tab tab = getTab(position);
+        switch (tab) {
+            case OverflowTab: {
+                updateCustomActionsWithPlaybackState(holder.itemView);
+                break;
+            }
+            case QueueTab: {
+                mQueueCreator.createQueueController(queue);
+                break;
+            }
+            case HistoryTab: {
+                mHistoryCreator.createHistoryController(history);
+                break;
+            }
+        }
+        overflowGrid.setVisibility(tab == OverflowTab ? View.VISIBLE : View.GONE);
+        queue.setVisibility(tab == QueueTab ? View.VISIBLE : View.GONE);
+        history.setVisibility(tab == HistoryTab ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public int getItemCount() {
+        return mHasQueue && mHasOverflow ? 3 : (!mHasQueue && !mHasOverflow ? 1 : 2);
+    }
+
+    /** Notify ViewHolder to rebind when a media source queue status changes */
+    public void setHasQueue(boolean hasQueue) {
+        mHasQueue = hasQueue;
+        notifyDataSetChanged();
+    }
+
+    public void setQueueControllerProvider(ViewPagerQueueCreator queueCreator) {
+        mQueueCreator = queueCreator;
+    }
+
+    public void setHistoryControllerProvider(ViewPagerHistoryCreator historyCreator) {
+        mHistoryCreator = historyCreator;
+    }
+
+    /** Notify ViewHolder to rebind when a media source overflow status changes */
+    public void setHasOverflow(boolean hasOverflow) {
+        if (mHasOverflow != hasOverflow) {
+            mHasOverflow = hasOverflow;
+            notifyDataSetChanged();
+        }
+    }
+
+    /** Notify a change in playback state so ViewHolder binds with latest update */
+    public void notifyPlaybackStateChanged(PlaybackStateWrapper playbackState,
+            PlaybackController playbackController,
+            List<PlaybackViewModel.RawCustomPlaybackAction> customActionsToExclude) {
+        mPlaybackState = playbackState;
+        mPlaybackController = playbackController;
+        mCustomActionsToExclude.clear();
+        mCustomActionsToExclude.addAll(customActionsToExclude);
+        if (mHasOverflow) {
+            notifyItemChanged(0);
+        }
+    }
+
+    private void updateCustomActionsWithPlaybackState(View itemView) {
+        List<ImageButton> actions = ViewUtils.getViewsById(itemView,
+                mContext.getResources(), R.array.playback_action_slot_ids, null);
+        Drawable defaultDrawable = mContext.getDrawable(
+                R.drawable.empty_action_drawable);
+        List<PlaybackViewModel.RawCustomPlaybackAction> customActions = mPlaybackState == null
+                ? new ArrayList<PlaybackViewModel.RawCustomPlaybackAction>()
+                : mPlaybackState.getCustomActions();
+        customActions.removeAll(mCustomActionsToExclude);
+        List<ImageButton> actionsToFill = new ArrayList<>();
+        for (int i = 0; i < actions.size(); i++) {
+            ImageButton button = actions.get(i);
+            if (button != null) {
+                actionsToFill.add(button);
+                button.setBackground(null);
+                button.setImageDrawable(defaultDrawable);
+                button.setImageTintList(ColorStateList.valueOf(
+                        mContext.getResources().getColor(
+                                R.color.car_surface_variant, /* theme */ null)));
+                ViewUtils.setVisible(button, true);
+            }
+        }
+
+        int i = 0;
+        for (PlaybackViewModel.RawCustomPlaybackAction a : customActions) {
+            if (i < actionsToFill.size()) {
+                CustomPlaybackAction customAction = a.fetchDrawable(mContext);
+                if (customAction != null) {
+                    actionsToFill.get(i).setImageDrawable(customAction.mIcon);
+                    actionsToFill.get(i).setBackgroundColor(Color.TRANSPARENT);
+                    actionsToFill.get(i).setImageTintList(ColorStateList.valueOf(
+                            mContext.getResources().getColor(
+                                    R.color.car_on_surface, /* theme */ null)));
+                    ViewUtils.setVisible(actionsToFill.get(i), true);
+                    actionsToFill.get(i).setOnClickListener(v -> {
+                        if (mPlaybackController != null) {
+                            mPlaybackController.doCustomAction(
+                                    customAction.mAction, customAction.mExtras);
+                        }
+                    });
+                }
+                i++;
+            } else {
+                break;
+            }
+        }
+    }
+
+    private Tab getTab(int index) {
+        if (index == getQueueTabIndex()) {
+            return QueueTab;
+        } else if (index == getOverflowTabIndex()) {
+            return OverflowTab;
+        } else {
+            return HistoryTab;
+        }
+    }
+
+    private int getQueueTabIndex() {
+        if (!mHasQueue) return -1;
+        return getOverflowTabIndex() + 1;
+    }
+
+    private int getOverflowTabIndex() {
+        return mHasOverflow ? 0 : -1;
+    }
+
+    static class PanelViewHolder extends RecyclerView.ViewHolder {
+
+        PanelViewHolder(@NonNull View itemView) {
+            super(itemView);
+        }
+    }
+
+    interface ViewPagerQueueCreator {
+        void createQueueController(ViewGroup queueContainer);
+    }
+
+    interface ViewPagerHistoryCreator {
+        void createHistoryController(ViewGroup historyContainer);
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenter.java b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenter.java
new file mode 100644
index 0000000..4ba575b
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenter.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.homescreen.audio.media;
+
+import android.content.Intent;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.car.carlauncher.homescreen.CardPresenter;
+import com.android.car.carlauncher.homescreen.HomeCardFragment;
+import com.android.car.carlauncher.homescreen.HomeCardFragment.OnViewLifecycleChangeListener;
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.AudioModel;
+import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
+import com.android.car.carlauncher.homescreen.ui.CardContent;
+import com.android.car.carlauncher.homescreen.ui.CardHeader;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
+
+import java.util.List;
+
+/**
+ * A portrait UI version of {@link MediaCardPresenter}
+ */
+public class MediaCardPresenter extends CardPresenter {
+
+    public final MediaIntentRouter mMediaIntentRouter = MediaIntentRouter.getInstance();
+
+    private MediaViewModel mViewModel;
+    private MediaCardFragment mFragment;
+    private boolean mShowMedia = true;
+    private CardContent mCachedCardContent;
+    private CardHeader mCachedCardHeader;
+
+    private final HomeCardFragment.OnViewClickListener mOnViewClickListener =
+            new HomeCardFragment.OnViewClickListener() {
+                @Override
+                public void onViewClicked() {
+                    Intent intent = mViewModel.getIntent();
+                    mMediaIntentRouter.handleMediaIntent(intent);
+                }
+            };
+
+    @VisibleForTesting
+    final HomeCardInterface.Model.OnModelUpdateListener mOnMediaModelUpdateListener =
+            new HomeCardInterface.Model.OnModelUpdateListener() {
+                @Override
+                public void onModelUpdate(HomeCardInterface.Model model) {
+                    MediaViewModel mediaViewModel = (MediaViewModel) model;
+                    if (mShowMedia) {
+                        if (mediaViewModel.getCardHeader() != null) {
+                            mFragment.updateHeaderView(mViewModel.getCardHeader());
+                        }
+                        if (mediaViewModel.getCardContent() != null) {
+                            mFragment.updateContentView(mViewModel.getCardContent());
+                        }
+                    } else {
+                        if (mediaViewModel.getCardHeader() != null) {
+                            mCachedCardHeader = mViewModel.getCardHeader();
+                        }
+                        if (mediaViewModel.getCardContent() != null) {
+                            mCachedCardContent = mViewModel.getCardContent();
+                        }
+                    }
+                }
+            };
+
+    private final HomeCardFragment.OnViewLifecycleChangeListener
+            mOnMediaViewLifecycleChangeListener =
+            new OnViewLifecycleChangeListener() {
+                @Override
+                public void onViewCreated() {
+                    mViewModel.setOnProgressUpdateListener(mOnMediaProgressUpdateListener);
+                    mViewModel.setOnModelUpdateListener(mOnMediaModelUpdateListener);
+                    mViewModel.onCreate(getFragment().requireContext());
+                }
+
+                @Override
+                public void onViewDestroyed() {
+                    mViewModel.onDestroy(getFragment().requireContext());
+                }
+            };
+
+    private final MediaCardFragment.OnMediaViewInitializedListener mOnMediaViewInitializedListener =
+            new MediaCardFragment.OnMediaViewInitializedListener() {
+                @Override
+                public void onMediaViewInitialized() {
+                    mFragment.getPlaybackControlsActionBar().setModel(
+                            mViewModel.getPlaybackViewModel(),
+                            mFragment.getViewLifecycleOwner());
+                }
+            };
+
+    private final AudioModel.OnProgressUpdateListener mOnMediaProgressUpdateListener =
+            new AudioModel.OnProgressUpdateListener() {
+                @Override
+                public void onProgressUpdate(AudioModel model, boolean updateProgress) {
+                    if (model == null || model.getCardContent() == null
+                            || model.getCardHeader() == null) {
+                        return;
+                    }
+                    DescriptiveTextWithControlsView descriptiveTextWithControlsContent =
+                            (DescriptiveTextWithControlsView) model.getCardContent();
+                    mFragment.updateProgress(
+                            descriptiveTextWithControlsContent.getSeekBarViewModel(),
+                            updateProgress);
+                }
+            };
+
+    /** Informs this presenter whether or not to process model updates */
+    public void setShowMedia(boolean shouldShowMedia) {
+        mShowMedia = shouldShowMedia;
+        if (shouldShowMedia) {
+            updateFragmentWithCachedContent();
+        }
+    }
+
+    // Deprecated. Use setModel instead.
+    @Override
+    public void setModels(List<HomeCardInterface.Model> models) {
+        // No-op
+    }
+
+    public void setModel(MediaViewModel viewModel) {
+        mViewModel = viewModel;
+    }
+
+    @Override
+    public void setView(HomeCardInterface.View view) {
+        super.setView(view);
+
+        mFragment = (MediaCardFragment) view;
+        mFragment.setOnViewLifecycleChangeListener(mOnMediaViewLifecycleChangeListener);
+        mFragment.setOnViewClickListener(mOnViewClickListener);
+        mFragment.setOnMediaViewInitializedListener(mOnMediaViewInitializedListener);
+    }
+
+    private void updateFragmentWithCachedContent() {
+        if (mCachedCardHeader != null) {
+            mFragment.updateHeaderView(mCachedCardHeader);
+            mCachedCardHeader = null;
+        }
+        if (mCachedCardContent != null) {
+            mFragment.updateContentView(mCachedCardContent);
+            mCachedCardContent = null;
+        }
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaIntentRouter.java b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaIntentRouter.java
new file mode 100644
index 0000000..191d4fa
--- /dev/null
+++ b/app/src/com/android/car/carlauncher/homescreen/audio/media/MediaIntentRouter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 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.carlauncher.homescreen.audio.media;
+
+import android.content.Intent;
+
+import com.android.car.carlauncher.homescreen.audio.IntentHandler;
+
+/**
+ * Routes media {@link Intent} to {@link IntentHandler}.
+ */
+public class MediaIntentRouter {
+    private static MediaIntentRouter sInstance;
+    private IntentHandler mIntentHandler;
+
+    /**
+     * @return an instance of {@link MediaIntentRouter}.
+     */
+    public static MediaIntentRouter getInstance() {
+        if (sInstance == null) {
+            sInstance = new MediaIntentRouter();
+        }
+        return sInstance;
+    }
+
+    /**
+     * Register a {@link IntentHandler}.
+     */
+    public void registerMediaIntentHandler(IntentHandler intentHandler) {
+        mIntentHandler = intentHandler;
+    }
+
+    /**
+     * Dispatch a media intent to {@link IntentHandler}
+     */
+    public void handleMediaIntent(Intent intent) {
+        if (intent != null) {
+            mIntentHandler.handleIntent(intent);
+        }
+    }
+}
diff --git a/app/src/com/android/car/carlauncher/recents/CarQuickStepService.java b/app/src/com/android/car/carlauncher/recents/CarQuickStepService.java
index a4c820e..e5e3c1b 100644
--- a/app/src/com/android/car/carlauncher/recents/CarQuickStepService.java
+++ b/app/src/com/android/car/carlauncher/recents/CarQuickStepService.java
@@ -17,7 +17,7 @@
 package com.android.car.carlauncher.recents;
 
 import static com.android.car.carlauncher.recents.CarRecentsActivity.OPEN_RECENT_TASK_ACTION;
-import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
+import static com.android.wm.shell.shared.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS;
 
 import android.app.ActivityManager;
 import android.app.Service;
@@ -27,11 +27,12 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.view.SurfaceControl;
 
 import androidx.annotation.Nullable;
 
 import com.android.systemui.shared.recents.IOverviewProxy;
+import com.android.systemui.shared.statusbar.phone.BarTransitions;
+import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
 import com.android.wm.shell.recents.IRecentTasks;
 
 import java.util.List;
@@ -141,7 +142,7 @@
         }
 
         @Override
-        public void onSystemUiStateChanged(int stateFlags) {
+        public void onSystemUiStateChanged(@SystemUiStateFlags long stateFlags) {
             // no-op
         }
 
@@ -161,22 +162,58 @@
         }
 
         @Override
+        public void onTransitionModeUpdated(int barMode, boolean checkBarModes) {
+            // no-op
+        }
+
+        @Override
         public void onNavButtonsDarkIntensityChanged(float darkIntensity) {
             // no-op
         }
 
         @Override
+        public void onNavigationBarLumaSamplingEnabled(int displayId, boolean enable) {
+            // no-op
+        }
+
+        @Override
         public void enterStageSplitFromRunningApp(boolean leftOrTop) {
             // no-op
         }
 
         @Override
-        public void onNavigationBarSurface(SurfaceControl surface) {
+        public void onTaskbarToggled() {
             // no-op
         }
 
         @Override
-        public void onTaskbarToggled() {
+        public void updateWallpaperVisibility(int displayId, boolean visible) {
+            // no-op
+        }
+
+        @Override
+        public void checkNavBarModes() {
+            // no-op
+        }
+
+        @Override
+        public void finishBarAnimations() {
+            // no-op
+        }
+
+        @Override
+        public void touchAutoDim(boolean reset) {
+            // no-op
+        }
+
+        @Override
+        public void transitionTo(@BarTransitions.TransitionMode int barMode,
+                boolean animate) {
+            // no-op
+        }
+
+        @Override
+        public void appTransitionPending(boolean pending) {
             // no-op
         }
     }
diff --git a/app/src/com/android/car/carlauncher/recents/CarRecentsActivity.java b/app/src/com/android/car/carlauncher/recents/CarRecentsActivity.java
index 9c69491..0731007 100644
--- a/app/src/com/android/car/carlauncher/recents/CarRecentsActivity.java
+++ b/app/src/com/android/car/carlauncher/recents/CarRecentsActivity.java
@@ -62,6 +62,7 @@
     private Animator mClearAllAnimator;
     private NonDODisabledTaskProvider mNonDODisabledTaskProvider;
     private Set<String> mPackagesToHideFromRecents;
+    private boolean mLaunchMostRecentTaskOnDismiss;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -80,6 +81,8 @@
         mNonDODisabledTaskProvider = new NonDODisabledTaskProvider(this);
         mRecentTasksViewModel.setDisabledTaskProvider(mNonDODisabledTaskProvider);
         WindowMetrics windowMetrics = this.getWindowManager().getCurrentWindowMetrics();
+        mLaunchMostRecentTaskOnDismiss = getResources().getBoolean(
+                R.bool.config_launch_most_recent_task_on_recents_dismiss);
         mRecentTasksViewModel.init(
                 /* displayId= */ getDisplay().getDisplayId(),
                 /* windowWidth= */ windowMetrics.getBounds().width(),
@@ -147,7 +150,9 @@
     protected void onResume() {
         super.onResume();
         if (OPEN_RECENT_TASK_ACTION.equals(getIntent().getAction())) {
-            mRecentTasksViewModel.openMostRecentTask();
+            if (mLaunchMostRecentTaskOnDismiss) {
+                mRecentTasksViewModel.openMostRecentTask();
+            }
             return;
         }
         mRecentTasksViewModel.fetchRecentTaskList();
@@ -174,7 +179,9 @@
         super.onDestroy();
         mNonDODisabledTaskProvider.terminate();
         mRecentTasksViewModel.terminate();
-        mClearAllAnimator.end();
+        if (mClearAllAnimator.isRunning()) {
+            mClearAllAnimator.end();
+        }
         mClearAllAnimator.removeAllListeners();
     }
 
diff --git a/app/src/com/android/car/carlauncher/recents/NonDODisabledTaskProvider.java b/app/src/com/android/car/carlauncher/recents/NonDODisabledTaskProvider.java
index 0a47e26..167a146 100644
--- a/app/src/com/android/car/carlauncher/recents/NonDODisabledTaskProvider.java
+++ b/app/src/com/android/car/carlauncher/recents/NonDODisabledTaskProvider.java
@@ -27,9 +27,8 @@
 import android.os.Build;
 import android.util.Log;
 import android.view.View;
-import android.widget.Toast;
 
-import com.android.car.carlauncher.R;
+import com.android.car.carlaunchercommon.toasts.NonDrivingOptimizedLaunchFailedToast;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -100,9 +99,8 @@
                 }
                 return;
             }
-            CharSequence appName = ai.loadLabel(mPackageManager);
-            String warningText = v.getResources().getString(R.string.driving_toast_text, appName);
-            Toast.makeText(v.getContext(), warningText, Toast.LENGTH_SHORT).show();
+            NonDrivingOptimizedLaunchFailedToast.Companion.showToast(
+                    v.getContext(), ai.loadLabel(mPackageManager).toString());
         };
     }
 
diff --git a/app/src/com/android/car/carlauncher/recents/RecentTasksProvider.java b/app/src/com/android/car/carlauncher/recents/RecentTasksProvider.java
index 884763b..ec1b4af 100644
--- a/app/src/com/android/car/carlauncher/recents/RecentTasksProvider.java
+++ b/app/src/com/android/car/carlauncher/recents/RecentTasksProvider.java
@@ -18,9 +18,9 @@
 
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE;
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -49,7 +49,7 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.wm.shell.recents.IRecentTasks;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 
 import com.google.common.annotations.VisibleForTesting;
 
@@ -237,7 +237,7 @@
     @Override
     public Bitmap getRecentTaskThumbnail(int taskId) {
         ThumbnailData thumbnailData = getRecentTaskThumbnailData(taskId);
-        return thumbnailData != null ? thumbnailData.thumbnail : null;
+        return thumbnailData != null ? thumbnailData.getThumbnail() : null;
     }
 
     @NonNull
@@ -320,11 +320,11 @@
 
     private void getRecentTaskThumbnailAsync(int taskId) {
         sRecentsModelExecutor.execute(() -> {
-            ThumbnailData thumbnailData = mActivityManagerWrapper.getTaskThumbnail(
-                    taskId, /* isLowResolution= */ false);
             if (!mRecentTaskIdToTaskMap.containsKey(taskId)) {
                 return;
             }
+            ThumbnailData thumbnailData = mActivityManagerWrapper.getTaskThumbnail(
+                    taskId, /* isLowResolution= */ false);
             mRecentTaskIdToTaskMap.get(taskId).thumbnail = thumbnailData;
             if (mRecentsDataChangeListener != null) {
                 sMainHandler.post(
@@ -336,6 +336,9 @@
     @VisibleForTesting
     void getRecentTaskIconAsync(int taskId) {
         sRecentsModelExecutor.execute(() -> {
+            if (!mRecentTaskIdToTaskMap.containsKey(taskId)) {
+                return;
+            }
             Task task = mRecentTaskIdToTaskMap.get(taskId);
             Task.TaskKey key = task.key;
             Drawable drawableIcon = getIconFromTaskDescription(task.taskDescription);
@@ -350,9 +353,6 @@
                     drawableIcon = mDefaultIcon;
                 }
             }
-            if (!mRecentTaskIdToTaskMap.containsKey(taskId)) {
-                return;
-            }
             mRecentTaskIdToTaskMap.get(taskId).icon = drawableIcon;
             if (mRecentsDataChangeListener != null) {
                 sMainHandler.post(
diff --git a/app/tests/Android.bp b/app/tests/Android.bp
index 322c2de..0eb8b29 100644
--- a/app/tests/Android.bp
+++ b/app/tests/Android.bp
@@ -16,6 +16,7 @@
 
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
 }
 
 android_test {
@@ -27,7 +28,7 @@
 
     libs: [
         "android.car",
-        "android.test.base",
+        "android.test.base.stubs.system",
         "android.car-system-stubs",
     ],
 
@@ -54,6 +55,9 @@
         "flag-junit",
     ],
 
+    // b/341652226: temporarily disable multi-dex until D8 is fixed
+    no_dex_container: true,
+
     platform_apis: true,
 
     certificate: "platform",
@@ -78,4 +82,6 @@
         "automotive-tests",
         "device-tests",
     ],
+    // TODO(b/319708040): re-enable use_resource_processor
+    use_resource_processor: false,
 }
diff --git a/app/tests/AndroidManifest.xml b/app/tests/AndroidManifest.xml
index 8054b2d..1bb4f4e 100644
--- a/app/tests/AndroidManifest.xml
+++ b/app/tests/AndroidManifest.xml
@@ -23,9 +23,7 @@
         android:label="@string/app_test_title"
         tools:replace="android:label">
 
-        <activity android:name="com.android.car.carlauncher.TaskViewManagerTest$TestActivity"/>
-        <activity
-            android:name="com.android.car.carlauncher.TaskViewInputInterceptorTest$TestActivity"/>
+        <activity android:name="com.android.car.carlauncher.CarLauncherViewModelTest$TestActivity"/>
         <uses-library android:name="android.test.runner"/>
         <provider android:name="com.android.car.carlauncher.calmmode.CalmModeQCProvider"
                  tools:node="remove"/>
diff --git a/docklib-util/res/values/strings.xml b/app/tests/res/values-en-rCA/strings.xml
similarity index 70%
rename from docklib-util/res/values/strings.xml
rename to app/tests/res/values-en-rCA/strings.xml
index 2c24df7..42f3ece 100644
--- a/docklib-util/res/values/strings.xml
+++ b/app/tests/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,9 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_test_title" msgid="5099375056282404070">"CarLauncherTests"</string>
 </resources>
diff --git a/app/tests/src/com/android/car/carlauncher/CarLauncherTest.java b/app/tests/src/com/android/car/carlauncher/CarLauncherTest.java
index 427f065..d62d0fe 100644
--- a/app/tests/src/com/android/car/carlauncher/CarLauncherTest.java
+++ b/app/tests/src/com/android/car/carlauncher/CarLauncherTest.java
@@ -16,47 +16,52 @@
 
 package com.android.car.carlauncher;
 
+import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
 import static android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED;
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.hasWindowLayoutParams;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.hamcrest.CoreMatchers.not;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.verify;
 
+import android.car.app.RemoteCarTaskView;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.car.user.CarUserManager;
-import android.car.user.CarUserManager.UserLifecycleListener;
 import android.content.Intent;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.testing.TestableContext;
 import android.util.ArraySet;
+import android.view.WindowManager;
 
 import androidx.lifecycle.Lifecycle;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.core.app.ActivityScenario;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.filters.Suppress;
 
 import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.Mock;
 
 import java.net.URISyntaxException;
 import java.util.Set;
 
-@Suppress // To be ignored until b/224978827 is fixed
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class CarLauncherTest extends AbstractExtendedMockitoTestCase {
@@ -65,8 +70,9 @@
     public TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
     private ActivityScenario<CarLauncher> mActivityScenario;
 
-    @Mock
-    private CarUserManager mMockCarUserManager;
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
 
     private static final String TOS_MAP_INTENT = "intent:#Intent;"
             + "component=com.android.car.carlauncher/"
@@ -79,6 +85,11 @@
     private static final String CUSTOM_MAP_INTENT = "intent:#Intent;component=com.custom.car.maps/"
             + "com.custom.car.maps.MapActivity;"
             + "action=android.intent.action.MAIN;end";
+    // TOS disabled app list is non empty when TOS is not accepted.
+    private static final String NON_EMPTY_TOS_DISABLED_APPS =
+            "com.test.package1, com.test.package2";
+    // TOS disabled app list is empty when TOS has been accepted or uninitialized.
+    private static final String EMPTY_TOS_DISABLED_APPS = "";
 
     @Override
     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
@@ -95,36 +106,40 @@
 
     @Test
     public void onResume_mapsCard_isVisible() {
-        mActivityScenario = ActivityScenario.launch(CarLauncher.class);
-        mActivityScenario.moveToState(Lifecycle.State.RESUMED);
+        setUpActivityScenario();
 
-        onView(withId(R.id.maps_card)).check(matches(isDisplayed()));
+        onView(withId(R.id.maps_card))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void onResume_assistiveCard_isVisible() {
-        mActivityScenario = ActivityScenario.launch(CarLauncher.class);
-        mActivityScenario.moveToState(Lifecycle.State.RESUMED);
+        setUpActivityScenario();
 
-        onView(withId(R.id.top_card)).check(matches(isDisplayed()));
+        onView(withId(R.id.top_card))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void onResume_fullscreenMediaCard_assistiveCard_isGone() {
+        setUpActivityScenario();
+
+        onView(withId(R.id.top_card))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
     }
 
     @Test
     public void onResume_audioCard_isVisible() {
-        mActivityScenario = ActivityScenario.launch(CarLauncher.class);
-        mActivityScenario.moveToState(Lifecycle.State.RESUMED);
+        setUpActivityScenario();
 
-        onView(withId(R.id.bottom_card)).check(matches(isDisplayed()));
-    }
-
-    @Test
-    public void onDestroy_unregistersUserLifecycleListener() {
-        mActivityScenario = ActivityScenario.launch(CarLauncher.class);
-        mActivityScenario.onActivity(activity -> activity.setCarUserManager(mMockCarUserManager));
-
-        mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
-
-        verify(mMockCarUserManager).removeListener(any(UserLifecycleListener.class));
+        onView(withId(R.id.bottom_card))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
     }
 
     @Test
@@ -193,9 +208,11 @@
     }
 
     @Test
-    public void onCreate_tosStateContentObserver_tosAccepted() {
+    public void onCreate_whenTosAccepted_tosContentObserverIsNull() {
         TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
         Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 2);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                EMPTY_TOS_DISABLED_APPS);
 
         mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
         mActivityScenario.moveToState(Lifecycle.State.RESUMED);
@@ -207,9 +224,11 @@
     }
 
     @Test
-    public void onCreate_registerTosStateContentObserver_tosNotAccepted() {
+    public void onCreate_whenTosNotAccepted_tosContentObserverIsNotNull() {
         TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
         Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                NON_EMPTY_TOS_DISABLED_APPS);
 
         mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
         mActivityScenario.moveToState(Lifecycle.State.RESUMED);
@@ -221,9 +240,11 @@
     }
 
     @Test
-    public void onCreate_registerTosStateContentObserver_tosNotInitialized() {
+    public void onCreate_whenTosNotInitialized_tosContentObserverIsNotNull() {
         TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
         Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 0);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                EMPTY_TOS_DISABLED_APPS);
 
         mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
         mActivityScenario.moveToState(Lifecycle.State.RESUMED);
@@ -235,9 +256,11 @@
     }
 
     @Test
-    public void recreate_tosStateContentObserver_tosNotAccepted() {
+    public void recreate_afterTosIsAccepted_tosStateContentObserverIsNull() {
         TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
-        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 0);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                NON_EMPTY_TOS_DISABLED_APPS);
 
         mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
 
@@ -246,30 +269,94 @@
 
             // Accept TOS
             Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 2);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    KEY_UNACCEPTED_TOS_DISABLED_APPS, EMPTY_TOS_DISABLED_APPS);
             activity.mTosContentObserver.onChange(true);
         });
+
         // Content observer is null after recreate
         mActivityScenario.onActivity(activity -> assertNull(activity.mTosContentObserver));
     }
 
     @Test
-    public void recreate_tosStateContentObserver_tosNotInitialized() {
+    public void recreate_afterTosIsInitialized_tosStateContentObserverIsNotNull() {
         TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
         Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 0);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                EMPTY_TOS_DISABLED_APPS);
 
         mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
 
         mActivityScenario.onActivity(activity -> {
             assertNotNull(activity.mTosContentObserver); // Content observer is setup
 
-            // TOS changed to unaccepted
+            // Initialize TOS
             Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    KEY_UNACCEPTED_TOS_DISABLED_APPS, NON_EMPTY_TOS_DISABLED_APPS);
             activity.mTosContentObserver.onChange(true);
         });
+
         // Content observer is not null after recreate
         mActivityScenario.onActivity(activity -> assertNotNull(activity.mTosContentObserver));
     }
 
+    @Test
+    public void recreate_afterTosIsInitialized_releaseTaskView() {
+        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 0);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                EMPTY_TOS_DISABLED_APPS);
+
+        mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
+
+        mActivityScenario.onActivity(activity -> {
+            assertNotNull(activity.mCarLauncherViewModel); // CarLauncherViewModel is setup
+
+            RemoteCarTaskView oldRemoteCarTaskView =
+                    activity.mCarLauncherViewModel.getRemoteCarTaskView().getValue();
+            assertNotNull(oldRemoteCarTaskView);
+
+            // Initialize TOS
+            Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    KEY_UNACCEPTED_TOS_DISABLED_APPS, NON_EMPTY_TOS_DISABLED_APPS);
+            activity.mTosContentObserver.onChange(true);
+
+            // Different instance of task view since TOS has gone from uninitialized to initialized
+            assertThat(oldRemoteCarTaskView).isNotSameInstanceAs(
+                    activity.mCarLauncherViewModel.getRemoteCarTaskView().getValue());
+        });
+    }
+
+    @Test
+    public void recreate_afterTosIsAccepted_releaseTaskView() {
+        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
+        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
+        Settings.Secure.putString(mContext.getContentResolver(), KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                NON_EMPTY_TOS_DISABLED_APPS);
+
+        mActivityScenario = ActivityScenario.launch(new Intent(mContext, CarLauncher.class));
+
+        mActivityScenario.onActivity(activity -> {
+            assertNotNull(activity.mCarLauncherViewModel); // CarLauncherViewModel is setup
+
+            RemoteCarTaskView oldRemoteCarTaskView =
+                    activity.mCarLauncherViewModel.getRemoteCarTaskView().getValue();
+            assertNotNull(oldRemoteCarTaskView);
+
+            // Accept TOS
+            Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 2);
+            Settings.Secure.putString(mContext.getContentResolver(),
+                    KEY_UNACCEPTED_TOS_DISABLED_APPS, EMPTY_TOS_DISABLED_APPS);
+            activity.mTosContentObserver.onChange(true);
+
+            // Different instance of task view since TOS has been accepted
+            assertThat(oldRemoteCarTaskView).isNotSameInstanceAs(
+                    activity.mCarLauncherViewModel.getRemoteCarTaskView().getValue());
+        });
+    }
+
     private Intent createIntentFromString(String intentString) {
         try {
             return Intent.parseUri(intentString, Intent.URI_ANDROID_APP_SCHEME);
@@ -284,4 +371,16 @@
         packages.add("com.android.car.assistant");
         return packages;
     }
+
+    private void setUpActivityScenario() {
+        mActivityScenario = ActivityScenario.launch(CarLauncher.class);
+        mActivityScenario.moveToState(Lifecycle.State.RESUMED);
+        mActivityScenario.onActivity(activity -> {
+            activity.runOnUiThread(new Runnable() {
+                public void run() {
+                    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+                }
+            });
+        });
+    }
 }
diff --git a/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelFactoryTest.java b/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelFactoryTest.java
index 722a3ed..b11a40f 100644
--- a/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelFactoryTest.java
+++ b/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelFactoryTest.java
@@ -34,7 +34,7 @@
 import androidx.test.ext.junit.rules.ActivityScenarioRule;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.car.carlauncher.TaskViewManagerTest.TestActivity;
+import com.android.car.carlauncher.CarLauncherViewModelTest.TestActivity;
 
 import org.junit.After;
 import org.junit.Before;
@@ -64,7 +64,7 @@
                 .createWindowContext(TYPE_APPLICATION_STARTING, /* options */ null);
         when(mContext.createWindowContext(eq(WindowManager.LayoutParams.TYPE_APPLICATION_STARTING),
                 any())).thenReturn(windowContext);
-        mCarLauncherViewModelFactory = new CarLauncherViewModelFactory(mContext, mIntent);
+        mCarLauncherViewModelFactory = new CarLauncherViewModelFactory(mContext);
     }
 
     @After
diff --git a/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelTest.java b/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelTest.java
index 087d5a1..33d1611 100644
--- a/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelTest.java
+++ b/app/tests/src/com/android/car/carlauncher/CarLauncherViewModelTest.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.Activity;
 import android.app.Instrumentation;
 import android.car.app.RemoteCarTaskView;
 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
@@ -29,8 +30,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.car.carlauncher.TaskViewManagerTest.TestActivity;
-
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
@@ -38,6 +37,9 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @RunWith(AndroidJUnit4.class)
 public final class CarLauncherViewModelTest extends AbstractExtendedMockitoTestCase {
     private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -93,7 +95,8 @@
     }
 
     private CarLauncherViewModel createCarLauncherViewModel() {
-        CarLauncherViewModel carLauncherViewModel = new CarLauncherViewModel(mActivity, mIntent);
+        CarLauncherViewModel carLauncherViewModel = new CarLauncherViewModel(mActivity);
+        carLauncherViewModel.initializeRemoteCarTaskView(mIntent);
         runOnMain(() -> carLauncherViewModel.getRemoteCarTaskView().observeForever(
                 remoteCarTaskView -> mRemoteCarTaskView = remoteCarTaskView));
         mInstrumentation.waitForIdleSync();
@@ -110,4 +113,21 @@
     private void runOnMain(Runnable runnable) {
         mContext.getMainExecutor().execute(runnable);
     }
+
+
+    public static class TestActivity extends Activity {
+        private static final int FINISH_TIMEOUT_MS = 1000;
+        private final CountDownLatch mDestroyed = new CountDownLatch(1);
+
+        @Override
+        protected void onDestroy() {
+            super.onDestroy();
+            mDestroyed.countDown();
+        }
+
+        void finishCompletely() throws InterruptedException {
+            finish();
+            mDestroyed.await(FINISH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+    }
 }
diff --git a/app/tests/src/com/android/car/carlauncher/TaskViewInputInterceptorTest.java b/app/tests/src/com/android/car/carlauncher/TaskViewInputInterceptorTest.java
deleted file mode 100644
index 7c90094..0000000
--- a/app/tests/src/com/android/car/carlauncher/TaskViewInputInterceptorTest.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.view.WindowManager.LayoutParams.INPUT_FEATURE_SPY;
-import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-
-import android.app.Activity;
-import android.app.UiAutomation;
-import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.hardware.input.InputManager;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.SystemClock;
-import android.view.InputDevice;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.lifecycle.Lifecycle;
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.ext.junit.rules.ActivityScenarioRule;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.Suppress;
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-@Suppress // CarTaskView in Launcher are not supported anymore and hence this test is not required
-@RunWith(AndroidJUnit4.class)
-public class TaskViewInputInterceptorTest extends AbstractExtendedMockitoTestCase {
-    private static final UiAutomation UI_AUTOMATION =
-            InstrumentationRegistry.getInstrumentation().getUiAutomation();
-    private final List<ControlledCarTaskView> mControlledCarTaskViews = new ArrayList<>();
-
-    @Rule
-    public ActivityScenarioRule mActivityRule = new ActivityScenarioRule<>(TestActivity.class);
-
-    @Mock TaskViewManager mTaskViewManager;
-    @Captor ArgumentCaptor<WindowManager.LayoutParams> mLayoutParamsArgumentCaptor;
-    @Captor ArgumentCaptor<View> mSpyWindowArgumentCaptor;
-    private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
-    private TestActivity mActivity;
-    private TaskViewInputInterceptor mTaskViewInputInterceptor;
-    private ActivityScenario<TestActivity> mScenario;
-
-    @Before
-    public void setup() throws Exception {
-        mScenario = mActivityRule.getScenario();
-        mScenario.onActivity(activity -> mActivity = activity);
-
-        if (Looper.myLooper() == null) {
-            Looper.prepare();
-        }
-        runOnMainAndWait(
-                () -> {
-                    mTaskViewInputInterceptor =
-                            new TaskViewInputInterceptor(mActivity, mTaskViewManager);
-                });
-
-        doReturn(mControlledCarTaskViews).when(mTaskViewManager).getControlledTaskViews();
-    }
-
-    @After
-    public void tearDown() throws InterruptedException {
-        if (mActivity == null) {
-            return;
-        }
-        mActivity.finishCompletely();
-    }
-
-    @Test
-    public void init_addsSpyWindow() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-
-        verify(mActivity.mSpyWm).addView(any(), mLayoutParamsArgumentCaptor.capture());
-        assertThat(mLayoutParamsArgumentCaptor.getValue().inputFeatures)
-                .isEqualTo(INPUT_FEATURE_SPY);
-    }
-
-    @Test
-    public void init_again_doesNothing() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-
-        verify(mActivity.mSpyWm, times(1)).addView(any(), any());
-    }
-
-    @Test
-    public void activityStopped_removesSpyWindow() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-
-        mScenario.moveToState(Lifecycle.State.CREATED);
-
-        verify(mActivity.mSpyWm).removeView(any());
-    }
-
-    @Test
-    public void activityStoppedStarted_addsSpyWindow() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        mScenario.moveToState(Lifecycle.State.CREATED);
-
-        mScenario.moveToState(Lifecycle.State.STARTED);
-
-        verify(mActivity.mSpyWm, times(2)).addView(any(), any());
-    }
-
-    @Test
-    public void singleTap_insideTaskView_capturingEnabled_noCapturing() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        createControlledCarTaskView(new Rect(10, 0, 30, 100), /* capturingEnabled= */ true);
-
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        MotionEvent moveEventOnTheDownEventLocation =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_MOVE,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent upEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-
-        // Act
-        spyWindow.dispatchTouchEvent(downEvent);
-        spyWindow.dispatchTouchEvent(moveEventOnTheDownEventLocation);
-        spyWindow.dispatchTouchEvent(upEvent);
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(0)).pilferPointers(any());
-    }
-
-    @Test
-    public void longPress_insideTaskView_capturingEnabled_capturesGesture() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        ControlledCarTaskView taskView =
-                createControlledCarTaskView(new Rect(10, 0, 30, 100), /* capturingEnabled= */ true);
-        View.OnLongClickListener taskViewLongClickListener = mock(View.OnLongClickListener.class);
-        doReturn(taskViewLongClickListener).when(taskView).getOnLongClickListener();
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-
-        // Act
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        runOnMainAndWait(() -> spyWindow.dispatchTouchEvent(downEvent));
-        waitForLongPressTimeout();
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(1)).pilferPointers(any());
-        verify(taskViewLongClickListener).onLongClick(any());
-    }
-
-    @Test
-    public void longPress_insideTaskView_capturingDisabled_noCapturing() throws Exception {
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        ControlledCarTaskView taskView =
-                createControlledCarTaskView(
-                        new Rect(10, 0, 30, 100), /* capturingEnabled= */ false);
-        View.OnLongClickListener taskViewLongClickListener = mock(View.OnLongClickListener.class);
-        doReturn(taskViewLongClickListener).when(taskView).getOnLongClickListener();
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-
-        // Act
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        runOnMainAndWait(() -> spyWindow.dispatchTouchEvent(downEvent));
-        waitForLongPressTimeout();
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(0)).pilferPointers(any());
-        verifyZeroInteractions(taskViewLongClickListener);
-    }
-
-    private void waitForLongPressTimeout() throws InterruptedException {
-        CountDownLatch l = new CountDownLatch(1);
-        Handler handler = new Handler(Looper.getMainLooper());
-        handler.postDelayed(() -> l.countDown(), ViewConfiguration.getLongPressTimeout());
-        l.await(1, TimeUnit.SECONDS);
-    }
-
-    @Test
-    public void swipeGesture_whenActionDownInsideTaskView_capturingEnabled_capturesGesture()
-            throws Exception {
-        // Arrange
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        createControlledCarTaskView(new Rect(10, 0, 30, 100), /* capturingEnabled= */ true);
-
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent moveEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_MOVE,
-                        /* x= */ 12,
-                        /* y= */ 7,
-                        /* metaState= */ 0);
-        moveEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent upEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 12,
-                        /* y= */ 9,
-                        /* metaState= */ 0);
-        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-
-        // Act
-        runOnMainAndWait(
-                () -> {
-                    spyWindow.dispatchTouchEvent(downEvent);
-                    spyWindow.dispatchTouchEvent(moveEvent);
-                    spyWindow.dispatchTouchEvent(upEvent);
-                });
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(2)).pilferPointers(any());
-        assertThat(mActivity.mEventsReceived.size()).isEqualTo(3);
-        assertThat(mActivity.mEventsReceived.get(0).getAction()).isEqualTo(MotionEvent.ACTION_DOWN);
-        assertThat(mActivity.mEventsReceived.get(0).getX()).isEqualTo(12);
-        assertThat(mActivity.mEventsReceived.get(0).getY()).isEqualTo(7);
-        assertThat(mActivity.mEventsReceived.get(1).getAction()).isEqualTo(MotionEvent.ACTION_MOVE);
-        assertThat(mActivity.mEventsReceived.get(1).getX()).isEqualTo(12);
-        assertThat(mActivity.mEventsReceived.get(1).getY()).isEqualTo(7);
-        assertThat(mActivity.mEventsReceived.get(2).getAction()).isEqualTo(MotionEvent.ACTION_UP);
-        assertThat(mActivity.mEventsReceived.get(2).getX()).isEqualTo(12);
-        assertThat(mActivity.mEventsReceived.get(2).getY()).isEqualTo(9);
-    }
-
-    @Test
-    public void swipeGesture_whenActionDownInsideTaskView_capturingDisabled_noCapturing()
-            throws Exception {
-        // Arrange
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        createControlledCarTaskView(new Rect(10, 0, 30, 100), /* capturingEnabled= */ false);
-
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 11,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent moveEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_MOVE,
-                        /* x= */ 12,
-                        /* y= */ 7,
-                        /* metaState= */ 0);
-        moveEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent upEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 12,
-                        /* y= */ 9,
-                        /* metaState= */ 0);
-        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-
-        // Act
-        runOnMainAndWait(
-                () -> {
-                    spyWindow.dispatchTouchEvent(downEvent);
-                    spyWindow.dispatchTouchEvent(moveEvent);
-                    spyWindow.dispatchTouchEvent(upEvent);
-                });
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(0)).pilferPointers(any());
-        assertThat(mActivity.mEventsReceived.size()).isEqualTo(0);
-    }
-
-    @Test
-    public void swipeGesture_whenActionDownOutsideTaskView_capturingEnabled_noCapturing()
-            throws Exception {
-        // Arrange
-        runOnMainAndWait(() -> mTaskViewInputInterceptor.init());
-        verify(mActivity.mSpyWm).addView(mSpyWindowArgumentCaptor.capture(), any());
-        createControlledCarTaskView(new Rect(10, 0, 30, 100), /* capturingEnabled= */ true);
-
-        View spyWindow = mSpyWindowArgumentCaptor.getValue();
-        final long eventTime = SystemClock.uptimeMillis();
-        MotionEvent downEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_DOWN,
-                        /* x= */ 8,
-                        /* y= */ 2,
-                        /* metaState= */ 0);
-        downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent moveEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_MOVE,
-                        /* x= */ 8,
-                        /* y= */ 7,
-                        /* metaState= */ 0);
-        moveEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-        MotionEvent upEvent =
-                MotionEvent.obtain(
-                        /* downTime= */ eventTime,
-                        /* eventTime= */ eventTime,
-                        MotionEvent.ACTION_UP,
-                        /* x= */ 8,
-                        /* y= */ 9,
-                        /* metaState= */ 0);
-        upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
-
-        // Act
-        runOnMainAndWait(
-                () -> {
-                    spyWindow.dispatchTouchEvent(downEvent);
-                    spyWindow.dispatchTouchEvent(moveEvent);
-                    spyWindow.dispatchTouchEvent(upEvent);
-                });
-
-        // Assert
-        verify(mActivity.mSpyInputManager, times(0)).pilferPointers(any());
-        assertThat(mActivity.mEventsReceived.size()).isEqualTo(0);
-    }
-
-    private ControlledCarTaskView createControlledCarTaskView(
-            Rect bounds, boolean capturingEnabled) {
-        ControlledCarTaskView taskView = mock(ControlledCarTaskView.class);
-
-        doAnswer(
-                invocation -> {
-                    Rect r = invocation.getArgument(0);
-                    r.set(bounds);
-                    return null;
-                })
-                .when(taskView)
-                .getBoundsOnScreen(any());
-
-        doReturn(
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(new Intent())
-                        .setCaptureGestures(capturingEnabled)
-                        .setCaptureLongPress(capturingEnabled)
-                        .build())
-                .when(taskView)
-                .getConfig();
-        mControlledCarTaskViews.add(taskView);
-        return taskView;
-    }
-
-    private void runOnMainAndWait(Runnable r) throws Exception {
-        mActivity
-                .getMainExecutor()
-                .execute(
-                        () -> {
-                            r.run();
-                            mIdleHandlerLatch.countDown();
-                            mIdleHandlerLatch = new CountDownLatch(1);
-                        });
-        mIdleHandlerLatch.await(5, TimeUnit.SECONDS);
-    }
-
-    public static class TestActivity extends Activity {
-        private static final int FINISH_TIMEOUT_MS = 1000;
-        private final CountDownLatch mDestroyed = new CountDownLatch(1);
-        private final List<MotionEvent> mEventsReceived = new ArrayList<>();
-        private WindowManager mSpyWm;
-        private InputManager mSpyInputManager;
-
-        @Override
-        protected void onCreate(@Nullable Bundle savedInstanceState) {
-            super.onCreate(savedInstanceState);
-            mSpyWm = spy(getSystemService(WindowManager.class));
-            mSpyInputManager = spy(getSystemService(InputManager.class));
-        }
-
-        @Override
-        protected void onDestroy() {
-            super.onDestroy();
-            mDestroyed.countDown();
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            // MotionEvent.obtain() is important otherwise the event is recycled and by the time
-            // assertion happens, the values might be changed
-            mEventsReceived.add(MotionEvent.obtain(event));
-            return super.onTouchEvent(event);
-        }
-
-        @Override
-        public Object getSystemService(@NonNull String name) {
-            if (name.equals(Context.WINDOW_SERVICE)) {
-                if (mSpyWm != null) {
-                    return mSpyWm;
-                }
-            }
-            if (name.equals(Context.INPUT_SERVICE)) {
-                if (mSpyInputManager != null) {
-                    return mSpyInputManager;
-                }
-            }
-            return super.getSystemService(name);
-        }
-
-        void finishCompletely() throws InterruptedException {
-            finish();
-            mDestroyed.await(FINISH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        }
-    }
-}
diff --git a/app/tests/src/com/android/car/carlauncher/TaskViewManagerTest.java b/app/tests/src/com/android/car/carlauncher/TaskViewManagerTest.java
deleted file mode 100644
index 1e2411d..0000000
--- a/app/tests/src/com/android/car/carlauncher/TaskViewManagerTest.java
+++ /dev/null
@@ -1,1068 +0,0 @@
-/*
- * Copyright (C) 2022 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.carlauncher;
-
-import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK;
-import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT;
-
-import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_SWITCHING;
-import static com.android.car.internal.common.CommonConstants.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.notNull;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyZeroInteractions;
-import static org.mockito.Mockito.when;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
-import android.app.TaskStackListener;
-import android.car.Car;
-import android.car.app.CarActivityManager;
-import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.car.user.CarUserManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Looper;
-import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.window.TaskAppearedInfo;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import androidx.annotation.NonNull;
-import androidx.lifecycle.Lifecycle;
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.ext.junit.rules.ActivityScenarioRule;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.car.carlauncher.taskstack.TaskStackChangeListeners;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.HandlerExecutor;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.startingsurface.StartingWindowController;
-import com.android.wm.shell.sysui.ShellController;
-import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.taskview.TaskView;
-import com.android.wm.shell.transition.Transitions;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.stream.Collectors;
-
-//TODO(b/266742500): Test that uses setUpLaunchRootTaskView get null listener
-@RunWith(AndroidJUnit4.class)
-public class TaskViewManagerTest extends AbstractExtendedMockitoTestCase {
-    @Rule
-    public ActivityScenarioRule mActivityRule = new ActivityScenarioRule<>(TestActivity.class);
-
-    @Mock
-    private ShellTaskOrganizer mOrganizer;
-    @Mock
-    private SyncTransactionQueue mSyncQueue;
-    @Mock
-    private Transitions mTransitions;
-    @Mock
-    private HandlerExecutor mShellExecutor;
-    @Mock
-    private CarActivityManager mCarActivityManager;
-    @Mock
-    private Car mCar;
-    @Mock
-    private TaskStackChangeListeners mTaskStackChangeListeners;
-    @Mock
-    private CarUserManager mCarUserManager;
-    @Mock
-    private WindowContainerToken mToken;
-
-    @Mock
-    private ShellController mShellController;
-    @Mock
-    private StartingWindowController mStartingWindowController;
-    @Mock
-    private TaskViewInputInterceptor mTaskViewInputInterceptor;
-
-    @Captor
-    private ArgumentCaptor<TaskStackListener> mTaskStackListenerArgumentCaptor;
-    @Captor
-    private ArgumentCaptor<CarUserManager.UserLifecycleListener>
-            mUserLifecycleListenerArgumentCaptor;
-
-    private TestActivity mActivity;
-    private Car.CarServiceLifecycleListener mCarServiceLifecycleListener;
-    private ActivityTaskManager mSpyActivityTaskManager;
-    private CountDownLatch mIdleHandlerLatch = new CountDownLatch(1);
-    private SurfaceControl mLeash;
-
-    @Override
-    protected void onSessionBuilder(@NonNull CustomMockitoSessionBuilder builder) {
-        builder.spyStatic(ActivityTaskManager.class);
-        builder.spyStatic(Car.class);
-        builder.spyStatic(TaskStackChangeListeners.class);
-    }
-
-    @Before
-    public void setUp() {
-        ExtendedMockito.doAnswer(invocation -> {
-            mCarServiceLifecycleListener = invocation.getArgument(3);
-            return mCar;
-        }).when(() -> Car.createCar(any(), any(), anyLong(), any()));
-        when(mCar.getCarManager(eq(Car.CAR_ACTIVITY_SERVICE))).thenReturn(mCarActivityManager);
-        when(mCar.getCarManager(eq(Car.CAR_USER_SERVICE))).thenReturn(mCarUserManager);
-
-        ExtendedMockito.doReturn(mTaskStackChangeListeners).when(() ->
-                TaskStackChangeListeners.getInstance());
-        doNothing().when(mTaskStackChangeListeners).registerTaskStackListener(
-                mTaskStackListenerArgumentCaptor.capture());
-
-        doNothing().when(mCarUserManager).addListener(any(), any(),
-                mUserLifecycleListenerArgumentCaptor.capture());
-
-        mLeash = new SurfaceControl.Builder(null)
-                .setName("test")
-                .build();
-
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            SyncTransactionQueue.TransactionRunnable r =
-                    invocationOnMock.getArgument(0);
-            r.runWithTransaction(new SurfaceControl.Transaction());
-            return null;
-        }).when(mSyncQueue).runInSync(any());
-
-        doAnswer((InvocationOnMock invocationOnMock) -> {
-            Runnable r = invocationOnMock.getArgument(0);
-            r.run();
-            return null;
-        }).when(mShellExecutor).execute(any());
-        doReturn(mShellExecutor).when(mOrganizer).getExecutor();
-
-        mSpyActivityTaskManager = spy(ActivityTaskManager.getInstance());
-        ExtendedMockito.doReturn(mSpyActivityTaskManager).when(() ->
-                ActivityTaskManager.getInstance());
-
-        ActivityScenario<TestActivity> scenario = mActivityRule.getScenario();
-        scenario.onActivity(activity -> mActivity = activity);
-    }
-
-    @After
-    public void tearDown() throws InterruptedException {
-        mActivity.finishCompletely();
-    }
-
-    private TaskAppearedInfo createMultiWindowTask(int taskId) {
-        ActivityManager.RunningTaskInfo taskInfo =
-                new ActivityManager.RunningTaskInfo();
-        taskInfo.taskId = taskId;
-        taskInfo.configuration.windowConfiguration.setWindowingMode(
-                WINDOWING_MODE_MULTI_WINDOW);
-        taskInfo.parentTaskId = INVALID_TASK_ID;
-        taskInfo.token = mock(WindowContainerToken.class);
-        taskInfo.isVisible = true;
-        return new TaskAppearedInfo(taskInfo, new SurfaceControl());
-    }
-
-    private TaskAppearedInfo createMultiWindowTask(int taskId, IBinder token) {
-        TaskAppearedInfo taskInfo = createMultiWindowTask(taskId);
-        when(taskInfo.getTaskInfo().token.asBinder()).thenReturn(token);
-        return taskInfo;
-    }
-
-    @Test
-    public void init_cleansUpExistingMultiWindowTasks() {
-        TaskAppearedInfo existingTask1 = createMultiWindowTask(/* taskId= */ 1);
-        TaskAppearedInfo existingTask2 = createMultiWindowTask(/* taskId= */ 2);
-        doReturn(ImmutableList.of(existingTask1, existingTask2))
-                .when(mOrganizer).registerOrganizer();
-        ExtendedMockito.doReturn(false).when(mSpyActivityTaskManager).removeTask(anyInt());
-
-        createTaskViewManager();
-
-        verify(mSpyActivityTaskManager).removeTask(eq(1));
-        verify(mSpyActivityTaskManager).removeTask(eq(2));
-    }
-
-    @Test
-    public void testCreateControlledTaskView() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-
-        Intent activityIntent = new Intent();
-        Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package");
-        ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock(
-                ControlledCarTaskViewCallbacks.class);
-        when(controlledCarTaskViewCallbacks.getDependingPackageNames())
-                .thenReturn(packagesThatCanRestart);
-
-        taskViewManager.createControlledCarTaskView(
-                mActivity.getMainExecutor(),
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(activityIntent)
-                        .setAutoRestartOnCrash(false)
-                        .build(),
-                controlledCarTaskViewCallbacks
-        );
-
-        runOnMainAndWait(() -> {});
-        verify(controlledCarTaskViewCallbacks).onTaskViewCreated(any());
-        verifyZeroInteractions(mTaskViewInputInterceptor);
-    }
-
-    @Test
-    public void testCreateControlledTaskView_initializesInterceptor_whenCapturingEvents() throws
-            Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-
-        Intent activityIntent = new Intent();
-        Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package");
-        ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock(
-                ControlledCarTaskViewCallbacks.class);
-        when(controlledCarTaskViewCallbacks.getDependingPackageNames())
-                .thenReturn(packagesThatCanRestart);
-
-        taskViewManager.createControlledCarTaskView(
-                mActivity.getMainExecutor(),
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(activityIntent)
-                        .setAutoRestartOnCrash(false)
-                        .setCaptureLongPress(true)
-                        .build(),
-                controlledCarTaskViewCallbacks
-        );
-
-        runOnMainAndWait(() -> {});
-        verify(mTaskViewInputInterceptor).init();
-    }
-
-    @Test
-    public void testCreateControlledTaskView_callsOnReadyWhenVisible() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        Intent activityIntent = new Intent("ACTION_VIEW");
-        Set<String> packagesThatCanRestart = ImmutableSet.of("com.random.package");
-        ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock(
-                ControlledCarTaskViewCallbacks.class);
-        when(controlledCarTaskViewCallbacks.getDependingPackageNames())
-                .thenReturn(packagesThatCanRestart);
-        taskViewManager.createControlledCarTaskView(
-                mActivity.getMainExecutor(),
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(activityIntent)
-                        .setAutoRestartOnCrash(false)
-                        .build(),
-                controlledCarTaskViewCallbacks
-        );
-        ControlledCarTaskView taskView = spy(taskViewManager.getControlledTaskViews().get(0));
-        doNothing().when(taskView).startActivity();
-
-        taskView.surfaceCreated(mock(SurfaceHolder.class));
-
-        runOnMainAndWait(() -> {});
-        verify(controlledCarTaskViewCallbacks).onTaskViewCreated(any());
-        verify(controlledCarTaskViewCallbacks).onTaskViewReady();
-    }
-
-    @Test
-    public void testCreateLaunchRootTaskView() throws Exception {
-        LaunchRootCarTaskViewCallbacks taskViewCallbacks =
-                mock(LaunchRootCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-
-        taskViewManager.createLaunchRootTaskView(
-                mActivity.getMainExecutor(),
-                taskViewCallbacks
-        );
-        runOnMainAndWait(() -> {});
-        TaskView taskView = taskViewManager.getLaunchRootCarTaskView();
-        taskView.surfaceCreated(mock(SurfaceHolder.class));
-
-        runOnMainAndWait(() -> {});
-        verify(taskViewCallbacks).onTaskViewCreated(any());
-        verify(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY),
-                eq(WINDOWING_MODE_MULTI_WINDOW),
-                any(ShellTaskOrganizer.TaskListener.class),
-                /* removeWithTaskOrganizer= */ eq(true));
-    }
-
-    @Test
-    public void testCreateLaunchRootTaskView_callsOnReadyWhenVisible() throws Exception {
-        TaskAppearedInfo fakeLaunchRootTaskInfo  = createMultiWindowTask(1);
-        LaunchRootCarTaskViewCallbacks taskViewCallbacks =
-                mock(LaunchRootCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        doAnswer(invocation -> {
-            ShellTaskOrganizer.TaskListener listener = invocation.getArgument(2);
-            listener.onTaskAppeared(fakeLaunchRootTaskInfo.getTaskInfo(), mLeash);
-            return null;
-        }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY),
-                eq(WINDOWING_MODE_MULTI_WINDOW),
-                any(ShellTaskOrganizer.TaskListener.class),
-                /* removeWithTaskOrganizer= */ eq(true));
-
-        taskViewManager.createLaunchRootTaskView(
-                mActivity.getMainExecutor(),
-                taskViewCallbacks
-        );
-        runOnMainAndWait(() -> {});
-        TaskView taskView = taskViewManager.getLaunchRootCarTaskView();
-        taskView.surfaceCreated(mock(SurfaceHolder.class));
-
-        runOnMainAndWait(() -> {});
-        verify(taskViewCallbacks).onTaskViewCreated(any());
-        verify(taskViewCallbacks).onTaskViewReady();
-        ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
-                WindowContainerTransaction.class);
-        verify(mSyncQueue, atLeastOnce()).queue(wctCaptor.capture());
-        List<WindowContainerTransaction> wcts = wctCaptor.getAllValues();
-        assertWithMessage("There must be a WindowContainerTransaction to set the"
-                + " root task as the launch root.")
-                .that(wcts.stream()
-                        .flatMap(wct -> wct.getHierarchyOps().stream())
-                        .map(WindowContainerTransaction.HierarchyOp::getType)
-                        .anyMatch(type -> type == HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT))
-                .isTrue();
-    }
-
-    @Test
-    public void testLaunchRootTaskView_onBackPressed_removesTopTask() throws Exception {
-        IBinder task1Token = new Binder();
-        IBinder task2Token = new Binder();
-        IBinder task3Token = new Binder();
-        ActivityManager.RunningTaskInfo task1 = createMultiWindowTask(1, task1Token).getTaskInfo();
-        ActivityManager.RunningTaskInfo task2 = createMultiWindowTask(2, task2Token).getTaskInfo();
-        ActivityManager.RunningTaskInfo task3 = createMultiWindowTask(3, task3Token).getTaskInfo();
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        runOnMainAndWait(() -> {});
-        // Set up a LaunchRootTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        ActivityManager.RunningTaskInfo launchRootTask =
-                setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 100);
-        runOnMainAndWait(() -> {});
-        // Trigger a taskAppeared on the launch root task to mimic the task appearance.
-        rootTaskListener.get().onTaskAppeared(task1, mLeash);
-        rootTaskListener.get().onTaskAppeared(task2, mLeash);
-        rootTaskListener.get().onTaskAppeared(task3, mLeash);
-        rootTaskListener.get().onTaskInfoChanged(task1);
-        // The resultant stack top to bottom is task1, task3, task2
-        runOnMainAndWait(() -> {});
-
-        // Act
-        // Press back button 3 times, trigger corresponding task vanishing as well. In real
-        // scenario, removeTask() will trigger onTaskVanished.
-        rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask);
-        rootTaskListener.get().onTaskVanished(task1);
-        rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask);
-        rootTaskListener.get().onTaskVanished(task3);
-        rootTaskListener.get().onBackPressedOnTaskRoot(launchRootTask);
-
-        // Assert
-        ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
-                WindowContainerTransaction.class);
-        verify(mSyncQueue, atLeastOnce()).queue(wctCaptor.capture());
-        List<WindowContainerTransaction> wcts = wctCaptor.getAllValues();
-        List<WindowContainerTransaction.HierarchyOp> removeTaskOps =
-                wcts.stream().flatMap(wct -> wct.getHierarchyOps().stream())
-                        .filter(op -> op.getType() == HIERARCHY_OP_TYPE_REMOVE_TASK)
-                        .collect(Collectors.toList());
-        assertWithMessage("There must be a WindowContainerTransaction to remove"
-                + " 2 of the 3 tasks.")
-                .that(removeTaskOps.size())
-                .isEqualTo(2);
-        assertThat(removeTaskOps.get(0).getContainer()).isEqualTo(task1Token);
-        assertThat(removeTaskOps.get(1).getContainer()).isEqualTo(task3Token);
-        assertThat(taskViewManager.getRootTaskCount()).isEqualTo(1);
-        assertThat(taskViewManager.getTopTaskInLaunchRootTask().taskId).isEqualTo(2);
-    }
-
-    @Test
-    public void testCreateSemiControlledTaskView() throws Exception {
-        SemiControlledCarTaskViewCallbacks taskViewCallbacks =
-                mock(SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-
-        taskViewManager.createSemiControlledTaskView(
-                mActivity.getMainExecutor(),
-                List.of(),
-                taskViewCallbacks
-        );
-        runOnMainAndWait(() -> {});
-        TaskView taskView = taskViewManager.getSemiControlledTaskViews().get(0);
-        taskView.surfaceCreated(mock(SurfaceHolder.class));
-
-        runOnMainAndWait(() -> {});
-        verify(taskViewCallbacks).onTaskViewCreated(any());
-        verify(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY),
-                eq(WINDOWING_MODE_MULTI_WINDOW),
-                any(ShellTaskOrganizer.TaskListener.class),
-                /* removeWithTaskOrganizer= */ eq(true));
-    }
-
-    @Test
-    public void testSemiControlledTaskView_callsOnReady() throws Exception {
-        List<ComponentName> persistentActivities =
-                List.of(ComponentName.unflattenFromString("com.example/.MainActivity"));
-        SemiControlledCarTaskViewCallbacks mockCallbacks = mock(
-                SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-
-        // Act
-        setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, persistentActivities, mockCallbacks);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        verify(mockCallbacks).onTaskViewReady();
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(persistentActivities),
-                any());
-    }
-
-    @Test
-    public void testTaskAppeared_semiControlledTaskView_topTaskUpdated() throws Exception {
-        SemiControlledCarTaskViewCallbacks mockCallbacks = mock(
-                SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, List.of(), mockCallbacks);
-        ActivityManager.RunningTaskInfo childTaskInfo =
-                createMultiWindowTask(2).getTaskInfo();
-
-        // Act
-        rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo);
-    }
-
-    @Test
-    public void testAddAllowListedActivities() throws Exception {
-        ComponentName componentName1 = ComponentName.unflattenFromString("com.example/.Activity1");
-        ComponentName componentName2 = ComponentName.unflattenFromString("com.example/.Activity2");
-        List<ComponentName> persistentActivities =
-                List.of(componentName1);
-        SemiControlledCarTaskViewCallbacks mockCallbacks = mock(
-                SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, persistentActivities, mockCallbacks);
-        runOnMainAndWait(() -> {});
-
-        // Action
-        List<ComponentName> activities = new ArrayList<>(persistentActivities);
-        activities.add(componentName2);
-        taskViewManager.addAllowListedActivities(taskView, activities);
-
-        // Assert
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(List.of(componentName2)),
-                any());
-    }
-
-    @Test
-    public void testRemoveAllowListedActivities() throws Exception {
-        List<ComponentName> activities =
-                List.of(ComponentName.unflattenFromString("com.example/.MainActivity"));
-        SemiControlledCarTaskViewCallbacks mockCallbacks = mock(
-                SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, activities, mockCallbacks);
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities), any());
-
-        // Action
-        taskViewManager.removeAllowListedActivities(taskView, activities);
-
-        // Assert
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities), eq(null));
-        assertThat(taskView.getPersistentActivities().size()).isEqualTo(0);
-    }
-
-    @Test
-    public void testSetAllowListedActivities() throws Exception {
-        ComponentName componentName1 = ComponentName.unflattenFromString("com.example/.Activity1");
-        ComponentName componentName2 = ComponentName.unflattenFromString("com.example/.Activity2");
-        ComponentName componentName3 = ComponentName.unflattenFromString("com.example/.Activity3");
-        List<ComponentName> activities1 =
-                List.of(componentName1, componentName2);
-        List<ComponentName> activities2 =
-                List.of(componentName2, componentName3);
-
-        SemiControlledCarTaskViewCallbacks mockCallbacks = mock(
-                SemiControlledCarTaskViewCallbacks.class);
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {
-        });
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, activities1, mockCallbacks);
-        runOnMainAndWait(() -> {
-        });
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities1), notNull());
-        assertThat(taskView.getPersistentActivities()).isEqualTo(activities1);
-
-        // Action
-        taskViewManager.setAllowListedActivities(taskView, activities2);
-        runOnMainAndWait(() -> {
-        });
-
-        // Assert
-        verify(mCarActivityManager).setPersistentActivitiesOnRootTask(eq(activities1), eq(null));
-        verify(mCarActivityManager, atLeastOnce()).setPersistentActivitiesOnRootTask(
-                eq(activities2), notNull());
-        assertThat(taskView.getPersistentActivities()).isEqualTo(activities2);
-    }
-
-    @Test
-    public void testTaskInfoChanged_semiControlledTaskView_topTaskUpdated() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, List.of(), mock(
-                        SemiControlledCarTaskViewCallbacks.class));
-        ActivityManager.RunningTaskInfo childTaskInfo =
-                createMultiWindowTask(1).getTaskInfo();
-        rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash);
-        runOnMainAndWait(() -> {});
-        ActivityManager.RunningTaskInfo childTaskInfo2 =
-                createMultiWindowTask(2).getTaskInfo();
-        rootTaskListener.get().onTaskAppeared(childTaskInfo2, mLeash);
-        runOnMainAndWait(() -> {});
-
-        // Act
-        rootTaskListener.get().onTaskInfoChanged(childTaskInfo);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo);
-    }
-
-    @Test
-    public void testTaskVanished_semiControlledTaskView_topTaskUpdated() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a SemiControlledCarTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        SemiControlledCarTaskView taskView = setUpSemiControlledTaskView(taskViewManager,
-                rootTaskListener, /* rootTaskId = */ 1, List.of(),
-                mock(SemiControlledCarTaskViewCallbacks.class));
-        ActivityManager.RunningTaskInfo childTaskInfo =
-                createMultiWindowTask(1).getTaskInfo();
-        rootTaskListener.get().onTaskAppeared(childTaskInfo, mLeash);
-        runOnMainAndWait(() -> {});
-        ActivityManager.RunningTaskInfo childTaskInfo2 =
-                createMultiWindowTask(2).getTaskInfo();
-        rootTaskListener.get().onTaskAppeared(childTaskInfo2, mLeash);
-        runOnMainAndWait(() -> {});
-
-        // Act
-        rootTaskListener.get().onTaskVanished(childTaskInfo2);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        assertThat(taskView.getTopTaskInTheRootTask()).isEqualTo(childTaskInfo);
-    }
-
-
-    private ActivityManager.RunningTaskInfo setUpLaunchRootTaskView(TaskViewManager taskViewManager,
-            AtomicReference<ShellTaskOrganizer.TaskListener> listener,
-            int rootTaskId) throws Exception {
-        ActivityManager.RunningTaskInfo launchRootTaskInfo =
-                createMultiWindowTask(rootTaskId).getTaskInfo();
-        doAnswer(invocation -> {
-            listener.set(invocation.getArgument(2));
-            listener.get().onTaskAppeared(launchRootTaskInfo, mLeash);
-            return null;
-        }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY),
-                eq(WINDOWING_MODE_MULTI_WINDOW),
-                any(ShellTaskOrganizer.TaskListener.class),
-                /* removeWithTaskOrganizer= */ eq(true));
-        taskViewManager.createLaunchRootTaskView(
-                mActivity.getMainExecutor(),
-                mock(LaunchRootCarTaskViewCallbacks.class)
-        );
-        runOnMainAndWait(() -> {});
-        LaunchRootCarTaskView launchRootCarTaskView = taskViewManager.getLaunchRootCarTaskView();
-        launchRootCarTaskView.surfaceCreated(mock(SurfaceHolder.class));
-        runOnMainAndWait(() -> {});
-        return launchRootTaskInfo;
-    }
-
-    private SemiControlledCarTaskView setUpSemiControlledTaskView(
-            TaskViewManager taskViewManager,
-            AtomicReference<ShellTaskOrganizer.TaskListener> listener, int rootTaskId,
-            List<ComponentName> persistentActivities,
-            SemiControlledCarTaskViewCallbacks callbacks)
-            throws Exception {
-        IBinder taskToken = new Binder();
-        ActivityManager.RunningTaskInfo rootTaskInfo =
-                createMultiWindowTask(rootTaskId, taskToken).getTaskInfo();
-        doAnswer(invocation -> {
-            listener.set(invocation.getArgument(2));
-            listener.get().onTaskAppeared(rootTaskInfo, mLeash);
-            return null;
-        }).when(mOrganizer).createRootTask(eq(DEFAULT_DISPLAY),
-                eq(WINDOWING_MODE_MULTI_WINDOW),
-                any(ShellTaskOrganizer.TaskListener.class),
-                /* removeWithTaskOrganizer= */ eq(true));
-        taskViewManager.createSemiControlledTaskView(
-                mActivity.getMainExecutor(),
-                persistentActivities,
-                callbacks
-        );
-        runOnMainAndWait(() -> {});
-        SemiControlledCarTaskView semiControlledCarTaskView =
-                taskViewManager.getSemiControlledTaskViews().get(0);
-        semiControlledCarTaskView.surfaceCreated(mock(SurfaceHolder.class));
-        runOnMainAndWait(() -> {});
-        return semiControlledCarTaskView;
-    }
-
-    @Test
-    public void testInit_registersTaskMonitor() throws Exception {
-        createTaskViewManager();
-        runOnMainAndWait(() -> {});
-
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        verify(mCarActivityManager).registerTaskMonitor();
-    }
-
-    @Test
-    public void testTaskAppeared_launchRootTaskView_updatesCarActivityManager() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a LaunchRootTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-
-        // Act
-        rootTaskListener.get().onTaskAppeared(taskInfo, mLeash);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        verify(mCarActivityManager).onTaskAppeared(taskInfo);
-    }
-
-    @Test
-    public void testTaskInfoChanged_launchRootTaskView_updatesCarActivityManager()
-            throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a LaunchRootTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-
-        // Act
-        rootTaskListener.get().onTaskInfoChanged(taskInfo);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        verify(mCarActivityManager).onTaskInfoChanged(taskInfo);
-    }
-
-    @Test
-    public void testTaskVanished_launchRootTaskView_updatesCarActivityManager() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Set up a LaunchRootTaskView
-        AtomicReference<ShellTaskOrganizer.TaskListener> rootTaskListener = new AtomicReference<>();
-        setUpLaunchRootTaskView(taskViewManager, rootTaskListener, /* rootTaskId = */ 1);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-
-        // Act
-        rootTaskListener.get().onTaskVanished(taskInfo);
-        runOnMainAndWait(() -> {});
-
-        // Assert
-        verify(mCarActivityManager).onTaskVanished(taskInfo);
-    }
-
-    @Test
-    public void testHostActivityDestroyed_releasesAllTaskViews() throws Exception {
-        testReleaseAllTaskViews(() -> {
-            ActivityScenario<TestActivity> scenario = mActivityRule.getScenario();
-            scenario.moveToState(Lifecycle.State.DESTROYED);
-        });
-    }
-
-    private void setUpControlledTaskView(TaskViewManager taskViewManager, Intent activityIntent,
-            Set<String> packagesThatCanRestart) throws Exception {
-        ControlledCarTaskViewCallbacks controlledCarTaskViewCallbacks = mock(
-                ControlledCarTaskViewCallbacks.class);
-        when(controlledCarTaskViewCallbacks.getDependingPackageNames())
-                .thenReturn(packagesThatCanRestart);
-        taskViewManager.createControlledCarTaskView(
-                mActivity.getMainExecutor(),
-                ControlledCarTaskViewConfig.builder()
-                        .setActivityIntent(activityIntent)
-                        .setAutoRestartOnCrash(false)
-                        .build(),
-                controlledCarTaskViewCallbacks
-        );
-
-        int lastIndex = Math.min(0, taskViewManager.getControlledTaskViews().size() - 1);
-        ControlledCarTaskView taskView = spy(taskViewManager.getControlledTaskViews()
-                .get(lastIndex));
-        doNothing().when(taskView).startActivity();
-
-        taskView.surfaceCreated(mock(SurfaceHolder.class));
-        runOnMainAndWait(() -> {});
-    }
-
-    @Test
-    public void testRestartControlledTask_whenHostActivityFocussed() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        // Send onTaskVanished to mimic the task removal behavior.
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package2"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-        controlledCarTaskView.dispatchTaskVanished(taskInfo);
-        assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        mTaskStackListenerArgumentCaptor.getValue().onTaskFocusChanged(mActivity.getTaskId(),
-                /* focused = */ true);
-
-        // Assert
-        verify(spiedTaskView).startActivity();
-    }
-
-    @Test
-    public void testControlledTaskNotRestarted_ifAlreadyRunning_whenHostActivityFocussed()
-            throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package2"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        mTaskStackListenerArgumentCaptor.getValue().onTaskFocusChanged(mActivity.getTaskId(),
-                /* focused = */ true);
-
-        // Assert
-        verify(spiedTaskView, times(0)).startActivity();
-    }
-
-    @Test
-    public void testRestartControlledTask_whenHostActivityRestarted() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        // Send onTaskVanished to mimic the task removal behavior.
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package2"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-        controlledCarTaskView.dispatchTaskVanished(taskInfo);
-        assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        mTaskStackListenerArgumentCaptor.getValue().onActivityRestartAttempt(
-                createMultiWindowTask(mActivity.getTaskId()).getTaskInfo(),
-                /* homeTaskVisible = */ true, false,
-                /* focused = */ true);
-
-        // Assert
-        verify(spiedTaskView).startActivity();
-    }
-
-    @Test
-    public void testRestartControlledTask_whenPackageThatCanRestartChanged() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        // Send onTaskVanished to mimic the task removal behavior.
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.relevant.package"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-        controlledCarTaskView.dispatchTaskVanished(taskInfo);
-        assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        taskViewManager.getPackageBroadcastReceiver().onReceive(mActivity,
-                new Intent().setData(Uri.parse("package:com.relevant.package")));
-
-        // Assert
-        verify(spiedTaskView).startActivity();
-    }
-
-    @Test
-    public void testControlledTaskNotRestarted_whenARandomPackageChanged() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        // Send onTaskVanished to mimic the task removal behavior.
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.relevant.package"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-        controlledCarTaskView.dispatchTaskVanished(taskInfo);
-        assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        taskViewManager.getPackageBroadcastReceiver().onReceive(mActivity,
-                new Intent().setData(Uri.parse("package:com.random.package")));
-
-        // Assert
-        verify(spiedTaskView, times(0)).startActivity();
-    }
-
-    // User switch related tests.
-
-    @Test
-    public void testRestartControlledTask_onUserUnlocked() throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-
-        // Send onTaskVanished to mimic the task removal behavior.
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package2"));
-        ControlledCarTaskView controlledCarTaskView =
-                taskViewManager.getControlledTaskViews().get(0);
-        ActivityManager.RunningTaskInfo taskInfo = createMultiWindowTask(2).getTaskInfo();
-        controlledCarTaskView.dispatchTaskAppeared(taskInfo, mLeash);
-        controlledCarTaskView.dispatchTaskVanished(taskInfo);
-        assertThat(controlledCarTaskView.getTaskId()).isEqualTo(INVALID_TASK_ID);
-
-        // Stub the taskview with a spy to assert on startActivity.
-        ControlledCarTaskView spiedTaskView = spy(controlledCarTaskView);
-        doNothing().when(spiedTaskView).startActivity();
-        taskViewManager.getControlledTaskViews().set(0, spiedTaskView);
-
-        // Act
-        mUserLifecycleListenerArgumentCaptor.getValue().onEvent(
-                new CarUserManager.UserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED,
-                        mActivity.getUserId()));
-
-        // Assert
-        verify(spiedTaskView).startActivity();
-    }
-
-    @Test
-    public void testUserSwitch_releasesAllTaskViews() throws Exception {
-        testReleaseAllTaskViews(/* actionBlock= */ () ->
-                mUserLifecycleListenerArgumentCaptor.getValue().onEvent(
-                        new CarUserManager.UserLifecycleEvent(USER_LIFECYCLE_EVENT_TYPE_SWITCHING,
-                                /* from= */ mActivity.getUserId(), /* to= */ 20))
-        );
-    }
-
-    private void testReleaseAllTaskViews(Runnable actionBlock) throws Exception {
-        TaskViewManager taskViewManager = createTaskViewManager();
-        runOnMainAndWait(() -> {});
-        mCarServiceLifecycleListener.onLifecycleChanged(mCar, true);
-        // Create a few TaskViews
-        AtomicReference<ShellTaskOrganizer.TaskListener> listener = new AtomicReference<>();
-        setUpLaunchRootTaskView(taskViewManager, listener, /* rootTaskId = */ 1);
-        LaunchRootCarTaskView launchRootCarTaskView = taskViewManager.getLaunchRootCarTaskView();
-
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package"));
-        ControlledCarTaskView controlledCarTaskView = taskViewManager.getControlledTaskViews()
-                .get(0);
-        setUpControlledTaskView(taskViewManager, new Intent("ACTION_VIEW"),
-                ImmutableSet.of("com.random.package2"));
-        ControlledCarTaskView controlledCarTaskView2 = taskViewManager.getControlledTaskViews()
-                .get(1);
-
-        SemiControlledCarTaskViewCallbacks taskViewCallbacks =
-                mock(SemiControlledCarTaskViewCallbacks.class);
-        taskViewManager.createSemiControlledTaskView(
-                mActivity.getMainExecutor(),
-                List.of(),
-                taskViewCallbacks
-        );
-        runOnMainAndWait(() -> {});
-        taskViewManager.createSemiControlledTaskView(
-                mActivity.getMainExecutor(),
-                List.of(),
-                taskViewCallbacks
-        );
-        runOnMainAndWait(() -> {});
-        // Trigger surfaceCreated on SemiControlledTaskView.
-        SemiControlledCarTaskView semiControlledCarTaskView =
-                taskViewManager.getSemiControlledTaskViews().get(0);
-        semiControlledCarTaskView.surfaceCreated(mock(SurfaceHolder.class));
-        runOnMainAndWait(() -> {});
-        SemiControlledCarTaskView semiControlledCarTaskView2 =
-                taskViewManager.getSemiControlledTaskViews().get(1);
-        semiControlledCarTaskView2.surfaceCreated(mock(SurfaceHolder.class));
-        runOnMainAndWait(() -> {});
-
-        // Act
-        actionBlock.run();
-
-        // Assert
-        assertThat(launchRootCarTaskView.isInitialized()).isFalse();
-        assertThat(controlledCarTaskView.isInitialized()).isFalse();
-        assertThat(controlledCarTaskView2.isInitialized()).isFalse();
-        assertThat(semiControlledCarTaskView.isInitialized()).isFalse();
-        assertThat(semiControlledCarTaskView2.isInitialized()).isFalse();
-
-        assertThat(taskViewManager.getSemiControlledTaskViews()).isEmpty();
-        assertThat(taskViewManager.getLaunchRootCarTaskView()).isNull();
-        assertThat(taskViewManager.getControlledTaskViews()).isEmpty();
-
-        verify(mOrganizer).unregisterOrganizer();
-        verify(mTaskViewInputInterceptor).release();
-    }
-
-    private TaskViewManager createTaskViewManager() {
-        // InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
-        // http://b/25897652.
-        Looper looper = Looper.myLooper();
-        if (looper == null) {
-            Looper.prepare();
-        }
-
-        TaskViewManager taskViewManager =  new TaskViewManager(mActivity, mShellExecutor,
-                mOrganizer, mSyncQueue, mTransitions, new ShellInit(mShellExecutor),
-                mShellController, mStartingWindowController);
-        taskViewManager.setTaskViewInputInterceptor(mTaskViewInputInterceptor);
-        return taskViewManager;
-    }
-
-    private void runOnMainAndWait(Runnable r) throws Exception {
-        mActivity.getMainExecutor().execute(() -> {
-            r.run();
-            mIdleHandlerLatch.countDown();
-            mIdleHandlerLatch = new CountDownLatch(1);
-        });
-        mIdleHandlerLatch.await(5, TimeUnit.SECONDS);
-    }
-
-    public static class TestActivity extends Activity {
-        private static final int FINISH_TIMEOUT_MS = 1000;
-        private final CountDownLatch mDestroyed = new CountDownLatch(1);
-
-        @Override
-        protected void onDestroy() {
-            super.onDestroy();
-            mDestroyed.countDown();
-        }
-
-        void finishCompletely() throws InterruptedException {
-            finish();
-            mDestroyed.await(FINISH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
-        }
-    }
-}
diff --git a/app/tests/src/com/android/car/carlauncher/calmmode/CalmModeFragmentTest.java b/app/tests/src/com/android/car/carlauncher/calmmode/CalmModeFragmentTest.java
index 6345cc0..d092135 100644
--- a/app/tests/src/com/android/car/carlauncher/calmmode/CalmModeFragmentTest.java
+++ b/app/tests/src/com/android/car/carlauncher/calmmode/CalmModeFragmentTest.java
@@ -21,29 +21,44 @@
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static org.hamcrest.Matchers.not;
 import static org.junit.Assert.*;
+import static org.mockito.Mockito.when;
 
 import android.app.Activity;
 
 import androidx.fragment.app.testing.FragmentScenario;
+import androidx.test.espresso.matcher.ViewMatchers;
 
 import com.android.car.carlauncher.R;
+import com.android.car.media.common.MediaItemMetadata;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 public class CalmModeFragmentTest {
 
     private FragmentScenario<CalmModeFragment> mFragmentScenario;
     private CalmModeFragment mCalmModeFragment;
     private Activity mActivity;
+    @Mock
+    private MediaItemMetadata testMediaItem;
+
+    private static final CharSequence TEST_MEDIA_TITLE = "Title";
+    private static final CharSequence TEST_MEDIA_SUB_TITLE = "Sub Title";
 
     @Before
     public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(testMediaItem.getTitle()).thenReturn(TEST_MEDIA_TITLE);
+        when(testMediaItem.getSubtitle()).thenReturn(TEST_MEDIA_SUB_TITLE);
         mFragmentScenario =
                 FragmentScenario.launchInContainer(
                         CalmModeFragment.class, null, R.style.Theme_CalmMode);
@@ -68,36 +83,62 @@
     }
 
     @Test
-    public void fragmentResumed_testClock_isVisible() {
+    public void fragmentResumed_testClock_isInvisible() {
         mFragmentScenario.moveToState(RESUMED);
 
-        onView(withId(R.id.clock)).check(matches(isDisplayed()));
+        onView(withId(R.id.clock)).check(matches(withEffectiveVisibility(
+                ViewMatchers.Visibility.INVISIBLE)));
     }
 
     @Test
-    public void fragmentResumed_testDate_isVisible() {
+    public void fragmentResumed_testDate_isInvisible() {
         mFragmentScenario.moveToState(RESUMED);
 
-        onView(withId(R.id.date)).check(matches(isDisplayed()));
+        onView(withId(R.id.date)).check(matches(withEffectiveVisibility(
+                ViewMatchers.Visibility.INVISIBLE)));
     }
 
     @Test
-    public void fragmentResumed_testMedia_isVisible() {
-        String testMediaTitle = "Test media title";
+    public void fragmentResumed_testMedia_isInvisible() {
         mFragmentScenario.moveToState(RESUMED);
 
-        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(testMediaTitle));
+        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(testMediaItem));
 
-        onView(withId(R.id.media_title)).check(matches(isDisplayed()));
+        onView(withId(R.id.media_title)).check(matches(withEffectiveVisibility(
+                ViewMatchers.Visibility.INVISIBLE)));
+        String expectedText =
+                TEST_MEDIA_TITLE + "   •   " + TEST_MEDIA_SUB_TITLE;
+        onView(withId(R.id.media_title)).check(matches(withText(expectedText)));
+    }
+
+    @Test
+    public void fragmentResumed_testMediaItemNull_isGone() {
+        mFragmentScenario.moveToState(RESUMED);
+
+        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(null));
+
+        onView(withId(R.id.media_title)).check(matches(not(isDisplayed())));
     }
 
     @Test
     public void fragmentResumed_testMediaTitleNull_isGone() {
-        String testMediaTitle = null;
+        when(testMediaItem.getTitle()).thenReturn(null);
         mFragmentScenario.moveToState(RESUMED);
 
-        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(testMediaTitle));
+        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(testMediaItem));
 
         onView(withId(R.id.media_title)).check(matches(not(isDisplayed())));
     }
+
+    @Test
+    public void fragmentResumed_testMediaSubTitleNull_isVisible() {
+        when(testMediaItem.getSubtitle()).thenReturn(null);
+        mFragmentScenario.moveToState(RESUMED);
+
+        mActivity.runOnUiThread(()->mCalmModeFragment.updateMediaTitle(testMediaItem));
+
+        onView(withId(R.id.media_title)).check(matches(withEffectiveVisibility(
+                ViewMatchers.Visibility.INVISIBLE)));
+        onView(withId(R.id.media_title)).check(matches(withText(TEST_MEDIA_TITLE.toString())));
+    }
 }
diff --git a/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateDataTest.java b/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateDataTest.java
index b614cbe..d7e60bc 100644
--- a/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateDataTest.java
+++ b/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateDataTest.java
@@ -39,6 +39,7 @@
 @RunWith(AndroidJUnit4.class)
 public class NavigationStateDataTest extends AbstractExtendedMockitoTestCase {
 
+    private static final String SEPARATOR = "\\u0020\\u0020\\u0020\\u2022\\u0020\\u0020\\u0020";
     private static final String TIME_TO_DEST = "1hr 30min";
     private static final double DISTANCE_TO_DEST = 100;
     private static final String DISTANCE_TO_DEST_STR = "100";
@@ -85,14 +86,16 @@
 
     @Test
     public void testBuildTripStatusString_nullNavState_returnsNull() {
-        assertNull(NavigationStateData.buildTripStatusString(null, LOCALE_US));
+        assertNull(NavigationStateData.buildTripStatusString(null, LOCALE_US,
+                SEPARATOR));
     }
 
     @Test
     public void testBuildTripStatusString_nullTime_returnsNull() {
         when(mNavigationStateDataImperial.getTimeToDestination()).thenReturn(null);
         final String tripStatusActual =
-                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, null);
+                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, null,
+                        SEPARATOR);
         assertNull(tripStatusActual);
     }
 
@@ -100,25 +103,28 @@
     public void testBuildTripStatusString_nullDistanceUnit_returnsNull() {
         when(mNavigationStateDataImperial.getDistanceUnit()).thenReturn(null);
         final String tripStatusActual =
-                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, LOCALE_US);
+                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, LOCALE_US,
+                        SEPARATOR);
         assertNull(tripStatusActual);
     }
 
     @Test
     public void testBuildTripStatusString_navStateDataImperial_matchesForLocaleUS() {
         final String tripStatusExpected =
-                TIME_TO_DEST + "  " + DISTANCE_TO_DEST_STR + " " + DISTANCE_UNIT_IMPERIAL_STR;
+                TIME_TO_DEST + SEPARATOR + DISTANCE_TO_DEST_STR + " " + DISTANCE_UNIT_IMPERIAL_STR;
         final String tripStatusActual =
-                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, LOCALE_US);
+                NavigationStateData.buildTripStatusString(mNavigationStateDataImperial, LOCALE_US,
+                        SEPARATOR);
         assertEquals(tripStatusExpected, tripStatusActual);
     }
 
     @Test
     public void testBuildTripStatusString_navStateDataMetric_matchesForLocaleUS() {
         final String tripStatusExpected =
-                TIME_TO_DEST + "  " + DISTANCE_TO_DEST_STR + " " + DISTANCE_UNIT_METRIC_STR;
+                TIME_TO_DEST + SEPARATOR + DISTANCE_TO_DEST_STR + " " + DISTANCE_UNIT_METRIC_STR;
         final String tripStatusActual =
-                NavigationStateData.buildTripStatusString(mNavigationStateDataMetric, LOCALE_US);
+                NavigationStateData.buildTripStatusString(mNavigationStateDataMetric, LOCALE_US,
+                        SEPARATOR);
         assertEquals(tripStatusExpected, tripStatusActual);
     }
 }
diff --git a/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateViewModelTest.java b/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateViewModelTest.java
index efa1938..f9549d6 100644
--- a/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateViewModelTest.java
+++ b/app/tests/src/com/android/car/carlauncher/calmmode/NavigationStateViewModelTest.java
@@ -121,7 +121,7 @@
 
         NavigationState.NavigationStateProto navStateProto =
                 NavigationState.NavigationStateProto.newBuilder().build();
-        mNavigationStateViewModel.onNavigationState(navStateProto.toByteArray());
+        mNavigationStateViewModel.onNavigationStateChanged(navStateProto.toByteArray());
 
         NavigationStateData navStateData =
                 mNavigationStateViewModel.getNavigationState().getValue();
@@ -130,7 +130,7 @@
 
     @Test
     public void onNavigationState_protoExceptionWhileParsing_navStateIsSetToNull() {
-        mNavigationStateViewModel.onNavigationState(new byte[] {});
+        mNavigationStateViewModel.onNavigationStateChanged(new byte[] {});
         NavigationStateData navStateData =
                 mNavigationStateViewModel.getNavigationState().getValue();
         assertNull(navStateData);
@@ -143,7 +143,7 @@
 
         NavigationState.NavigationStateProto navStateProto =
                 buildNavStateProto(distanceToDest, NavigationState.Distance.Unit.MILES, timeToDest);
-        mNavigationStateViewModel.onNavigationState(navStateProto.toByteArray());
+        mNavigationStateViewModel.onNavigationStateChanged(navStateProto.toByteArray());
 
         NavigationStateData navStateData =
                 mNavigationStateViewModel.getNavigationState().getValue();
@@ -160,7 +160,7 @@
         NavigationState.NavigationStateProto navStateProto =
                 buildNavStateProto(
                         distanceToDest, NavigationState.Distance.Unit.KILOMETERS, timeToDest);
-        mNavigationStateViewModel.onNavigationState(navStateProto.toByteArray());
+        mNavigationStateViewModel.onNavigationStateChanged(navStateProto.toByteArray());
 
         NavigationStateData navStateData =
                 mNavigationStateViewModel.getNavigationState().getValue();
@@ -176,7 +176,7 @@
         final String distanceToDest = "1000";
         NavigationState.NavigationStateProto navStateProto =
                 buildNavStateProto(distanceToDest, NavigationState.Distance.Unit.FEET, timeToDest);
-        mNavigationStateViewModel.onNavigationState(navStateProto.toByteArray());
+        mNavigationStateViewModel.onNavigationStateChanged(navStateProto.toByteArray());
 
         NavigationStateData navStateData =
                 mNavigationStateViewModel.getNavigationState().getValue();
diff --git a/app/tests/src/com/android/car/carlauncher/calmmode/TemperatureDataTest.java b/app/tests/src/com/android/car/carlauncher/calmmode/TemperatureDataTest.java
index a2b4e6c..00d9fb8 100644
--- a/app/tests/src/com/android/car/carlauncher/calmmode/TemperatureDataTest.java
+++ b/app/tests/src/com/android/car/carlauncher/calmmode/TemperatureDataTest.java
@@ -161,7 +161,20 @@
         String tempCExpected = "25°C";
         TemperatureData tempC = new TemperatureData.Builder().setValueCelsius(tempCVal).build();
 
-        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US);
+        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US,
+                /* showUnit = */ true);
+
+        assertEquals(tempCExpected, tempCActual);
+    }
+
+    @Test
+    public void testBuildTemperatureString_positiveCelsiusDoNotShowUnit_matchesForLocaleUS() {
+        float tempCVal = 25.12345f;
+        String tempCExpected = "25°";
+        TemperatureData tempC = new TemperatureData.Builder().setValueCelsius(tempCVal).build();
+
+        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US,
+                /* showUnit = */ false);
 
         assertEquals(tempCExpected, tempCActual);
     }
@@ -172,7 +185,20 @@
         String tempFExpected = "77°F";
         TemperatureData tempF = new TemperatureData.Builder().setValueFahrenheit(tempFVal).build();
 
-        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US);
+        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US,
+                /* showUnit = */ true);
+
+        assertEquals(tempFExpected, tempFActual);
+    }
+
+    @Test
+    public void testBuildTemperatureString_positiveFahrenheitDoNotShowUnit_matchesForLocaleUS() {
+        float tempFVal = 77.2212f;
+        String tempFExpected = "77°";
+        TemperatureData tempF = new TemperatureData.Builder().setValueFahrenheit(tempFVal).build();
+
+        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US,
+                /* showUnit = */ false);
 
         assertEquals(tempFExpected, tempFActual);
     }
@@ -183,7 +209,20 @@
         String tempCExpected = "-20°C";
         TemperatureData tempC = new TemperatureData.Builder().setValueCelsius(tempCVal).build();
 
-        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US);
+        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US,
+                /* showUnit = */ true);
+
+        assertEquals(tempCExpected, tempCActual);
+    }
+
+    @Test
+    public void testBuildTemperatureString_negativeCelsiusDoNotShowUnit_matchesForLocaleUS() {
+        float tempCVal = -20f;
+        String tempCExpected = "-20°";
+        TemperatureData tempC = new TemperatureData.Builder().setValueCelsius(tempCVal).build();
+
+        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US,
+                /* showUnit = */ false);
 
         assertEquals(tempCExpected, tempCActual);
     }
@@ -194,7 +233,20 @@
         String tempFExpected = "-4°F";
         TemperatureData tempF = new TemperatureData.Builder().setValueFahrenheit(tempFVal).build();
 
-        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US);
+        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US,
+                /* showUnit = */ true);
+
+        assertEquals(tempFExpected, tempFActual);
+    }
+
+    @Test
+    public void testBuildTemperatureString_negativeFahrenheitDoNotShowUnit_matchesForLocaleUS() {
+        float tempFVal = -4f;
+        String tempFExpected = "-4°";
+        TemperatureData tempF = new TemperatureData.Builder().setValueFahrenheit(tempFVal).build();
+
+        String tempFActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US,
+                /* showUnit = */ false);
 
         assertEquals(tempFExpected, tempFActual);
     }
@@ -205,7 +257,8 @@
         String tempCExpected = "0°C";
         TemperatureData tempC = new TemperatureData.Builder().setValueCelsius(tempCVal).build();
 
-        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US);
+        String tempCActual = TemperatureData.buildTemperatureString(tempC, TEST_LOCALE_US,
+                /* showUnit = */ true);
 
         assertEquals(tempCExpected, tempCActual);
     }
@@ -216,7 +269,8 @@
         String tempFExpected = "0°C";
         TemperatureData tempF = new TemperatureData.Builder().setValueCelsius(tempFVal).build();
 
-        String tempCActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US);
+        String tempCActual = TemperatureData.buildTemperatureString(tempF, TEST_LOCALE_US,
+                /* showUnit = */ true);
 
         assertEquals(tempFExpected, tempCActual);
     }
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/HomeCardFragmentTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/HomeCardFragmentTest.java
index 0a1afc9..7866abe 100644
--- a/app/tests/src/com/android/car/carlauncher/homescreen/HomeCardFragmentTest.java
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/HomeCardFragmentTest.java
@@ -18,6 +18,7 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.RootMatchers.hasWindowLayoutParams;
 import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
@@ -31,25 +32,30 @@
 import static org.mockito.Mockito.when;
 
 import android.graphics.drawable.Drawable;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.WindowManager;
 import android.widget.ImageButton;
 
+import androidx.fragment.app.FragmentActivity;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.Suppress;
 import androidx.test.platform.app.InstrumentationRegistry;
 import androidx.test.rule.ActivityTestRule;
 
 import com.android.car.carlauncher.CarLauncher;
+import com.android.car.carlauncher.Flags;
 import com.android.car.carlauncher.R;
 import com.android.car.carlauncher.homescreen.ui.CardHeader;
 import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
 import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
 import com.android.car.carlauncher.homescreen.ui.TextBlockView;
 
+import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@Suppress // To be ignored until b/224978827 is fixed
 @RunWith(AndroidJUnit4.class)
 public class HomeCardFragmentTest {
 
@@ -71,90 +77,134 @@
     private static final TextBlockView TEXT_BLOCK_VIEW_NO_FOOTER = new TextBlockView(
             TEXT_BLOCK_CONTENT);
 
+    private FragmentActivity mActivity;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
     @Rule
     public ActivityTestRule<CarLauncher> mActivityTestRule = new ActivityTestRule<CarLauncher>(
             CarLauncher.class);
 
+    @Before
+    public void setUp() {
+        mActivity = mActivityTestRule.getActivity();
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            }
+        });
+    }
+
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void updateContentView_descriptiveTextWithFooter_displaysTapForMoreView() {
-        HomeCardFragment fragment = (HomeCardFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.top_card);
+        HomeCardFragment fragment = (HomeCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.top_card);
         fragment.updateHeaderView(CARD_HEADER);
         fragment.updateContentView(DESCRIPTIVE_TEXT_VIEW);
 
         onView(allOf(withId(R.id.descriptive_text_layout),
-                isDescendantOfA(withId(R.id.top_card)))).check(
-                matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.primary_text), withText(DESCRIPTIVE_TEXT_TITLE),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.secondary_text), withText(DESCRIPTIVE_TEXT_SUBTITLE),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.tap_for_more_text), withText(DESCRIPTIVE_TEXT_FOOTER),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void updateContentView_descriptiveTextWithNoFooter_hidesTapForMoreView() {
-        HomeCardFragment fragment = (HomeCardFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.top_card);
+        HomeCardFragment fragment = (HomeCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.top_card);
         fragment.updateHeaderView(CARD_HEADER);
         fragment.updateContentView(DESCRIPTIVE_TEXT_VIEW_NO_FOOTER);
 
         onView(allOf(withId(R.id.descriptive_text_layout),
-                isDescendantOfA(withId(R.id.top_card)))).check(
-                matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.primary_text), withText(DESCRIPTIVE_TEXT_TITLE),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.secondary_text), withText(DESCRIPTIVE_TEXT_SUBTITLE),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.tap_for_more_text),
                 isDescendantOfA(withId(R.id.descriptive_text_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(not(isDisplayed())));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void updateContentView_textBlockWithFooter_displaysTapForMoreView() {
-        HomeCardFragment fragment = (HomeCardFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.top_card);
+        HomeCardFragment fragment = (HomeCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.top_card);
         fragment.updateHeaderView(CARD_HEADER);
         fragment.updateContentView(TEXT_BLOCK_VIEW);
 
-        onView(allOf(withId(R.id.text_block_layout), isDescendantOfA(withId(R.id.top_card)))).check(
-                matches(isDisplayed()));
+        onView(allOf(withId(R.id.text_block_layout), isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.text_block), withText(TEXT_BLOCK_CONTENT),
                 isDescendantOfA(withId(R.id.text_block_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.tap_for_more_text), withText(TEXT_BLOCK_FOOTER),
                 isDescendantOfA(withId(R.id.text_block_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void updateContentView_textBlockNoFooter_hidesTapForMoreView() {
-        HomeCardFragment fragment = (HomeCardFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.top_card);
+        HomeCardFragment fragment = (HomeCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.top_card);
         fragment.updateHeaderView(CARD_HEADER);
         fragment.updateContentView(TEXT_BLOCK_VIEW_NO_FOOTER);
 
-        onView(allOf(withId(R.id.text_block_layout), isDescendantOfA(withId(R.id.top_card)))).check(
-                matches(isDisplayed()));
+        onView(allOf(withId(R.id.text_block_layout), isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.text_block), withText(TEXT_BLOCK_CONTENT),
                 isDescendantOfA(withId(R.id.text_block_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(isDisplayed()));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
         onView(allOf(withId(R.id.tap_for_more_text),
                 isDescendantOfA(withId(R.id.text_block_layout)),
-                isDescendantOfA(withId(R.id.top_card)))).check(matches(not(isDisplayed())));
+                isDescendantOfA(withId(R.id.top_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
     }
 
     @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
     public void updateControlBarButton_updatesButtonSelectedState() {
-        HomeCardFragment fragment = (HomeCardFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.top_card);
+        HomeCardFragment fragment = (HomeCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.top_card);
         assertNotNull(fragment);
 
         ImageButton leftImageButton = mock(ImageButton.class);
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragmentTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragmentTest.java
new file mode 100644
index 0000000..a89e2f9
--- /dev/null
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioCardFragmentTest.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.matcher.RootMatchers.hasWindowLayoutParams;
+
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.WindowManager;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.rule.ActivityTestRule;
+
+import com.android.car.apps.common.CrossfadeImageView;
+import com.android.car.carlauncher.CarLauncher;
+import com.android.car.carlauncher.Flags;
+import com.android.car.carlauncher.R;
+import com.android.car.carlauncher.homescreen.audio.dialer.DialerCardFragment;
+import com.android.car.carlauncher.homescreen.audio.media.MediaCardFragment;
+import com.android.car.carlauncher.homescreen.ui.CardContent.CardBackgroundImage;
+import com.android.car.carlauncher.homescreen.ui.CardHeader;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
+import com.android.car.carlauncher.homescreen.ui.TextBlockView;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class AudioCardFragmentTest {
+
+    private static final CardHeader CARD_HEADER = new CardHeader("Test App Name", null);
+    private static final BitmapDrawable BITMAP = new BitmapDrawable(
+            Bitmap.createBitmap(/* width = */100, /* height = */100, Bitmap.Config.ARGB_8888));
+    private static final CardBackgroundImage CARD_BACKGROUND_IMAGE = new CardBackgroundImage(
+            BITMAP, null);
+    private static final String AUDIO_VIEW_TITLE = "Test song title";
+    private static final String AUDIO_VIEW_SUBTITLE = "Test artist name";
+    private static final long AUDIO_START_TIME = 1L;
+    private static final DescriptiveTextView DESCRIPTIVE_TEXT_VIEW = new DescriptiveTextView(
+            /* image = */ null, "Primary Text", "Secondary Text");
+    private static final TextBlockView TEXT_BLOCK_VIEW = new TextBlockView("Text");
+
+    private final DescriptiveTextWithControlsView
+            mDescriptiveTextWithControlsView = new DescriptiveTextWithControlsView(
+            CARD_BACKGROUND_IMAGE,
+            AUDIO_VIEW_TITLE,
+            AUDIO_VIEW_SUBTITLE);
+    private final DescriptiveTextWithControlsView.Control mControl =
+            new DescriptiveTextWithControlsView.Control(BITMAP, v -> {
+            });
+    private final DescriptiveTextWithControlsView
+            mDescriptiveTextWithControlsViewWithButtons = new DescriptiveTextWithControlsView(
+            CARD_BACKGROUND_IMAGE, AUDIO_VIEW_TITLE, AUDIO_VIEW_SUBTITLE, AUDIO_START_TIME,
+            mControl, mControl, mControl);
+
+    private FragmentActivity mActivity;
+
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule =
+            DeviceFlagsValueProvider.createCheckFlagsRule();
+    @Rule
+    public ActivityTestRule<CarLauncher> mActivityTestRule =
+            new ActivityTestRule<CarLauncher>(CarLauncher.class);
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityTestRule.getActivity();
+        mActivity.runOnUiThread(new Runnable() {
+            public void run() {
+                mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
+            }
+        });
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void updateContentAndHeaderView_noControls_showsMediaPlaybackControlsBar_hidesDialer() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showMediaCard);
+        MediaCardFragment mediaCardFragment = (MediaCardFragment) fragment.getMediaFragment();
+
+        mediaCardFragment.updateHeaderView(CARD_HEADER);
+        mediaCardFragment.updateContentView(mDescriptiveTextWithControlsView);
+
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.primary_text), withText(AUDIO_VIEW_TITLE),
+                isDescendantOfA(withId(R.id.media_layout)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.secondary_text), withText(AUDIO_VIEW_SUBTITLE),
+                isDescendantOfA(withId(R.id.media_layout)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.optional_timer),
+                isDescendantOfA(withId(R.id.media_layout)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_playback_controls_bar),
+                isDescendantOfA(withId(R.id.media_layout)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.motion_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void showMediaCard_showsFullscreenMediaCardLayout_hidesDialerLayout() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showMediaCard);
+        MediaCardFragment mediaCardFragment = (MediaCardFragment) fragment.getMediaFragment();
+
+        onView(allOf(withId(R.id.motion_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void updateContentAndHeaderView_showsDialerControlBarControls_hidesMediaCardControls() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showInCallCard);
+        DialerCardFragment dialerCardFragment = (DialerCardFragment) fragment.getInCallFragment();
+
+        dialerCardFragment.updateHeaderView(CARD_HEADER);
+        dialerCardFragment.updateContentView(mDescriptiveTextWithControlsViewWithButtons);
+
+        onView(allOf(withId(R.id.optional_timer),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_left),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_center),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_right),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.bottom_card)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.bottom_card)),
+                isDescendantOfA(withId(R.id.media_fragment_container))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.bottom_card)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.bottom_card)),
+                isDescendantOfA(withId(R.id.media_fragment_container))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void updateContentAndHeaderView_audioContentWithControls_showsDialer_notMediaCard() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showInCallCard);
+        DialerCardFragment dialerCardFragment = (DialerCardFragment) fragment.getInCallFragment();
+
+        dialerCardFragment.updateHeaderView(CARD_HEADER);
+        dialerCardFragment.updateContentView(mDescriptiveTextWithControlsViewWithButtons);
+
+        onView(allOf(withId(R.id.optional_timer),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_left),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_center),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.button_right),
+                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_view),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(doesNotExist());
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void mediaFragment_updateContentView_descriptiveText_hidesPlaybackControlsBar() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        MediaCardFragment mediaCardFragment = (MediaCardFragment) fragment.getMediaFragment();
+        mediaCardFragment.updateContentView(mDescriptiveTextWithControlsView);
+        mediaCardFragment.updateContentView(DESCRIPTIVE_TEXT_VIEW);
+
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.descriptive_text_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void mediaFragment_updateContentView_textBlock_hidesPlaybackControlsBar() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        MediaCardFragment mediaCardFragment = (MediaCardFragment) fragment.getMediaFragment();
+        mediaCardFragment.updateContentView(mDescriptiveTextWithControlsView);
+        mediaCardFragment.updateContentView(TEXT_BLOCK_VIEW);
+
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.text_block_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.media_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(Flags.FLAG_MEDIA_CARD_FULLSCREEN)
+    public void dialerFragment_updateContentView_descriptiveText_hidesDescriptiveControlsView() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showInCallCard);
+        DialerCardFragment dialerCardFragment = (DialerCardFragment) fragment.getInCallFragment();
+        dialerCardFragment.updateContentView(mDescriptiveTextWithControlsViewWithButtons);
+        dialerCardFragment.updateContentView(DESCRIPTIVE_TEXT_VIEW);
+
+        // card_background is displayed since the onRootLayoutChangeListener sets it visible
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+
+    @Test
+    public void dialerFragment_updateContentView_textBlock_hidesDescriptiveControlsView() {
+        AudioCardFragment fragment = (AudioCardFragment) mActivity.getSupportFragmentManager()
+                .findFragmentById(R.id.bottom_card);
+        mActivity.runOnUiThread(fragment::showInCallCard);
+        DialerCardFragment dialerCardFragment = (DialerCardFragment) fragment.getInCallFragment();
+        dialerCardFragment.updateContentView(mDescriptiveTextWithControlsViewWithButtons);
+        dialerCardFragment.updateContentView(TEXT_BLOCK_VIEW);
+
+        // card_background is displayed since the onRootLayoutChangeListener sets it visible
+        onView(allOf(withId(R.id.card_background),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.text_block_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(isDisplayed()));
+        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+        onView(allOf(withId(R.id.media_layout),
+                isDescendantOfA(withId(R.id.in_call_fragment_container)),
+                isDescendantOfA(withId(R.id.bottom_card))))
+                .inRoot(hasWindowLayoutParams())
+                .check(matches(not(isDisplayed())));
+    }
+}
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioFragmentTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioFragmentTest.java
deleted file mode 100644
index 1f0b5fb..0000000
--- a/app/tests/src/com/android/car/carlauncher/homescreen/audio/AudioFragmentTest.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2020 Google Inc.
- *
- * 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.carlauncher.homescreen.audio;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-import static androidx.test.espresso.matcher.ViewMatchers.withText;
-
-import static org.hamcrest.CoreMatchers.allOf;
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.CoreMatchers.not;
-
-import android.graphics.Bitmap;
-import android.graphics.drawable.BitmapDrawable;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.Suppress;
-import androidx.test.rule.ActivityTestRule;
-
-import com.android.car.apps.common.CrossfadeImageView;
-import com.android.car.carlauncher.CarLauncher;
-import com.android.car.carlauncher.R;
-import com.android.car.carlauncher.homescreen.ui.CardContent.CardBackgroundImage;
-import com.android.car.carlauncher.homescreen.ui.CardHeader;
-import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
-import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
-import com.android.car.carlauncher.homescreen.ui.TextBlockView;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@Suppress // To be ignored until b/224978827 is fixed
-@RunWith(AndroidJUnit4.class)
-public class AudioFragmentTest {
-
-    private static final CardHeader CARD_HEADER = new CardHeader("Test App Name", null);
-    private static final BitmapDrawable BITMAP = new BitmapDrawable(
-            Bitmap.createBitmap(/* width = */100, /* height = */100, Bitmap.Config.ARGB_8888));
-    private static final CardBackgroundImage CARD_BACKGROUND_IMAGE = new CardBackgroundImage(
-            BITMAP, null);
-    private static final String AUDIO_VIEW_TITLE = "Test song title";
-    private static final String AUDIO_VIEW_SUBTITLE = "Test artist name";
-    private static final long AUDIO_START_TIME = 1L;
-    private static final DescriptiveTextView DESCRIPTIVE_TEXT_VIEW = new DescriptiveTextView(
-            /* image = */ null, "Primary Text", "Secondary Text");
-    private static final TextBlockView TEXT_BLOCK_VIEW = new TextBlockView("Text");
-
-    private final DescriptiveTextWithControlsView
-            mDescriptiveTextWithControlsView = new DescriptiveTextWithControlsView(
-            CARD_BACKGROUND_IMAGE,
-            AUDIO_VIEW_TITLE,
-            AUDIO_VIEW_SUBTITLE);
-    private final DescriptiveTextWithControlsView.Control mControl =
-            new DescriptiveTextWithControlsView.Control(BITMAP, v -> {
-            });
-    private final DescriptiveTextWithControlsView
-            mDescriptiveTextWithControlsViewWithButtons = new DescriptiveTextWithControlsView(
-            CARD_BACKGROUND_IMAGE, AUDIO_VIEW_TITLE, AUDIO_VIEW_SUBTITLE, AUDIO_START_TIME,
-            mControl, mControl, mControl);
-
-    @Rule
-    public ActivityTestRule<CarLauncher> mActivityTestRule =
-            new ActivityTestRule<CarLauncher>(CarLauncher.class);
-
-    @Test
-    public void updateContentAndHeaderView_audioContentNoControls_showsMediaPlaybackControlsBar() {
-        AudioFragment fragment = (AudioFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.bottom_card);
-        mActivityTestRule.getActivity().runOnUiThread(fragment::hideCard);
-        fragment.updateContentView(mDescriptiveTextWithControlsView);
-        // Card is only made visible when the header is updated
-        // But content should still be updated so it is correct when card is next made visible
-        onView(allOf(withId(R.id.card_view), isDescendantOfA(withId(R.id.bottom_card))))
-                .check(matches(not(isDisplayed())));
-
-        // Now the card is made visible and we verify that content has been updated
-        fragment.updateHeaderView(CARD_HEADER);
-        onView(allOf(withId(R.id.card_background),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.media_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.primary_text), withText(AUDIO_VIEW_TITLE),
-                isDescendantOfA(withId(R.id.media_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.secondary_text), withText(AUDIO_VIEW_SUBTITLE),
-                isDescendantOfA(withId(R.id.media_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.optional_timer), isDescendantOfA(withId(R.id.bottom_card)),
-                isDescendantOfA(withId(R.id.media_layout)))).check(
-                matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.media_playback_controls_bar),
-                isDescendantOfA(withId(R.id.media_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-    }
-
-    @Test
-    public void updateContentAndHeaderView_audioContentWithControls_showsControlBar() {
-        AudioFragment fragment = (AudioFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.bottom_card);
-        mActivityTestRule.getActivity().runOnUiThread(fragment::hideCard);
-        fragment.updateHeaderView(CARD_HEADER);
-        fragment.updateContentView(mDescriptiveTextWithControlsViewWithButtons);
-
-        onView(allOf(withId(R.id.optional_timer),
-                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.button_left),
-                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.button_center),
-                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.button_right),
-                isDescendantOfA(withId(R.id.descriptive_text_with_controls_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.media_playback_controls_bar),
-                isDescendantOfA(withId(R.id.media_layout)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.media_layout), isDescendantOfA(withId(R.id.bottom_card)))).check(
-                matches(not(isDisplayed())));
-    }
-
-    @Test
-    public void updateContentView_descriptiveText_hidesPlaybackControlsBar() {
-        AudioFragment fragment = (AudioFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.bottom_card);
-        fragment.updateContentView(mDescriptiveTextWithControlsView);
-        fragment.updateContentView(DESCRIPTIVE_TEXT_VIEW);
-
-        onView(allOf(withId(R.id.card_background),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.descriptive_text_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.media_layout), isDescendantOfA(withId(R.id.bottom_card)))).check(
-                matches(not(isDisplayed())));
-    }
-
-    @Test
-    public void updateContentView_textBlock_hidesPlaybackControlsBar() {
-        AudioFragment fragment = (AudioFragment) mActivityTestRule.getActivity()
-                .getSupportFragmentManager().findFragmentById(R.id.bottom_card);
-        fragment.updateContentView(mDescriptiveTextWithControlsView);
-        fragment.updateContentView(TEXT_BLOCK_VIEW);
-
-        onView(allOf(withId(R.id.card_background),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.card_background_image), is(instanceOf(CrossfadeImageView.class)),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.text_block_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(isDisplayed()));
-        onView(allOf(withId(R.id.descriptive_text_with_controls_layout),
-                isDescendantOfA(withId(R.id.bottom_card)))).check(matches(not(isDisplayed())));
-        onView(allOf(withId(R.id.media_layout), isDescendantOfA(withId(R.id.bottom_card)))).check(
-                matches(not(isDisplayed())));
-    }
-}
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenterTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenterTest.java
deleted file mode 100644
index 160245d..0000000
--- a/app/tests/src/com/android/car/carlauncher/homescreen/audio/HomeAudioCardPresenterTest.java
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Copyright (C) 2020 Google Inc.
- *
- * 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.carlauncher.homescreen.audio;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.view.View;
-
-import com.android.car.carlauncher.homescreen.HomeCardInterface;
-import com.android.car.carlauncher.homescreen.ui.CardHeader;
-import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
-import com.android.car.carlauncher.homescreen.ui.DescriptiveTextWithControlsView;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(JUnit4.class)
-public class HomeAudioCardPresenterTest {
-
-    private static final CardHeader CARD_HEADER = new CardHeader("testAppName", /* appIcon = */
-            null);
-    private static final DescriptiveTextView CARD_CONTENT = new DescriptiveTextView(/* image = */
-            null, "title", "subtitle");
-
-    private HomeAudioCardPresenter mPresenter;
-
-    @Mock
-    private View mFragmentView;
-    @Mock
-    private AudioFragment mView;
-    @Mock
-    private AudioModel mModel;
-    @Mock
-    private InCallModel mOtherModel;
-
-    private HomeCardInterface.Model.OnModelUpdateListener mOnModelUpdateListener;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mModel.getCardHeader()).thenReturn(CARD_HEADER);
-        when(mModel.getCardContent()).thenReturn(CARD_CONTENT);
-        mPresenter = new HomeAudioCardPresenter();
-        mPresenter.setView(mView);
-        mOnModelUpdateListener = mPresenter.mOnModelUpdateListener;
-    }
-
-    @Test
-    public void onModelUpdated_updatesFragment() {
-        mOnModelUpdateListener.onModelUpdate(mModel);
-
-        verify(mView).updateHeaderView(CARD_HEADER);
-        verify(mView).updateContentView(CARD_CONTENT);
-    }
-
-    @Test
-    public void onModelUpdated_nullDifferentModel_doesNotUpdate() {
-        when(mOtherModel.getCardHeader()).thenReturn(null);
-        mOnModelUpdateListener.onModelUpdate(mModel);
-        reset(mView);
-
-        mOnModelUpdateListener.onModelUpdate(mOtherModel);
-
-        verify(mView, never()).hideCard();
-        verify(mView, never()).updateHeaderView(any());
-        verify(mView, never()).updateContentView(any());
-    }
-
-    @Test
-    public void onModelUpdated_activePhoneCall_doesNotUpdateFragment() {
-        //setUpActivePhoneCall in presenter
-        CardHeader callModelHeader = new CardHeader("dialer", /* appIcon = */
-                null);
-        DescriptiveTextWithControlsView callModelContent = new DescriptiveTextWithControlsView(
-                /* image = */ null, "callerNumber", "ongoingCall");
-        when(mOtherModel.getCardHeader()).thenReturn(callModelHeader);
-        when(mOtherModel.getCardContent()).thenReturn(callModelContent);
-        mOnModelUpdateListener.onModelUpdate(mOtherModel);
-
-        // send MediaModel update during ongoing call
-        mOnModelUpdateListener.onModelUpdate(mModel);
-
-        //verify call
-        verify(mView).updateHeaderView(callModelHeader);
-        verify(mView).updateContentView(callModelContent);
-        verify(mView, never()).hideCard();
-        verify(mView, never()).updateHeaderView(CARD_HEADER);
-        verify(mView, never()).updateContentView(CARD_CONTENT);
-    }
-
-    @Test
-    public void onModelUpdated_nullSameModel_updatesFragment() {
-        mOnModelUpdateListener.onModelUpdate(mModel);
-        reset(mView);
-        when(mModel.getCardHeader()).thenReturn(null);
-
-        mOnModelUpdateListener.onModelUpdate(mModel);
-
-        verify(mView).hideCard();
-    }
-
-    @Test
-    public void onModelUpdated_nullModelAndNullCurrentModel_updatesFragment() {
-        when(mModel.getCardHeader()).thenReturn(null);
-
-        mOnModelUpdateListener.onModelUpdate(mModel);
-
-        verify(mView, never()).hideCard();
-    }
-}
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
index f5e0b01..e6e6583 100644
--- a/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/audio/MediaViewModelTest.java
@@ -30,6 +30,7 @@
 import android.graphics.drawable.Drawable;
 
 import androidx.lifecycle.MutableLiveData;
+import androidx.test.annotation.UiThreadTest;
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
@@ -111,6 +112,7 @@
     }
 
     @Before
+    @UiThreadTest
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mMediaViewModel = new MediaViewModel(ApplicationProvider.getApplicationContext(),
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenterTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenterTest.java
new file mode 100644
index 0000000..0be8313
--- /dev/null
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/audio/dialer/DialerCardPresenterTest.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio.dialer;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.ui.CardHeader;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class DialerCardPresenterTest {
+
+    private static final CardHeader CARD_HEADER = new CardHeader("testAppName", /* appIcon = */
+            null);
+    private static final DescriptiveTextView CARD_CONTENT = new DescriptiveTextView(/* image = */
+            null, "title", "subtitle");
+
+    private DialerCardPresenter mPresenter;
+
+    @Mock
+    private DialerCardFragment mView;
+    @Mock
+    private DialerCardModel mModel;
+
+    @Mock
+    private DialerCardPresenter.OnInCallStateChangeListener mOnInCallStateChangeListener;
+
+    private HomeCardInterface.Model.OnModelUpdateListener mOnModelUpdateListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mModel.getCardHeader()).thenReturn(CARD_HEADER);
+        when(mModel.getCardContent()).thenReturn(CARD_CONTENT);
+        when(mModel.hasActiveCall()).thenReturn(true);
+        mPresenter = new DialerCardPresenter();
+        mPresenter.setView(mView);
+        mPresenter.setModel(mModel);
+        mPresenter.setOnInCallStateChangeListener(mOnInCallStateChangeListener);
+        mOnModelUpdateListener = mPresenter.mOnInCallModelUpdateListener;
+    }
+
+    @Test
+    public void onModelUpdated_updatesFragment_hasActiveCall_callsStateChangedWithTrue() {
+        when(mModel.hasActiveCall()).thenReturn(true);
+        mPresenter.mHasActiveCall = false;
+
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView).updateHeaderView(CARD_HEADER);
+        verify(mView).updateContentView(CARD_CONTENT);
+        verify(mOnInCallStateChangeListener).onInCallStateChanged(true);
+    }
+
+    @Test
+    public void onModelUpdated_updatesFragment_noActiveCall_callStateChangedWithFalse() {
+        when(mModel.hasActiveCall()).thenReturn(false);
+        mPresenter.mHasActiveCall = true;
+
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView).updateHeaderView(CARD_HEADER);
+        verify(mView).updateContentView(CARD_CONTENT);
+        verify(mOnInCallStateChangeListener).onInCallStateChanged(false);
+    }
+
+    @Test
+    public void onModelUpdated_updatesFragment_noCallStateChange_doesNotCallStateChange() {
+        when(mModel.hasActiveCall()).thenReturn(true);
+        mPresenter.mHasActiveCall = true;
+
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView).updateHeaderView(CARD_HEADER);
+        verify(mView).updateContentView(CARD_CONTENT);
+        verify(mOnInCallStateChangeListener, never()).onInCallStateChanged(anyBoolean());
+    }
+
+    @Test
+    public void onModelUpdated_nullHeaderAndContent_doesNotUpdateFragment() {
+        when(mModel.getCardHeader()).thenReturn(null);
+        when(mModel.getCardContent()).thenReturn(null);
+
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView, never()).updateContentView(any());
+        verify(mView, never()).updateHeaderView(any());
+    }
+}
diff --git a/app/tests/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenterTest.java b/app/tests/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenterTest.java
new file mode 100644
index 0000000..029db1b
--- /dev/null
+++ b/app/tests/src/com/android/car/carlauncher/homescreen/audio/media/MediaCardPresenterTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.homescreen.audio.media;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import com.android.car.carlauncher.homescreen.HomeCardInterface;
+import com.android.car.carlauncher.homescreen.audio.MediaViewModel;
+import com.android.car.carlauncher.homescreen.ui.CardHeader;
+import com.android.car.carlauncher.homescreen.ui.DescriptiveTextView;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public class MediaCardPresenterTest {
+
+    private static final CardHeader CARD_HEADER = new CardHeader("testAppName", /* appIcon = */
+            null);
+    private static final DescriptiveTextView CARD_CONTENT = new DescriptiveTextView(/* image = */
+            null, "title", "subtitle");
+
+    private MediaCardPresenter mPresenter;
+
+    @Mock
+    private MediaCardFragment mView;
+    @Mock
+    private MediaViewModel mModel;
+
+    private HomeCardInterface.Model.OnModelUpdateListener mOnModelUpdateListener;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mModel.getCardHeader()).thenReturn(CARD_HEADER);
+        when(mModel.getCardContent()).thenReturn(CARD_CONTENT);
+        mPresenter = new MediaCardPresenter();
+        mPresenter.setView(mView);
+        mPresenter.setModel(mModel);
+        mOnModelUpdateListener = mPresenter.mOnMediaModelUpdateListener;
+    }
+
+    @Test
+    public void onModelUpdated_updatesFragment() {
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView).updateHeaderView(CARD_HEADER);
+        verify(mView).updateContentView(CARD_CONTENT);
+    }
+
+    @Test
+    public void onModelUpdated_nullHeaderAndContent_doesNotUpdateFragment() {
+        when(mModel.getCardHeader()).thenReturn(null);
+        when(mModel.getCardContent()).thenReturn(null);
+
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        verify(mView, never()).updateContentView(any());
+        verify(mView, never()).updateHeaderView(any());
+    }
+
+    @Test
+    public void onModelUpdated_activePhoneCall_doesNotUpdateFragment() {
+        //mimic active phone call in presenter
+        mPresenter.setShowMedia(false);
+
+        // send MediaModel update during ongoing call
+        mOnModelUpdateListener.onModelUpdate(mModel);
+
+        //verify call
+        verify(mView, never()).updateHeaderView(any());
+        verify(mView, never()).updateContentView(any());
+    }
+}
diff --git a/app/tests/src/com/android/car/carlauncher/recents/RecentTasksProviderTest.java b/app/tests/src/com/android/car/carlauncher/recents/RecentTasksProviderTest.java
index b03ebe4..71aa8d8 100644
--- a/app/tests/src/com/android/car/carlauncher/recents/RecentTasksProviderTest.java
+++ b/app/tests/src/com/android/car/carlauncher/recents/RecentTasksProviderTest.java
@@ -18,9 +18,9 @@
 
 import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE;
 
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM;
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SINGLE;
-import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_SPLIT;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SINGLE;
+import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_SPLIT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -56,7 +56,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
 import com.android.wm.shell.recents.IRecentTasks;
-import com.android.wm.shell.util.GroupedRecentTaskInfo;
+import com.android.wm.shell.shared.GroupedRecentTaskInfo;
 
 import com.google.common.util.concurrent.MoreExecutors;
 
diff --git a/build.gradle b/build.gradle
index 5590825..2d3cb5d 100644
--- a/build.gradle
+++ b/build.gradle
@@ -27,12 +27,16 @@
     gradle.ext.lib_car_ui_lib_aar = gradle.ext.prebuiltSdkPath + "current/aaos-libs/car-ui-lib.aar"
     gradle.ext.lib_car_ui_lib_oem_apis = gradle.ext.prebuiltSdkPath + "current/aaos-libs/car-ui-lib-oem-apis.jar"
     gradle.ext.lib_system_stubs = gradle.ext.prebuiltSdkPath + gradle.ext.aaosLatestSDK + "/system/android.jar"
+    gradle.ext.lib_car_test_api = gradle.ext.prebuiltSdkPath + gradle.ext.aaosLatestSDK + "/system/android.car.testapi.jar"
     gradle.ext.debugCertPath = gradle.ext.repoRootPath + "/packages/apps/Car/Launcher/libs/appgrid/keys/com_android_car_launcher_test.jks"
     gradle.ext.soongBash = gradle.ext.repoRootPath + "/build/soong/soong_ui.bash"
-    gradle.ext.platformSdkVersion = "34" // Change this to the most recent android API level.
+    gradle.ext.platformSdkVersion = "35" // Change this to the most recent android API level.
 
     if (file(gradle.ext.soongBash).exists()) {
-        gradle.ext.platformSdkVersion = (gradle.ext.soongBash + " --dumpvar-mode PLATFORM_SDK_VERSION").execute().text.trim()
+        def soongPlatformSdkVersion = (gradle.ext.soongBash + " --dumpvar-mode PLATFORM_SDK_VERSION").execute().text.trim()
+        if (!soongPlatformSdkVersion.isEmpty()) {
+            gradle.ext.platformSdkVersion = soongPlatformSdkVersion
+        }
     }
 
     gradle.ext.getVersionCode = { ->
@@ -48,13 +52,13 @@
 }
 
 plugins {
-    id 'com.android.application' version '8.1.2' apply false
-    id 'com.android.library' version '8.1.2' apply false
+    // TODO b/324426571: The version for com.android.application and com.android.library is 8.1.2
+    id 'com.android.application' apply false
+    id 'com.android.library' apply false
     id 'org.jetbrains.kotlin.android' version '1.8.21' apply false
     id 'com.google.protobuf' version '0.9.1' apply false
 }
 
-
 allprojects {
     tasks.withType(JavaCompile).tap {
         configureEach {
@@ -64,4 +68,5 @@
             options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation"
         }
     }
+    ext.ANDROID_TOP = gradle.ext.repoRootPath
 }
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java b/buildSrc/build.gradle.kts
similarity index 68%
copy from app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
copy to buildSrc/build.gradle.kts
index e30ffe5..d53f40c 100644
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
+++ b/buildSrc/build.gradle.kts
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,10 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher;
+plugins {
+    `java-gradle-plugin`
+    `kotlin-dsl`
+}
 
-/**
- * A callbacks interface for {@link LaunchRootCarTaskView}.
- */
-public interface LaunchRootCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {}
+repositories {
+    mavenCentral()
+    google()
+}
+
+dependencies {
+    implementation("com.android.tools.build:gradle:8.1.2")
+}
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java b/buildSrc/src/main/java/aconfig.gradle.kts
similarity index 68%
rename from app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
rename to buildSrc/src/main/java/aconfig.gradle.kts
index e30ffe5..d40d8ca 100644
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
+++ b/buildSrc/src/main/java/aconfig.gradle.kts
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,10 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher;
+import aconfig.AConfigPlugin
+import org.gradle.kotlin.dsl.apply
 
-/**
- * A callbacks interface for {@link LaunchRootCarTaskView}.
- */
-public interface LaunchRootCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {}
+apply<AConfigPlugin>()
diff --git a/buildSrc/src/main/java/aconfig/AConfigCreateCacheTask.kt b/buildSrc/src/main/java/aconfig/AConfigCreateCacheTask.kt
new file mode 100644
index 0000000..5212e33
--- /dev/null
+++ b/buildSrc/src/main/java/aconfig/AConfigCreateCacheTask.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 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 aconfig
+
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.AbstractExecTask
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.InputFiles
+import org.gradle.api.tasks.OutputFile
+
+abstract class AConfigCreateCacheTask :
+    AbstractExecTask<AConfigCreateCacheTask>(AConfigCreateCacheTask::class.java) {
+
+    @get:InputFile
+    abstract val aconfigPath: RegularFileProperty
+
+    @get:Input
+    abstract var packageName: String
+
+    @get:InputFiles
+    abstract val srcFiles: ConfigurableFileCollection
+
+    @get:OutputFile
+    abstract val outputFile: RegularFileProperty
+
+    override fun exec() {
+        commandLine(aconfigPath.get())
+        args("create-cache", "--package", packageName)
+
+        srcFiles.files.forEach { aconfigFile ->
+            args("--declarations", aconfigFile)
+        }
+        args("--cache", "${outputFile.get()}")
+        super.exec()
+    }
+}
diff --git a/buildSrc/src/main/java/aconfig/AConfigCreateJavaLibTask.kt b/buildSrc/src/main/java/aconfig/AConfigCreateJavaLibTask.kt
new file mode 100644
index 0000000..95e53ca
--- /dev/null
+++ b/buildSrc/src/main/java/aconfig/AConfigCreateJavaLibTask.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 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 aconfig
+
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.tasks.AbstractExecTask
+import org.gradle.api.tasks.InputFile
+import org.gradle.api.tasks.OutputDirectory
+
+abstract class AConfigCreateJavaLibTask :
+    AbstractExecTask<AConfigCreateJavaLibTask>(AConfigCreateJavaLibTask::class.java) {
+
+    @get:InputFile
+    abstract val aconfigPath: RegularFileProperty
+
+    @get:InputFile
+    abstract val cacheFile: RegularFileProperty
+
+    @get:OutputDirectory
+    abstract val outputFolder: DirectoryProperty
+
+    override fun exec() {
+        commandLine(aconfigPath.get())
+        args(
+            "create-java-lib",
+            "--mode",
+            "production",
+            "--cache",
+            cacheFile.get(),
+            "--out",
+            outputFolder.get()
+        )
+        super.exec()
+    }
+}
diff --git a/buildSrc/src/main/java/aconfig/AConfigExtension.kt b/buildSrc/src/main/java/aconfig/AConfigExtension.kt
new file mode 100644
index 0000000..c2eb1ff
--- /dev/null
+++ b/buildSrc/src/main/java/aconfig/AConfigExtension.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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 aconfig
+
+import org.gradle.api.Action
+import org.gradle.api.DomainObjectSet
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.provider.Property
+
+interface AConfigDeclaration {
+    val packageName: Property<String>
+    val srcFile: ConfigurableFileCollection
+}
+
+open class AConfigExtension(private val objectFactory: ObjectFactory) {
+
+    val declarations: DomainObjectSet<AConfigDeclaration> = objectFactory.domainObjectSet(
+        AConfigDeclaration::class.java
+    )
+
+    fun aconfigDeclaration(action: Action<AConfigDeclaration>) {
+        val declaration = objectFactory.newInstance(AConfigDeclaration::class.java)
+        action.execute(declaration)
+        declarations.add(declaration)
+    }
+}
diff --git a/buildSrc/src/main/java/aconfig/AConfigPlugin.kt b/buildSrc/src/main/java/aconfig/AConfigPlugin.kt
new file mode 100644
index 0000000..7c3c868
--- /dev/null
+++ b/buildSrc/src/main/java/aconfig/AConfigPlugin.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 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 aconfig
+
+import com.android.build.api.variant.AndroidComponentsExtension
+import java.io.File
+import org.apache.tools.ant.taskdefs.condition.Os
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.configurationcache.extensions.capitalized
+import org.gradle.kotlin.dsl.create
+import org.gradle.kotlin.dsl.extra
+import org.gradle.kotlin.dsl.getByType
+import org.gradle.kotlin.dsl.register
+
+abstract class AConfigPlugin : Plugin<Project> {
+
+    override fun apply(project: Project) {
+        project.dependencies.add("implementation", project.project(":libs:aconfig-platform-compat"))
+        project.extensions.create<AConfigExtension>("aconfig", project.objects)
+        val androidComponents = project.extensions.getByType(AndroidComponentsExtension::class.java)
+        val androidTop = project.extra["ANDROID_TOP"].toString()
+        val platform = if (Os.isFamily(Os.FAMILY_MAC)) "darwin" else "linux"
+        androidComponents.onVariants { variant ->
+            val variantName = variant.name.capitalized()
+            val aconfigExtension = project.extensions.getByType<AConfigExtension>()
+            val aconfigBin = File("$androidTop/prebuilts/build-tools/$platform-x86/bin/aconfig")
+
+            aconfigExtension.declarations.forEach {
+                val pkgName = it.packageName.get()
+                val addFlagCacheTaskProvider = project.tasks.register<AConfigCreateCacheTask>(
+                    "generate${variantName}FlagCache_$pkgName"
+                ) {
+                    aconfigPath.set(aconfigBin)
+                    packageName = pkgName
+                    srcFiles.setFrom(it.srcFile)
+                    outputFile.set(
+                        project.layout.buildDirectory.file(
+                            "intermediates/${variant.name}/aconfig/flag-cache-$pkgName.pb"
+                        )
+                    )
+                }
+                val addFlagLibTaskProvider = project.tasks.register<AConfigCreateJavaLibTask>(
+                    "generate${variantName}FlagLib_$pkgName"
+                ) {
+                    aconfigPath.set(aconfigBin)
+                    cacheFile.set(
+                        addFlagCacheTaskProvider.flatMap(AConfigCreateCacheTask::outputFile)
+                    )
+                }
+                variant.sources.java?.addGeneratedSourceDirectory(
+                    addFlagLibTaskProvider,
+                    AConfigCreateJavaLibTask::outputFolder
+                )
+            }
+        }
+    }
+}
diff --git a/buildSrc/src/main/java/aconfig/README.md b/buildSrc/src/main/java/aconfig/README.md
new file mode 100644
index 0000000..d560409
--- /dev/null
+++ b/buildSrc/src/main/java/aconfig/README.md
@@ -0,0 +1,23 @@
+# AAOS AConfig gradle Plugin
+This plugin is copied over from:
+```
+$ANDROID_BUILD_TOP/packages/apps/ManagedProvisioning/studio-dev/ManagedProvisioningGradleProject/buildSrc/src/main/java/
+```
+
+This gradle plugin generates Trunk-stable Flag helper classes.
+
+## Using in module's build.gradle
+Add `id 'aconfig'` in plugins blocks and specify `packageName` and `.aconfig src file`
+For example:
+```
+plugins {
+    id 'aconfig'
+}
+
+aconfig {
+    aconfigDeclaration {
+        packageName.set("com.example.package.name")
+        srcFile.setFrom(files("some_flags.aconfig"))
+    }
+}
+```
diff --git a/docklib-util/Android.bp b/docklib-util/Android.bp
index d7e2f5a..29ae215 100644
--- a/docklib-util/Android.bp
+++ b/docklib-util/Android.bp
@@ -17,21 +17,32 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-
 android_library {
     name: "CarDockUtilLib",
     srcs: [
         "src/**/*.java",
         "src/**/*.kt",
+        ":hidden_api_enabled_srcs",
     ],
 
     resource_dirs: ["res"],
 
     static_libs: [
+        "dock_flags_java_lib",
         "androidx.lifecycle_lifecycle-extensions",
-        "com.google.android.material_material",
-        "car-ui-lib",
     ],
 
     manifest: "AndroidManifest.xml",
 }
+
+aconfig_declarations {
+    name: "dock_flags",
+    package: "com.android.car.dockutil",
+    container: "system",
+    srcs: ["dock_flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "dock_flags_java_lib",
+    aconfig_declarations: "dock_flags",
+}
diff --git a/docklib-util/OWNERS b/docklib-util/OWNERS
index c7be970..7c73cd2 100644
--- a/docklib-util/OWNERS
+++ b/docklib-util/OWNERS
@@ -5,4 +5,3 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
diff --git a/docklib-util/build.gradle b/docklib-util/build.gradle
new file mode 100644
index 0000000..c7f87d4
--- /dev/null
+++ b/docklib-util/build.gradle
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+    id 'aconfig'
+}
+
+aconfig {
+    aconfigDeclaration {
+        packageName.set("com.android.car.dockutil")
+        srcFile.setFrom(files("dock_flags.aconfig"))
+    }
+}
+
+android {
+    namespace 'com.android.car.dockutil'
+    compileSdk gradle.ext.aaosTargetSDK
+
+    defaultConfig {
+        minSdk gradle.ext.aaosLatestSDK
+        targetSdk gradle.ext.aaosLatestSDK
+        versionCode gradle.ext.getVersionCode()
+        versionName gradle.ext.getVersionName()
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'AndroidManifest.xml'
+            res.srcDirs = ['res']
+            java.srcDirs = ['src']
+        }
+
+        androidTest {
+            java.srcDirs += 'tests/src'
+        }
+    }
+
+    testOptions {
+        unitTests {
+            includeAndroidResources = true
+            returnDefaultValues = true
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    lintOptions {
+        disable 'PrivateResource'
+    }
+}
+
+dependencies {
+    implementation project(":libs:hidden-apis-compat:hidden-apis-disabled")
+    implementation 'androidx.annotation:annotation:1.7.1'
+
+    //Android Test dependencies
+    androidTestImplementation project(":libs:hidden-apis-compat:hidden-apis-disabled")
+    androidTestImplementation 'junit:junit:4.13.2'
+    androidTestImplementation "org.mockito:mockito-android:5.10.0"
+    androidTestImplementation "org.mockito:mockito-core:5.10.0"
+    androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0"
+    androidTestImplementation "androidx.test:rules:1.5.0"
+    androidTestImplementation 'androidx.test:runner:1.5.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+    androidTestImplementation "com.google.truth:truth:1.3.0"
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+}
diff --git a/docklib-util/dock_flags.aconfig b/docklib-util/dock_flags.aconfig
new file mode 100644
index 0000000..b9e7369
--- /dev/null
+++ b/docklib-util/dock_flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.car.dockutil"
+container: "system"
+
+flag {
+  name: "dock_feature"
+  namespace: "car_sys_exp"
+  description: "This flag enables dock in Car"
+  bug: "301482374"
+}
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-af/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-af/strings.xml
index 2c24df7..3b9c4bd 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-af/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Speld app vas"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Ontspeld app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-am/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-am/strings.xml
index 2c24df7..b003435 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-am/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"መተግበሪያን ፒን ያድርጉ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"መተግበሪያ ይንቀሉ"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ar/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ar/strings.xml
index 2c24df7..ddcc49a 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ar/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"تثبيت التطبيق"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"إزالة تثبيت التطبيق"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-as/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-as/strings.xml
index 2c24df7..e21128b 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-as/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"এপ্‌ পিন কৰক"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"এপ্‌ আনপিন কৰক"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-az/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-az/strings.xml
index 2c24df7..92f48ca 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-az/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Tətbiqi bərkidin"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Tətbiqi bərkitməyin"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-b+sr+Latn/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-b+sr+Latn/strings.xml
index 2c24df7..b19e719 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-b+sr+Latn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Zakači aplikaciju"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Otkači aplikaciju"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-be/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-be/strings.xml
index 2c24df7..8969359 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-be/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Замацаваць праграму"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Адмацаваць праграму"</string>
 </resources>
diff --git a/docklib-util/res/values-bg/strings.xml b/docklib-util/res/values-bg/strings.xml
new file mode 100644
index 0000000..752ec64
--- /dev/null
+++ b/docklib-util/res/values-bg/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Фиксиране на приложението"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Освобождаване на приложението"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-bn/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-bn/strings.xml
index 2c24df7..1b0dbe3 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-bn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"অ্যাপ পিন করুন"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"অ্যাপ আনপিন করুন"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-bs/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-bs/strings.xml
index 2c24df7..9b9734b 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-bs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Kačenje aplikacije"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Otkačivanje aplikacije"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ca/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ca/strings.xml
index 2c24df7..3cc848c 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ca/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fixa l\'aplicació"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Deixa de fixar l\'aplicació"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-cs/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-cs/strings.xml
index 2c24df7..9e9312e 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-cs/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Připnout aplikaci"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Odepnout aplikaci"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-da/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-da/strings.xml
index 2c24df7..636417f 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-da/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fastgør app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Frigør app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-de/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-de/strings.xml
index 2c24df7..4c1593d 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-de/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"App anpinnen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"App loslösen"</string>
 </resources>
diff --git a/docklib-util/res/values-el/strings.xml b/docklib-util/res/values-el/strings.xml
new file mode 100644
index 0000000..75454fd
--- /dev/null
+++ b/docklib-util/res/values-el/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Καρφίτσωμα εφαρμογής"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Ξεκαρφίτσωμα εφαρμογής"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-en-rAU/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-en-rAU/strings.xml
index 2c24df7..6424b1a 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-en-rAU/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Unpin app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-en-rGB/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-en-rGB/strings.xml
index 2c24df7..6424b1a 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-en-rGB/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Unpin app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-en-rIN/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-en-rIN/strings.xml
index 2c24df7..6424b1a 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-en-rIN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Unpin app"</string>
 </resources>
diff --git a/docklib-util/res/values-en-rXC/strings.xml b/docklib-util/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..54b03e0
--- /dev/null
+++ b/docklib-util/res/values-en-rXC/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‎‎‏‎‎‎‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‎‎‏‏‎‎‏‎Pin app‎‏‎‎‏‎"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‏‎‏‏‎‎‎‏‏‎‏‏‎‏‎‎Unpin app‎‏‎‎‏‎"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-es-rUS/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-es-rUS/strings.xml
index 2c24df7..2dc2ca5 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-es-rUS/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fijar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Dejar de fijar app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-es/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-es/strings.xml
index 2c24df7..8370b21 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-es/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fijar aplicación"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Dejar de fijar aplicación"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-et/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-et/strings.xml
index 2c24df7..2161fee 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-et/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Rakenduse kinnitamine"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Rakenduse vabastamine"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-eu/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-eu/strings.xml
index 2c24df7..fcfdb19 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-eu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Ainguratu aplikazioa"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Kendu aplikazioaren aingura"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-fa/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-fa/strings.xml
index 2c24df7..2edabaa 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-fa/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"سنجاق کردن برنامه"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"برداشتن سنجاق برنامه"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-fi/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-fi/strings.xml
index 2c24df7..cfc4bf3 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-fi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Kiinnitä sovellus"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Irrota sovellus"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-fr-rCA/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-fr-rCA/strings.xml
index 2c24df7..2e6a162 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-fr-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Épingler l\'application"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Annuler l\'épinglage de l\'application"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-fr/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-fr/strings.xml
index 2c24df7..3b22b81 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-fr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Épingler l\'appli"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Retirer l\'appli"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-gl/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-gl/strings.xml
index 2c24df7..7168357 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-gl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fixar aplicación"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Deixar de fixar aplicación"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-gu/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-gu/strings.xml
index 2c24df7..6c3710b 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-gu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ઍપ પિન કરો"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ઍપ અનપિન કરો"</string>
 </resources>
diff --git a/docklib-util/res/values-hi/strings.xml b/docklib-util/res/values-hi/strings.xml
new file mode 100644
index 0000000..4a66c72
--- /dev/null
+++ b/docklib-util/res/values-hi/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ऐप्लिकेशन को पिन करें"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ऐप्लिकेशन को अनपिन करें"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-hr/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-hr/strings.xml
index 2c24df7..8bd8dd4 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-hr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Prikvačite aplikaciju"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Otkvačite aplikaciju"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-hu/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-hu/strings.xml
index 2c24df7..e4857fe 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-hu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Alkalmazás kitűzése"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Alkalmazás kitűzésének megszüntetése"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-hy/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-hy/strings.xml
index 2c24df7..b5ae9e2 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-hy/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Ամրացնել հավելվածը"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Ապամրացնել հավելվածը"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-in/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-in/strings.xml
index 2c24df7..4588f2d 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-in/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Sematkan aplikasi"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Lepaskan aplikasi"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-is/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-is/strings.xml
index 2c24df7..208fa99 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-is/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Festa forrit"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Losa forrit"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-it/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-it/strings.xml
index 2c24df7..9372bc3 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-it/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fissa app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Stacca app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-iw/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-iw/strings.xml
index 2c24df7..de0d846 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-iw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"הצמדת האפליקציה"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ביטול הצמדת האפליקציה"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ja/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ja/strings.xml
index 2c24df7..04061a6 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ja/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"アプリを固定"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"アプリの固定を解除"</string>
 </resources>
diff --git a/docklib-util/res/values-ka/strings.xml b/docklib-util/res/values-ka/strings.xml
new file mode 100644
index 0000000..e180e73
--- /dev/null
+++ b/docklib-util/res/values-ka/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"აპის ჩამაგრება"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"აპის ჩამაგრების მოხსნა"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-kk/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-kk/strings.xml
index 2c24df7..3b33b0b 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-kk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Қолданбаны бекіту"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Қолданбаны босату"</string>
 </resources>
diff --git a/docklib-util/res/values-km/strings.xml b/docklib-util/res/values-km/strings.xml
new file mode 100644
index 0000000..1429d24
--- /dev/null
+++ b/docklib-util/res/values-km/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ខ្ទាស់កម្មវិធី"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ដកខ្ទាស់កម្មវិធី"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-kn/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-kn/strings.xml
index 2c24df7..c148268 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-kn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ಆ್ಯಪ್ ಪಿನ್ ಮಾಡಿ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ಆ್ಯಪ್ ಅನ್‌ಪಿನ್ ಮಾಡಿ"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ko/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ko/strings.xml
index 2c24df7..b13e0bd 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ko/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"앱 고정"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"앱 고정 해제"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ky/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ky/strings.xml
index 2c24df7..25463e6 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ky/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Колдонмону кадап коюу"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Колдонмону бошотуу"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-lo/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-lo/strings.xml
index 2c24df7..3459dee 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-lo/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ປັກໝຸດແອັບ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ຖອດປັກໝຸດແອັບ"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-lt/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-lt/strings.xml
index 2c24df7..206c28c 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-lt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Prisegti programą"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Atsegti programą"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-lv/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-lv/strings.xml
index 2c24df7..9424827 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-lv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Piespraust lietotni"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Atspraust lietotni"</string>
 </resources>
diff --git a/docklib-util/res/values-mk/strings.xml b/docklib-util/res/values-mk/strings.xml
new file mode 100644
index 0000000..9351b67
--- /dev/null
+++ b/docklib-util/res/values-mk/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Закачување на апликацијата"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Откачување на апликацијата"</string>
+</resources>
diff --git a/docklib-util/res/values-ml/strings.xml b/docklib-util/res/values-ml/strings.xml
new file mode 100644
index 0000000..472f559
--- /dev/null
+++ b/docklib-util/res/values-ml/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ആപ്പ് പിൻ ചെയ്യുക"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ആപ്പ് അൺപിൻ ചെയ്യുക"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-mn/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-mn/strings.xml
index 2c24df7..04ef4c9 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-mn/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Аппыг бэхлэх"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Аппыг бэхэлснийг болиулах"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-mr/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-mr/strings.xml
index 2c24df7..5145620 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-mr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"अ‍ॅप पिन करा"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"अ‍ॅप अनपिन करा"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ms/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ms/strings.xml
index 2c24df7..ac93edf 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ms/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Semat apl"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Nyahsemat apl"</string>
 </resources>
diff --git a/docklib-util/res/values-my/strings.xml b/docklib-util/res/values-my/strings.xml
new file mode 100644
index 0000000..eb48e4f
--- /dev/null
+++ b/docklib-util/res/values-my/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"အက်ပ်ကို ပင်ထိုးရန်"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"အက်ပ်ကို ပင်ဖြုတ်ရန်"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-nb/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-nb/strings.xml
index 2c24df7..b72bf4e 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-nb/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fest appen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Løsne appen"</string>
 </resources>
diff --git a/docklib-util/res/values-ne/strings.xml b/docklib-util/res/values-ne/strings.xml
new file mode 100644
index 0000000..ad0e79a
--- /dev/null
+++ b/docklib-util/res/values-ne/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"एप पिन गर्नुहोस्"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"एप अनपिन गर्नुहोस्"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-nl/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-nl/strings.xml
index 2c24df7..c5b068c 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-nl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"App vastzetten"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"App losmaken"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-or/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-or/strings.xml
index 2c24df7..87a4a09 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-or/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ଆପ ପିନ କରନ୍ତୁ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ଆପ ଅନପିନ କରନ୍ତୁ"</string>
 </resources>
diff --git a/docklib-util/res/values-pa/strings.xml b/docklib-util/res/values-pa/strings.xml
new file mode 100644
index 0000000..b482240
--- /dev/null
+++ b/docklib-util/res/values-pa/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ਐਪ ਨੂੰ ਪਿੰਨ ਕਰੋ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ਐਪ ਨੂੰ ਅਣਪਿੰਨ ਕਰੋ"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-pl/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-pl/strings.xml
index 2c24df7..7695a48 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-pl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Przypnij aplikację"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Odepnij aplikację"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-pt-rPT/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-pt-rPT/strings.xml
index 2c24df7..63ba79e 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-pt-rPT/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Afixar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Desafixar app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-pt/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-pt/strings.xml
index 2c24df7..19ed9e2 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-pt/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fixar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Liberar app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ro/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ro/strings.xml
index 2c24df7..0f3d303 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ro/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fixează aplicația"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Anulează fixarea aplicației"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ru/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ru/strings.xml
index 2c24df7..d7c1efc 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ru/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Закрепить приложение"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Открепить приложение"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-si/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-si/strings.xml
index 2c24df7..d386dab 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-si/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"යෙදුම අමුණන්න"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"යෙදුම නොඅමුණන්න"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sk/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sk/strings.xml
index 2c24df7..d711648 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Pripnúť aplikáciu"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Odopnúť aplikáciu"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sl/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sl/strings.xml
index 2c24df7..7a97c75 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Pripenjanje aplikacije"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Odpenjanje aplikacije"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sq/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sq/strings.xml
index 2c24df7..d1b3afe 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sq/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Gozhdo aplikacionin"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Zhgozhdo aplikacionin"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sr/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sr/strings.xml
index 2c24df7..4a1fbc6 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Закачи апликацију"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Откачи апликацију"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sv/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sv/strings.xml
index 2c24df7..9a0907f 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sv/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Fäst appen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Lossa appen"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-sw/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-sw/strings.xml
index 2c24df7..973e2b4 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-sw/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Bandika programu"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Bandua programu"</string>
 </resources>
diff --git a/docklib-util/res/values-ta/strings.xml b/docklib-util/res/values-ta/strings.xml
new file mode 100644
index 0000000..d4bd741
--- /dev/null
+++ b/docklib-util/res/values-ta/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ஆப்ஸைப் பின் செய்தல்"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ஆப்ஸை அகற்றுதல்"</string>
+</resources>
diff --git a/docklib-util/res/values-te/strings.xml b/docklib-util/res/values-te/strings.xml
new file mode 100644
index 0000000..4b9ebca
--- /dev/null
+++ b/docklib-util/res/values-te/strings.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"యాప్‌ను పిన్ చేయండి"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"యాప్‌ను అన్‌పిన్ చేయండి"</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-th/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-th/strings.xml
index 2c24df7..2d07171 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-th/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ปักหมุดแอป"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"เลิกปักหมุดแอป"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-tl/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-tl/strings.xml
index 2c24df7..1978d27 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-tl/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"I-pin ang app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"I-unpin ang app"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-tr/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-tr/strings.xml
index 2c24df7..0c34641 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-tr/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Uygulamayı sabitle"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Uygulamanın sabitlemesini kaldır"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-uk/strings.xml
similarity index 60%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-uk/strings.xml
index 2c24df7..f0530b4 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-uk/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Закріпити додаток"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Відкріпити додаток"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-ur/strings.xml
similarity index 61%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-ur/strings.xml
index 2c24df7..78627b5 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-ur/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"ایپ پن کریں"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"ایپ کی پن ہٹائیں"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-uz/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-uz/strings.xml
index 2c24df7..ef2fe7c 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-uz/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Ilovani mahkamlash"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Ilovani yechish"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-vi/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-vi/strings.xml
index 2c24df7..5edae37 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-vi/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Ghim ứng dụng"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Bỏ ghim ứng dụng"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-zh-rCN/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-zh-rCN/strings.xml
index 2c24df7..25a1bcc 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-zh-rCN/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"固定应用"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"取消固定应用"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-zh-rHK/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-zh-rHK/strings.xml
index 2c24df7..88eebc5 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-zh-rHK/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"固定應用程式"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"取消固定應用程式"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-zh-rTW/strings.xml
similarity index 62%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-zh-rTW/strings.xml
index 2c24df7..88eebc5 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-zh-rTW/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"固定應用程式"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"取消固定應用程式"</string>
 </resources>
diff --git a/docklib-util/res/values/strings.xml b/docklib-util/res/values-zu/strings.xml
similarity index 63%
copy from docklib-util/res/values/strings.xml
copy to docklib-util/res/values-zu/strings.xml
index 2c24df7..3b4a509 100644
--- a/docklib-util/res/values/strings.xml
+++ b/docklib-util/res/values-zu/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,10 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="2366240044962379929">"Phina i-app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="4995780367670958298">"Susa ukuphina i-app"</string>
 </resources>
diff --git a/docklib-util/res/values/config.xml b/docklib-util/res/values/config.xml
index 93ff11d..2a28350 100644
--- a/docklib-util/res/values/config.xml
+++ b/docklib-util/res/values/config.xml
@@ -16,7 +16,4 @@
   -->
 
 <resources>
-    <!-- Flag to enable/disable dock -->
-    <!-- todo(b/304320644): remove flag -->
-    <bool name="config_enableDock">false</bool>
 </resources>
diff --git a/docklib-util/src/com/android/car/dockutil/events/DockEventSenderHelper.java b/docklib-util/src/com/android/car/dockutil/events/DockEventSenderHelper.java
index 4f8b52f..42197d6 100644
--- a/docklib-util/src/com/android/car/dockutil/events/DockEventSenderHelper.java
+++ b/docklib-util/src/com/android/car/dockutil/events/DockEventSenderHelper.java
@@ -16,6 +16,8 @@
 
 package com.android.car.dockutil.events;
 
+import static com.android.car.hidden.apis.HiddenApiAccess.getDisplayId;
+
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
@@ -26,7 +28,7 @@
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
 
-import com.android.car.dockutil.R;
+import com.android.car.dockutil.Flags;
 
 /**
  * Helper used to send Dock Events.
@@ -35,11 +37,9 @@
     public static final String EXTRA_COMPONENT = "EXTRA_COMPONENT";
 
     private final Context mContext;
-    private final boolean mIsDockEnabled;
 
     public DockEventSenderHelper(Context context) {
         mContext = context;
-        mIsDockEnabled = mContext.getResources().getBoolean(R.bool.config_enableDock);
     }
 
     /**
@@ -80,8 +80,8 @@
 
     @VisibleForTesting
     void sendEventBroadcast(@NonNull DockEvent event,
-                            @NonNull ActivityManager.RunningTaskInfo taskInfo) {
-        if (taskInfo.getDisplayId() != Display.DEFAULT_DISPLAY) {
+            @NonNull ActivityManager.RunningTaskInfo taskInfo) {
+        if (getDisplayId(taskInfo) != Display.DEFAULT_DISPLAY) {
             return;
         }
         ComponentName component = getComponentName(taskInfo);
@@ -91,7 +91,7 @@
     }
 
     private void sendEventBroadcast(@NonNull DockEvent event, @NonNull ComponentName component) {
-        if (!mIsDockEnabled) {
+        if (!Flags.dockFeature()) {
             return;
         }
 
diff --git a/docklib-util/tests/Android.bp b/docklib-util/tests/Android.bp
index 6883e97..4832e21 100644
--- a/docklib-util/tests/Android.bp
+++ b/docklib-util/tests/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2023 Google Inc.
+// Copyright (C) 2024 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.
@@ -16,6 +16,7 @@
 
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
 }
 
 android_test {
@@ -27,7 +28,7 @@
     ],
 
     libs: [
-        "android.test.base",
+        "android.test.base.stubs.system",
     ],
 
     optimize: {
@@ -41,6 +42,7 @@
         "mockito-kotlin2",
         "truth",
         "CarDockUtilLib",
+        "flag-junit",
     ],
 
     manifest: "AndroidManifest.xml",
diff --git a/docklib-util/tests/src/com/android/car/dockutil/events/DockEventSenderHelperTest.java b/docklib-util/tests/src/com/android/car/dockutil/events/DockEventSenderHelperTest.java
index 892e99b..81f8bbb 100644
--- a/docklib-util/tests/src/com/android/car/dockutil/events/DockEventSenderHelperTest.java
+++ b/docklib-util/tests/src/com/android/car/dockutil/events/DockEventSenderHelperTest.java
@@ -34,12 +34,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.car.dockutil.R;
+import com.android.car.dockutil.Flags;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
@@ -49,6 +51,8 @@
 
 @RunWith(AndroidJUnit4.class)
 public class DockEventSenderHelperTest {
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
     @Mock
     public ActivityManager.RunningTaskInfo mRunningTaskInfo;
     @Mock
@@ -67,7 +71,7 @@
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getResources()).thenReturn(mResources);
-        when(mResources.getBoolean(R.bool.config_enableDock)).thenReturn(true);
+        mSetFlagsRule.enableFlags(Flags.FLAG_DOCK_FEATURE);
         mDockEventSenderHelper = new DockEventSenderHelper(mContext);
     }
 
diff --git a/docklib/Android.bp b/docklib/Android.bp
index 79137d0..8fd6785 100644
--- a/docklib/Android.bp
+++ b/docklib/Android.bp
@@ -17,6 +17,17 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+java_library_static {
+    name: "dock_item",
+    host_supported: true,
+    proto: {
+        type: "lite",
+    },
+    sdk_version: "module_current",
+    min_sdk_version: "31",
+    srcs: ["src/com/android/car/docklib/data/proto/dock_item.proto"],
+}
+
 android_library {
     name: "CarDockLib",
     srcs: [
@@ -30,12 +41,18 @@
 
     static_libs: [
         "androidx.recyclerview_recyclerview",
-        "androidx.core_core-animation-nodeps",
+        "androidx.core_core-animation",
         "car-ui-lib-no-overlayable",
         "androidx.lifecycle_lifecycle-extensions",
         "com.google.android.material_material",
         "CarDockUtilLib",
-        "SystemUISharedLib"
+        "CarLauncherCommon",
+        "SystemUISharedLib",
+        "//frameworks/libs/systemui:iconloader",
+        "car-resource-common",
+        "dock_item",
+        "car_launcher_flags_java_lib",
+        "car-media-common-no-overlayable",
     ],
 
     platform_apis: true,
diff --git a/docklib/AndroidManifest.xml b/docklib/AndroidManifest.xml
index 64ecaf7..c3178e5 100644
--- a/docklib/AndroidManifest.xml
+++ b/docklib/AndroidManifest.xml
@@ -17,6 +17,12 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.car.docklib">
+    <!-- Permission to allow Dock to launch apps -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+    <!-- System permission to query active media sessions -->
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    <!-- System permission to access notifications -->
+    <uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS"/>
 
     <!-- Permission to allow packages to broadcast events to the dock -->
     <permission
diff --git a/docklib/OWNERS b/docklib/OWNERS
index 3cb098d..3a6dfd6 100644
--- a/docklib/OWNERS
+++ b/docklib/OWNERS
@@ -5,5 +5,4 @@
 [email protected]
 [email protected]
 [email protected]
[email protected]
 
diff --git a/docklib/res/layout/dock_view.xml b/docklib/res/layout/dock_view.xml
index d5b61e5..0446685 100644
--- a/docklib/res/layout/dock_view.xml
+++ b/docklib/res/layout/dock_view.xml
@@ -22,5 +22,6 @@
             android:layout_width="match_parent"
             android:layout_height="match_parent"
             android:orientation="horizontal"
+            android:overScrollMode="never"
             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
 </merge>
diff --git a/docklib/res/values-af/strings.xml b/docklib/res/values-af/strings.xml
new file mode 100644
index 0000000..66d922d
--- /dev/null
+++ b/docklib/res/values-af/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dokuitsendingstuurder"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Toestemming word vereis vir pakket om geleenthede na die Dok uit te saai."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dokuitsendingontvanger"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Toestemming word vereis vir pakket om te luister na uitsendinggeleenthede vir die Dok."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Geen plek beskikbaar om vas te speld nie"</string>
+</resources>
diff --git a/docklib/res/values-am/strings.xml b/docklib/res/values-am/strings.xml
new file mode 100644
index 0000000..82a8765
--- /dev/null
+++ b/docklib/res/values-am/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"የመትከያ የስርጭት ላኪ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ጥቅል ክስተቶችን ወደ መትከያ ለማሰራጨት ፈቃድ ያስፈልገዋል።"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"የመትከያ የስርጭት ተቀባይ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"የጥቅል ማዳመጥ ለመትከያ ክስተቶችን ለማሰራጨት ፈቃድ ያስፈልገዋል።"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ፒን ለማድረግ ምንም ቦታ የለም"</string>
+</resources>
diff --git a/docklib/res/values-ar/strings.xml b/docklib/res/values-ar/strings.xml
new file mode 100644
index 0000000..ea3e6ac
--- /dev/null
+++ b/docklib/res/values-ar/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"مرسِل بث قاعدة الإرساء"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"يجب الحصول على إذن لتتمكّن الحزمة من بث الأحداث إلى \"قاعدة الإرساء\"."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"مستقبِل بث قاعدة الإرساء"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"يجب الحصول على إذن بالاستماع لتتمكّن الحزمة من بث الأحداث إلى \"قاعدة الإرساء\"."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ما مِن مكان متاح للتثبيت فيه"</string>
+</resources>
diff --git a/docklib/res/values-as/strings.xml b/docklib/res/values-as/strings.xml
new file mode 100644
index 0000000..6a32e5d
--- /dev/null
+++ b/docklib/res/values-as/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ড’কৰ সম্প্ৰচাৰ প্ৰেৰক"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ড’কলৈ ইভেণ্ট সম্প্ৰচাৰ কৰিবলৈ পেকেজক অনুমতিৰ প্ৰয়োজন।"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ড’কৰ সম্প্ৰচাৰ ৰিচিভাৰ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ড’কটোৰ বাবে অনুষ্ঠান সম্প্ৰচাৰ কৰিবলৈ পেকেজ শুনাৰ অনুমতিৰ আৱশ্যক।"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"পিন কৰিবলৈ কোনো স্থান নাই"</string>
+</resources>
diff --git a/docklib/res/values-az/strings.xml b/docklib/res/values-az/strings.xml
new file mode 100644
index 0000000..4819f03
--- /dev/null
+++ b/docklib/res/values-az/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dok yayımı üçün göndərən"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Paket tədbirlərinin Dokda yayımlanması üçün icazə lazımdır."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dok üzrə yayımı qəbul edən"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Dok üçün tədbirləri yayımlamaqdan ötrü paket dinləmə üçün icazə lazımdır."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Bərkitmək üçün yer yoxdur"</string>
+</resources>
diff --git a/docklib/res/values-b+sr+Latn/strings.xml b/docklib/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..6fbe1c0
--- /dev/null
+++ b/docklib/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Pošiljalac emitovanja za traku s aplikacijama"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Potrebna je dozvola da bi paket emitovao događaje za traku s aplikacijama."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Prijemnik emitovanja za traku s aplikacijama"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Potrebna je dozvola da bi osluškivač paketa emitovao događaje za traku s aplikacijama."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nije dostupno mesto za kačenje"</string>
+</resources>
diff --git a/docklib/res/values-be/strings.xml b/docklib/res/values-be/strings.xml
new file mode 100644
index 0000000..8bed26d
--- /dev/null
+++ b/docklib/res/values-be/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Адпраўшчык трансляцыі паліцы"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Дазвол, які патрабуецца пакету, каб трансліраваць паліцы даныя аб падзеях"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Атрымальнік трансляцыі паліцы"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Дазвол, які патрабуецца пакету, каб чакаць перадачы ад паліцы даных аб падзеях"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Няма свабоднага месца для замацавання"</string>
+</resources>
diff --git a/docklib/res/values-bg/strings.xml b/docklib/res/values-bg/strings.xml
new file mode 100644
index 0000000..0e20d0a
--- /dev/null
+++ b/docklib/res/values-bg/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Подател на съобщение към докинг станцията"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Изисква се разрешение, за да може пакетът да съобщава събития към докинг станцията"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Приемник на излъчвания от докинг станцията"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Изисква се разрешение за слушане на пакета с цел съобщаване на събития към докинг станцията."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Няма налично място за фиксиране"</string>
+</resources>
diff --git a/docklib/res/values-bn/strings.xml b/docklib/res/values-bn/strings.xml
new file mode 100644
index 0000000..b1f0efa
--- /dev/null
+++ b/docklib/res/values-bn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ডক ব্রডকাস্টের প্রেরক"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ডকে ইভেন্ট ব্রডকাস্ট করতে প্যাকেজের জন্য অনুমতি প্রয়োজন।"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ডক ব্রডকাস্টের রিসিভার"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ডকের জন্য ইভেন্ট সম্প্রচার করতে প্যাকেজ শোনার ক্ষেত্রে অনুমতি প্রয়োজন।"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"পিন করার জন্য কোনও স্পট উপলভ্য নেই"</string>
+</resources>
diff --git a/docklib/res/values-bs/strings.xml b/docklib/res/values-bs/strings.xml
new file mode 100644
index 0000000..53f6c83
--- /dev/null
+++ b/docklib/res/values-bs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Pošiljalac emitiranja na priključnoj stanici"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Potrebno je odobrenje da paket emitira događaje na priključnu stanicu."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Prijemnik emitiranog sadržaja na priključnoj stanici"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Potrebno je odobrenje za paketno slušanje radi emitiranja događaja za priključnu stanicu."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nijedno mjesto nije dostupno za kačenje"</string>
+</resources>
diff --git a/docklib/res/values-ca/strings.xml b/docklib/res/values-ca/strings.xml
new file mode 100644
index 0000000..dc3be4e
--- /dev/null
+++ b/docklib/res/values-ca/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Emissor de difusió al connector"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Es necessita permís perquè el paquet emeti esdeveniments al connector."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Receptor d\'emissió al connector"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Es necessita permís perquè el paquet escolti els esdeveniments d\'emissió per al connector."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No hi ha cap lloc disponible per fixar"</string>
+</resources>
diff --git a/docklib/res/values-cs/strings.xml b/docklib/res/values-cs/strings.xml
new file mode 100644
index 0000000..ec575a5
--- /dev/null
+++ b/docklib/res/values-cs/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Odesílatel vysílání do doku"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Aby balíček mohl vysílat události do doku, potřebuje oprávnění."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Příjem oznámení pro dok"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Aby mohl balíček poslouchat události vysílání pro dok, potřebuje oprávnění."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Žádné dostupné místo k připnutí"</string>
+</resources>
diff --git a/docklib/res/values-da/strings.xml b/docklib/res/values-da/strings.xml
new file mode 100644
index 0000000..b32f477
--- /dev/null
+++ b/docklib/res/values-da/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Signalsender i applinjen"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Tilladelse påkræves, før pakken kan sende beskeder om hændelser til applinjen."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Signalmodtager i applinjen"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Tilladelse påkræves, før pakken kan sende beskeder om hændelser til applinjen."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Der er intet sted, hvor du kan fastgøre din app"</string>
+</resources>
diff --git a/docklib/res/values-de/strings.xml b/docklib/res/values-de/strings.xml
new file mode 100644
index 0000000..8d5f8d7
--- /dev/null
+++ b/docklib/res/values-de/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Sender für die Übertragung an das Dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Es ist eine Berechtigung erforderlich, damit das Paket Ereignisse an das Dock übertragen kann."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dock-Übertragungsempfänger"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Es ist eine Berechtigung erforderlich, damit das Paket Ereignisse abrufen und an das Dock übertragen kann."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Kein Platz zum Anpinnen verfügbar"</string>
+</resources>
diff --git a/docklib/res/values-el/strings.xml b/docklib/res/values-el/strings.xml
new file mode 100644
index 0000000..1bce60c
--- /dev/null
+++ b/docklib/res/values-el/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Αποστολέας μετάδοσης γραμμής εργαλείων"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Απαιτείται άδεια προκειμένου το πακέτο να μεταδίδει συμβάντα στη γραμμή εργαλείων."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Δέκτης μετάδοσης γραμμής εργαλείων"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Απαιτείται άδεια προκειμένου το πακέτο να ανιχνεύει τη μετάδοση συμβάντων για τη γραμμή εργαλείων."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Δεν υπάρχει διαθέσιμο σημείο για καρφίτσωμα"</string>
+</resources>
diff --git a/docklib/res/values-en-rAU/strings.xml b/docklib/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..6882693
--- /dev/null
+++ b/docklib/res/values-en-rAU/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dock broadcast sender"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Permission required for package to broadcast events to the dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dock broadcast receiver"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Permission required for package to listen to broadcast events for the dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No spot available to pin"</string>
+</resources>
diff --git a/docklib/res/values-en-rCA/strings.xml b/docklib/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..bafbfda
--- /dev/null
+++ b/docklib/res/values-en-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dock broadcast sender"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Permission required for package to broadcast events to the Dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dock broadcast receiver"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Permission required for package listen to broadcast events for the Dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No spot available to pin"</string>
+</resources>
diff --git a/docklib/res/values-en-rGB/strings.xml b/docklib/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..6882693
--- /dev/null
+++ b/docklib/res/values-en-rGB/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dock broadcast sender"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Permission required for package to broadcast events to the dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dock broadcast receiver"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Permission required for package to listen to broadcast events for the dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No spot available to pin"</string>
+</resources>
diff --git a/docklib/res/values-en-rIN/strings.xml b/docklib/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..6882693
--- /dev/null
+++ b/docklib/res/values-en-rIN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dock broadcast sender"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Permission required for package to broadcast events to the dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dock broadcast receiver"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Permission required for package to listen to broadcast events for the dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No spot available to pin"</string>
+</resources>
diff --git a/docklib/res/values-en-rXC/strings.xml b/docklib/res/values-en-rXC/strings.xml
index a48b2a2..d88ef4a 100644
--- a/docklib/res/values-en-rXC/strings.xml
+++ b/docklib/res/values-en-rXC/strings.xml
@@ -21,4 +21,5 @@
     <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‎‎‏‏‎Permission required for package to broadcast events to the Dock.‎‏‎‎‏‎"</string>
     <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‎‎‏‎‏‎‎Dock broadcast receiver‎‏‎‎‏‎"</string>
     <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‎‏‏‎Permission required for package listen to broadcast events for the Dock.‎‏‎‎‏‎"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‎‏‏‎‏‎‏‎‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‎‎‏‏‏‎‏‏‎‎No spot available to pin‎‏‎‎‏‎"</string>
 </resources>
diff --git a/docklib/res/values-es-rUS/strings.xml b/docklib/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..8d2bde2
--- /dev/null
+++ b/docklib/res/values-es-rUS/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Remitente de la transmisión del conector"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Se requiere permiso para que el paquete transmita eventos al conector."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Receptor de transmisiones del conector"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Se requiere permiso para que el paquete escuche los eventos de transmisión para el conector."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No hay una ubicación disponible para fijar"</string>
+</resources>
diff --git a/docklib/res/values-es/strings.xml b/docklib/res/values-es/strings.xml
new file mode 100644
index 0000000..46d11c1
--- /dev/null
+++ b/docklib/res/values-es/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Emisor de las aplicaciones ancladas"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Se necesita permiso para que el paquete emita eventos a las aplicaciones ancladas."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Receptor de emisión de las aplicaciones ancladas"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Se necesita permiso para que el paquete escuche los eventos de emisión para las aplicaciones ancladas."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"No hay espacio disponible para fijar"</string>
+</resources>
diff --git a/docklib/res/values-et/strings.xml b/docklib/res/values-et/strings.xml
new file mode 100644
index 0000000..a884cc4
--- /dev/null
+++ b/docklib/res/values-et/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Doki ülekande saatja"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Pakett vajab sündmuste dokki edastamiseks luba."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Doki ülekande vastuvõtja"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Pakett vajab sündmuste kuulamiseks doki jaoks edastamise luba."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Kinnitamiseks pole kohta saadaval"</string>
+</resources>
diff --git a/docklib/res/values-eu/strings.xml b/docklib/res/values-eu/strings.xml
new file mode 100644
index 0000000..0f4de1f
--- /dev/null
+++ b/docklib/res/values-eu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Oinarrira igortzeko igorlea"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Paketeak oinarrira gertaerak igortzeko baimena behar du."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Oinarriaren igorpen-hargailua"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Entzuteko paketeak oinarrira gertaerak igortzeko baimena behar du."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Ez dago ainguratzeko tokirik erabilgarri"</string>
+</resources>
diff --git a/docklib/res/values-fa/strings.xml b/docklib/res/values-fa/strings.xml
new file mode 100644
index 0000000..c16d50b
--- /dev/null
+++ b/docklib/res/values-fa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"فرستنده رویداد ثبت‌شده در «پایه اتصال»"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"برای اینکه بسته رویدادها را به «پایه اتصال» همه‌فرستی کند به اجازه نیاز است."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"گیرنده رویداد ثبت‌شده در «پایه اتصال»"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"برای اینکه بسته رویدادها را برای «پایه اتصال» همه‌فرستی کند به اجازه نیاز است."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"فضایی برای سنجاق کردن وجود ندارد"</string>
+</resources>
diff --git a/docklib/res/values-fi/strings.xml b/docklib/res/values-fi/strings.xml
new file mode 100644
index 0000000..2f0e89a
--- /dev/null
+++ b/docklib/res/values-fi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Telineen lähetyksen lähde"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Tarvitaan lupa, jotta paketti voi lähettää tapahtumia telineeseen."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Telineen lähetysvastaanotin"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Paketti tarvitsee luvan kuunteluun, jotta se voi lähettää tapahtumia telineeseen."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Ei kohtaa, johon sovelluksen voi kiinnittää"</string>
+</resources>
diff --git a/docklib/res/values-fr-rCA/strings.xml b/docklib/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..a578384
--- /dev/null
+++ b/docklib/res/values-fr-rCA/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Émetteur de diffusion vers la barre d\'applications"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Autorisation requise pour le paquet afin de diffuser des événements sur la barre d\'applications."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Récepteur de diffusion vers la barre d\'applications"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Autorisation requise pour le paquet afin de diffuser des événements sur la barre d\'applications."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Pas de place libre pour l\'épinglage"</string>
+</resources>
diff --git a/docklib/res/values-fr/strings.xml b/docklib/res/values-fr/strings.xml
new file mode 100644
index 0000000..0110ef9
--- /dev/null
+++ b/docklib/res/values-fr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Expéditeur de diffusion sur la barre des applications"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Autorisation requise pour le package afin de diffuser des événements sur la barre des applications."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver sur la barre des applications"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Autorisation requise pour que le paquet puisse écouter les événements diffusés pour la barre des applications."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Aucun espace disponible pour épingler"</string>
+</resources>
diff --git a/docklib/res/values-gl/strings.xml b/docklib/res/values-gl/strings.xml
new file mode 100644
index 0000000..71a235a
--- /dev/null
+++ b/docklib/res/values-gl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Emisor de difusións da barra de aplicacións"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Requírese permiso para que o paquete poida difundir eventos á barra de aplicacións."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Receptor de difusións da barra de aplicacións"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Requírese permiso para que o paquete poida escoitar co fin de difundir eventos á barra de aplicacións."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Non hai ningún oco para fixar o contido"</string>
+</resources>
diff --git a/docklib/res/values-gu/strings.xml b/docklib/res/values-gu/strings.xml
new file mode 100644
index 0000000..f7b5bd6
--- /dev/null
+++ b/docklib/res/values-gu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ડૉક પર બ્રોડકાસ્ટ મોકલનાર"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ડૉક પર ઇવેન્ટ બ્રોડકાસ્ટ કરવા, પૅકેજ માટે પરવાનગી આવશ્યક છે."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ડૉક પર બ્રોડકાસ્ટ પ્રાપ્તકર્તા"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ડૉક પર ઇવેન્ટ બ્રોડકાસ્ટ કરવા, પૅકેજ સાંભળવા માટેની પરવાનગી આવશ્યક છે."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"પિન કરવા માટે, કોઈ સ્પૉટ ઉપલબ્ધ નથી"</string>
+</resources>
diff --git a/docklib/res/values-hi/strings.xml b/docklib/res/values-hi/strings.xml
new file mode 100644
index 0000000..a186467
--- /dev/null
+++ b/docklib/res/values-hi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"डॉक ब्रॉडकास्ट सेंडर"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"डॉक पर इवेंट ब्रॉडकास्ट करने के लिए, पैकेज को अनुमति चाहिए."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"डॉक ब्रॉडकास्ट रिसीवर"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"डॉक पर इवेंट ब्रॉडकास्ट करने के लिए, पैकेज को अनुमति चाहिए."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"पिन करने के लिए कोई जगह नहीं है"</string>
+</resources>
diff --git a/docklib/res/values-hr/strings.xml b/docklib/res/values-hr/strings.xml
new file mode 100644
index 0000000..758523d
--- /dev/null
+++ b/docklib/res/values-hr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Pošiljatelj emitiranja na priključnu stanicu"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Potrebno je dopuštenje da paket emitira događaje na priključnu stanicu."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Prijamnik emitiranja za priključnu stanicu"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Dopuštenje za slušanje paketa za emitiranje događaja za priključnu stanicu."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nema dostupnog mjesta za prikvačiti"</string>
+</resources>
diff --git a/docklib/res/values-hu/strings.xml b/docklib/res/values-hu/strings.xml
new file mode 100644
index 0000000..700d6e1
--- /dev/null
+++ b/docklib/res/values-hu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dokk-közvetítés forrása"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Engedély szükséges a csomag számára, hogy eseményeket közvetíthessen a dokkra."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dokk broadcast receivere"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Engedély szükséges a csomag számára, hogy figyelhesse a dokkhoz kapcsolódó közvetítési eseményeket."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nincs rendelkezésre álló hely a kitűzéshez"</string>
+</resources>
diff --git a/docklib/res/values-hy/strings.xml b/docklib/res/values-hy/strings.xml
new file mode 100644
index 0000000..ed058e1
--- /dev/null
+++ b/docklib/res/values-hy/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Դոկ-կայանին բովանդակություն հեռարձակող"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Թույլտվություն, որն անհրաժեշտ է իրադարձությունների մասին տեղեկությունները դոկ-կայանին հեռարձակելու համար։"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Դոկ-կայանինից հեռարձակման ընդունիչ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Թույլտվություն, որն անհրաժեշտ է իրադարձությունների մասին՝ դոկ-կայանից հեռարձակվող տեղեկությունները լսելու համար։"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Ամրացնելու համար տեղ չկա"</string>
+</resources>
diff --git a/docklib/res/values-in/strings.xml b/docklib/res/values-in/strings.xml
new file mode 100644
index 0000000..729865d
--- /dev/null
+++ b/docklib/res/values-in/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Pengirim siaran Dok"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Izin diperlukan agar paket dapat menyiarkan acara ke Dok."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Penerima siaran dok"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Izin diperlukan agar paket dapat memproses siaran acara untuk Dok."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Tidak ada tempat yang tersedia untuk menyematkan"</string>
+</resources>
diff --git a/docklib/res/values-is/strings.xml b/docklib/res/values-is/strings.xml
new file mode 100644
index 0000000..597930c
--- /dev/null
+++ b/docklib/res/values-is/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Útsendir dokku"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Heimildar er krafist svo pakkinn geti sent út viðburði í dokku."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Móttakari útsendinga á dokku"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Heimildar er krafist svo pakkinn geti hlustað á útsenda viðburði í dokku."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Enginn staður tiltækur til að festa"</string>
+</resources>
diff --git a/docklib/res/values-it/strings.xml b/docklib/res/values-it/strings.xml
new file mode 100644
index 0000000..31ee89c
--- /dev/null
+++ b/docklib/res/values-it/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Broadcast sender del dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Autorizzazione necessaria per consentire al pacchetto di trasmettere eventi al dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver del dock"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Autorizzazione necessaria per l\'ascolto del pacchetto al fine di trasmettere eventi per il dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Non c\'è posto per fissare"</string>
+</resources>
diff --git a/docklib/res/values-iw/strings.xml b/docklib/res/values-iw/strings.xml
new file mode 100644
index 0000000..4599c73
--- /dev/null
+++ b/docklib/res/values-iw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ספק השידורים למגש האפליקציות"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"נדרשת הרשאה כדי שהחבילה תוכל לשדר אירועים למגש האפליקציות."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"מקלט השידורים של מגש האפליקציות"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"נדרשת הרשאה כדי שהחבילה תוכל לקלוט אירועים שישודרו למגש האפליקציות."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"אין מקום פנוי להצמדה"</string>
+</resources>
diff --git a/docklib/res/values-ja/strings.xml b/docklib/res/values-ja/strings.xml
new file mode 100644
index 0000000..e983846
--- /dev/null
+++ b/docklib/res/values-ja/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ホルダー ブロードキャスト送信側"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"イベントをホルダーにブロードキャストする権限がパッケージに必要です"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ホルダー ブロードキャスト受信側"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ホルダーのブロードキャスト イベントをリッスンする権限がパッケージに必要です"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"固定できる場所がありません"</string>
+</resources>
diff --git a/docklib/res/values-ka/strings.xml b/docklib/res/values-ka/strings.xml
new file mode 100644
index 0000000..05116e7
--- /dev/null
+++ b/docklib/res/values-ka/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"სამაგრის მაუწყებლობის გამგზავნი"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"პაკეტისთვის საჭიროა ნებართვა სამაგრში ღონისძიებების ტრანსლაციისთვის."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"სამაგრის მაუწყებლობათა მიმღები"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"პაკეტისთვის საჭიროა ნებართვა სამაგრში ღონისძიებების ტრანსლაციისთვის."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ჩასამაგრებელი ადგილი მიუწვდომელია"</string>
+</resources>
diff --git a/docklib/res/values-kk/strings.xml b/docklib/res/values-kk/strings.xml
new file mode 100644
index 0000000..605c2d1
--- /dev/null
+++ b/docklib/res/values-kk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Қондыру станциясының таратушысы"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Іс-шараларды қондыру станциясына тарату үшін пакетке рұқсат қажет."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Қондыру станциясының қабылдағышы"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Қондыру станциясындағы іс-шаралар жайлы ақпарат алу үшін пакетке рұқсат қажет."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Бекіту үшін ешқандай орын жоқ."</string>
+</resources>
diff --git a/docklib/res/values-km/strings.xml b/docklib/res/values-km/strings.xml
new file mode 100644
index 0000000..b6fbe0d
--- /dev/null
+++ b/docklib/res/values-km/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"កម្មវិធីបញ្ជូនការផ្សាយរបស់ធ្នើរ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ការអនុញ្ញាតដែលតម្រូវឱ្យមានសម្រាប់កញ្ចប់ ដើម្បីផ្សាយព្រឹត្តិការណ៍ទៅធ្នើរ។"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"កម្មវិធី​ទទួលការផ្សាយរបស់ធ្នើរ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ការអនុញ្ញាតដែលតម្រូវឱ្យមានសម្រាប់ការស្ដាប់កញ្ចប់ ដើម្បីផ្សាយព្រឹត្តិការណ៍សម្រាប់ធ្នើរ។"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"មិនមានកន្លែងដែលអាចខ្ទាស់បានទេ"</string>
+</resources>
diff --git a/docklib/res/values-kn/strings.xml b/docklib/res/values-kn/strings.xml
new file mode 100644
index 0000000..f26435b
--- /dev/null
+++ b/docklib/res/values-kn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ಡಾಕ್ ಪ್ರಸಾರವನ್ನು ಕಳುಹಿಸಿದವರು"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ಈವೆಂಟ್‌ಗಳನ್ನು ಡಾಕ್‌ಗೆ ಪ್ರಸಾರ ಮಾಡಲು ಪ್ಯಾಕೇಜ್‌ಗೆ ಅನುಮತಿಯ ಅಗತ್ಯವಿದೆ."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ಡಾಕ್ ಪ್ರಸಾರ ಸ್ವೀಕರಿಸುವವರು"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ಈವೆಂಟ್‌ಗಳನ್ನು ಡಾಕ್‌ಗೆ ಪ್ರಸಾರ ಮಾಡಲು ಪ್ಯಾಕೇಜ್ ಆಲಿಸುವಿಕೆಗೆ ಅನುಮತಿಯ ಅಗತ್ಯವಿದೆ."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ಪಿನ್ ಮಾಡಲು ಯಾವುದೇ ಸ್ಪಾಟ್ ಲಭ್ಯವಿಲ್ಲ"</string>
+</resources>
diff --git a/docklib/res/values-ko/strings.xml b/docklib/res/values-ko/strings.xml
new file mode 100644
index 0000000..97067b4
--- /dev/null
+++ b/docklib/res/values-ko/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"broadcast receiver 도킹"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"패키지에서 이벤트를 도크로 브로드캐스트하려면 권한이 필요합니다."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"broadcast receiver 도킹"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"패키지에서 도크를 위한 이벤트를 브로드캐스트하려면 리슨 권한이 필요합니다."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"고정할 수 있는 공간 없음"</string>
+</resources>
diff --git a/docklib/res/values-ky/strings.xml b/docklib/res/values-ky/strings.xml
new file mode 100644
index 0000000..cd65459
--- /dev/null
+++ b/docklib/res/values-ky/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Док бекетке кабарды жөнөтүүчү"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Иш-чараларды Док бекетке кабарлоо үчүн таңгакка уруксат талап кылынат."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Док бекеттеги кең диапазондогу кабыл алгыч"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Иш-чараларды Док бекетте угуу үчүн таңгакка уруксат талап кылынат."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Кадап коюу үчүн бош жер жок"</string>
+</resources>
diff --git a/docklib/res/values-lo/strings.xml b/docklib/res/values-lo/strings.xml
new file mode 100644
index 0000000..345a58d
--- /dev/null
+++ b/docklib/res/values-lo/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ຕົວສົ່ງປະກາດໄປຫາດັອກ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ຕ້ອງມີການອະນຸຍາດສຳລັບແພັກເກດເພື່ອປະກາດເຫດການໄປຫາດັອກ."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ຕົວຮັບປະກາດຈາກດັອກ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ຕ້ອງມີການອະນຸຍາດສຳລັບການຟັງແພັກເກດເພື່ອປະກາດເຫດການສຳລັບດັອກ."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ບໍ່ມີບ່ອນໃຫ້ປັກໝຸດ"</string>
+</resources>
diff --git a/docklib/res/values-lt/strings.xml b/docklib/res/values-lt/strings.xml
new file mode 100644
index 0000000..872a83a
--- /dev/null
+++ b/docklib/res/values-lt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Prisegtų programų juostos transliacijos siuntėjas"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Paketui reikalingas leidimas transliuoti įvykius į prisegtų programų juostą."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Prisegtų programų juostos transliacijos gavėjas"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Paketui reikalingas leidimas priimti transliacijos įvykius, skirtus prisegtų programų juostai."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nėra vietos, kad būtų galima prisegti"</string>
+</resources>
diff --git a/docklib/res/values-lv/strings.xml b/docklib/res/values-lv/strings.xml
new file mode 100644
index 0000000..df54fdd
--- /dev/null
+++ b/docklib/res/values-lv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Doka apraides sūtītājs"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Pakotnei ir nepieciešama atļauja, lai varētu apraidīt notikumus uz doku."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Doka apraides uztvērējs"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Pakotnei ir nepieciešama klausīšanās atļauja, lai varētu apraidīt notikumus uz doku."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nav brīvas vietas, kur piespraust"</string>
+</resources>
diff --git a/docklib/res/values-mk/strings.xml b/docklib/res/values-mk/strings.xml
new file mode 100644
index 0000000..868bcbe
--- /dev/null
+++ b/docklib/res/values-mk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Испраќач за емитувања на Dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Потребна е дозвола за пакетот да емитува настани на Dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Приемник за емитување на Dock"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Потребна е дозвола за пакетот за слушање да емитува настани за Dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Нема достапно место за закачување"</string>
+</resources>
diff --git a/docklib/res/values-ml/strings.xml b/docklib/res/values-ml/strings.xml
new file mode 100644
index 0000000..476c444
--- /dev/null
+++ b/docklib/res/values-ml/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ഡോക്ക് ബ്രോഡ്‌കാസ്റ്റ് സെൻഡർ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ഡോക്കിലേക്ക് ഇവന്റുകൾ ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യാൻ പാക്കേജിന് അനുമതി ആവശ്യമാണ്."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ഡോക്ക് ബ്രോഡ്‌കാസ്‌റ്റ് റിസീവർ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ഡോക്കിലേക്ക് ഇവന്റുകൾ ബ്രോഡ്‌കാസ്റ്റ് ചെയ്യുന്നതിന്, പാക്കേജ് കേൾക്കൽ അനുമതി ആവശ്യമാണ്."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"പിൻ ചെയ്യാൻ സ്‌‌പോട്ട് ഒന്നും ലഭ്യമല്ല"</string>
+</resources>
diff --git a/docklib/res/values-mn/strings.xml b/docklib/res/values-mn/strings.xml
new file mode 100644
index 0000000..c0bbb0c
--- /dev/null
+++ b/docklib/res/values-mn/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Суурилуулагчийн дамжуулалт илгээгч"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Суурилуулагч руу үйл явдлуудыг дамжуулахад багцад шаардлагатай зөвшөөрөл."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Суурилуулагчийн дамжуулалт хүлээн авагч"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Суурилуулагчид үйл явдлуудыг дамжуулахад багцад шаардлагатай зөвшөөрөл."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Бэхлэх зай байхгүй байна"</string>
+</resources>
diff --git a/docklib/res/values-mr/strings.xml b/docklib/res/values-mr/strings.xml
new file mode 100644
index 0000000..35bc2f5
--- /dev/null
+++ b/docklib/res/values-mr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"डॉक ब्रॉडकास्ट पाठवणारा"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"डॉक वर इव्हेंट ब्रॉडकास्ट करण्यासाठी पॅकेजला परवानगी हवी आहे."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"डॉक ब्रॉडकास्ट प्राप्‍तकर्ता"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"डॉक साठीचे ब्रॉडकास्ट इव्हेंट ऐकण्याकरिता पॅकेजला परवानगी हवी आहे."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"पिन करण्यासाठी जागा उपलब्ध नाही"</string>
+</resources>
diff --git a/docklib/res/values-ms/strings.xml b/docklib/res/values-ms/strings.xml
new file mode 100644
index 0000000..72d0826
--- /dev/null
+++ b/docklib/res/values-ms/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Pengirim siaran dok"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Kebenaran diperlukan untuk membolehkan pakej menyiarkan acara pada Dok."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Penerima siaran dok"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Kebenaran diperlukan bagi pendengaran pakej untuk menyiarkan acara bagi Dok."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Tiada tempat yang tersedia untuk disemat"</string>
+</resources>
diff --git a/docklib/res/values-my/strings.xml b/docklib/res/values-my/strings.xml
new file mode 100644
index 0000000..9c53da2
--- /dev/null
+++ b/docklib/res/values-my/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"အထိုင်ဆိုင်ရာ ထုတ်လွှင့်မှုပို့သောစနစ်"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"‘အထိုင်’ သို့ အစီအစဉ်များ ထုတ်လွှင့်ရန် ပက်ကေ့ဂျ်အတွက် လိုအပ်သည့် ခွင့်ပြုချက်။"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"အထိုင်ဆိုင်ရာ အသံလွှင့်ခြင်းကို လက်ခံသည့်စနစ်"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"အထိုင်တွင် ထုတ်လွှင့်မှုအစီအစဉ်များ နားထောင်ရန် ပက်ကေ့ဂျ်အတွက် လိုအပ်သည့် ခွင့်ပြုချက်။"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ပင်ထိုးရန် နေရာမရှိပါ"</string>
+</resources>
diff --git a/docklib/res/values-nb/strings.xml b/docklib/res/values-nb/strings.xml
new file mode 100644
index 0000000..ac2e0d3
--- /dev/null
+++ b/docklib/res/values-nb/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Sender for kringkasting til dokken"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Tillatelse som kreves for at pakker skal kunne kringkaste hendelser til dokken."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Mottaker for kringkasting til dokken"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Tillatelse som kreves for at pakker skal kunne lytte etter kringkastede hendelser for dokken."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Det finnes ingen steder der appen kan festes"</string>
+</resources>
diff --git a/docklib/res/values-ne/strings.xml b/docklib/res/values-ne/strings.xml
new file mode 100644
index 0000000..2470c03
--- /dev/null
+++ b/docklib/res/values-ne/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"डक ब्रोडकास्ट सेन्डर"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"डकमा गतिविधिसम्बन्धी सूचनाहरू पठाउन प्याकेजलाई अनुमति चाहिन्छ।"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"डक ब्रोडकास्ट रिसिभर"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"डकले पठाएका गतिविधिसम्बन्धी सूचनाहरू प्राप्त गर्न प्याकेजलाई अनुमति चाहिन्छ।"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"पिन गर्नका लागि कुनै पनि ठाउँ छैन"</string>
+</resources>
diff --git a/docklib/res/values-nl/strings.xml b/docklib/res/values-nl/strings.xml
new file mode 100644
index 0000000..74d18c6
--- /dev/null
+++ b/docklib/res/values-nl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Transmissieverzender voor dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Toestemming nodig voordat pakket gebeurtenissen kan uitzenden naar het dockstation."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dockontvanger"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Toestemming nodig voordat pakket kan luisteren naar transmissiegebeurtenissen voor het dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Geen plaats beschikbaar voor vastzetten"</string>
+</resources>
diff --git a/docklib/res/values-or/strings.xml b/docklib/res/values-or/strings.xml
new file mode 100644
index 0000000..5760ab1
--- /dev/null
+++ b/docklib/res/values-or/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ଡକ ବ୍ରଡକାଷ୍ଟ ପ୍ରେରକ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ଡକରେ ଇଭେଣ୍ଟଗୁଡ଼ିକ ବ୍ରଡକାଷ୍ଟ କରିବା ପାଇଁ ପେକେଜକୁ ଅନୁମତିର ଆବଶ୍ୟକତା ଅଛି।"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ଡକ ବ୍ରଡକାଷ୍ଟ ରିସିଭର"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ଡକ ପାଇଁ ଇଭେଣ୍ଟଗୁଡ଼ିକ ବ୍ରଡକାଷ୍ଟ କରିବାକୁ ଶୁଣିବା ନିମନ୍ତେ ପେକେଜକୁ ଅନୁମତିର ଆବଶ୍ୟକତା ଅଛି।"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ପିନ କରିବାକୁ କୌଣସି ସ୍ଥାନ ଉପଲବ୍ଧ ନାହିଁ"</string>
+</resources>
diff --git a/docklib/res/values-pa/strings.xml b/docklib/res/values-pa/strings.xml
new file mode 100644
index 0000000..c8228d4
--- /dev/null
+++ b/docklib/res/values-pa/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ਡੌਕ ਪ੍ਰਸਾਰਨ ਭੇਜਣ ਵਾਲਾ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ਡੌਕ \'ਤੇ ਇਵੈਂਟਾਂ ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਨ ਲਈ ਪੈਕੇਜ ਵਾਸਤੇ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ।"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ਡੌਕ broadcast receiver"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ਡੌਕ \'ਤੇ ਇਵੈਂਟਾਂ ਦਾ ਪ੍ਰਸਾਰਨ ਕਰਨ ਲਈ ਪੈਕੇਜ ਵਾਸਤੇ ਇਜਾਜ਼ਤ ਦੀ ਲੋੜ ਹੈ।"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ਪਿੰਨ ਕਰਨ ਲਈ ਕੋਈ ਥਾਂ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
+</resources>
diff --git a/docklib/res/values-pl/strings.xml b/docklib/res/values-pl/strings.xml
new file mode 100644
index 0000000..d4ce0f9
--- /dev/null
+++ b/docklib/res/values-pl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Moduł wysyłający transmisję na stację dokującą"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Pakiet potrzebuje pozwolenia na transmitowanie wydarzeń na stacji dokującej."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Odbiornik stacji dokującej"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Pakiet potrzebuje pozwolenia na odtwarzanie transmitowanych wydarzeń w przypadku stacji dokującej."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Brak miejsca do przypięcia"</string>
+</resources>
diff --git a/docklib/res/values-pt-rPT/strings.xml b/docklib/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..1841bf5
--- /dev/null
+++ b/docklib/res/values-pt-rPT/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Remetente de transmissão da estação de ancoragem"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"O pacote precisa de autorização para transmitir eventos para a estação de ancoragem."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Recetor de transmissão da estação de ancoragem"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"O pacote precisa de autorização para ouvir eventos de transmissão para a estação de ancoragem."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nenhum local disponível para afixar"</string>
+</resources>
diff --git a/docklib/res/values-pt/strings.xml b/docklib/res/values-pt/strings.xml
new file mode 100644
index 0000000..613f06a
--- /dev/null
+++ b/docklib/res/values-pt/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Remetente da transmissão para a base"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"O pacote precisa de permissão para transmitir eventos para a base."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver da base"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"O pacote precisa de permissão para ouvir eventos de transmissão para a base."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nenhum espaço disponível para fixar"</string>
+</resources>
diff --git a/docklib/res/values-ro/strings.xml b/docklib/res/values-ro/strings.xml
new file mode 100644
index 0000000..9b603a7
--- /dev/null
+++ b/docklib/res/values-ro/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Broadcast sender pentru bara de activități"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Permisiune necesară pentru ca pachetul să transmită evenimente către bara de activități."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver pentru bara de activități"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Permisiune necesară pentru ca pachetul să asculte evenimente transmise pentru bara de activități."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nu există un loc disponibil de fixat"</string>
+</resources>
diff --git a/docklib/res/values-ru/strings.xml b/docklib/res/values-ru/strings.xml
new file mode 100644
index 0000000..95a7268
--- /dev/null
+++ b/docklib/res/values-ru/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Широковещательный передатчик для док-станции"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Чтобы отправлять информацию о событиях на док-станцию, передатчику требуется разрешение."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Широковещательный приемник для док-станции"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Чтобы получать информацию о событиях на док-станции, приемнику требуется разрешение."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Нет места"</string>
+</resources>
diff --git a/docklib/res/values-si/strings.xml b/docklib/res/values-si/strings.xml
new file mode 100644
index 0000000..1d3cc91
--- /dev/null
+++ b/docklib/res/values-si/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ඩොක් විකාශන යවන්නා"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ඩොකය වෙත සිදුවීම් විකාශනය කිරීමට පැකේජය සඳහා අවසරය අවශ්‍යයි."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ඩොක් විකාශන ග්‍රාහකය"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ඩොකය සඳහා විකාශන සිදුවීම් වෙත සවන්දීම සඳහා අවසරය අවශ්‍යයි."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ඇමිණීමට ස්ථානයක් නොමැත"</string>
+</resources>
diff --git a/docklib/res/values-sk/strings.xml b/docklib/res/values-sk/strings.xml
new file mode 100644
index 0000000..16db20f
--- /dev/null
+++ b/docklib/res/values-sk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Broadcast sender pre dok"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Na to, aby balíček mohol vysielať udalosti do doku, potrebuje povolenie."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver pre dok"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Na to, aby balíček mohol počúvať udalosti vysielania do doku, potrebuje povolenie."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Žiadne dostupné miesto na pripnutie"</string>
+</resources>
diff --git a/docklib/res/values-sl/strings.xml b/docklib/res/values-sl/strings.xml
new file mode 100644
index 0000000..41d7f7e
--- /dev/null
+++ b/docklib/res/values-sl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Orodje za objavljanje v vrstici z aplikacijami"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Paket potrebuje dovoljenje za objavljanje dogodkov v vrstici z aplikacijami."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Sprejemnik za objavljanje v vrstici z aplikacijami"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Poslušanje paketov potrebuje dovoljenje za objavljanje dogodkov za vrstico z aplikacijami."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Ni mesta za pripenjanje"</string>
+</resources>
diff --git a/docklib/res/values-sq/strings.xml b/docklib/res/values-sq/strings.xml
new file mode 100644
index 0000000..870fff2
--- /dev/null
+++ b/docklib/res/values-sq/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dërguesi i transmetimit të \"Stacionit\""</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Kërkohet leje që paketa të transmetojë ngjarjet te \"Stacioni\"."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Marrësi i transmetimit të \"Stacionit\""</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Kërkohet leje që paketa të dëgjojë ngjarjet e transmetimit për \"Stacionin\"."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Nuk ka vend për të gozhduar"</string>
+</resources>
diff --git a/docklib/res/values-sr/strings.xml b/docklib/res/values-sr/strings.xml
new file mode 100644
index 0000000..5a1457a
--- /dev/null
+++ b/docklib/res/values-sr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Пошиљалац емитовања за траку с апликацијама"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Потребна је дозвола да би пакет емитовао догађаје за траку с апликацијама."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Пријемник емитовања за траку с апликацијама"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Потребна је дозвола да би ослушкивач пакета емитовао догађаје за траку с апликацијама."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Није доступно место за качење"</string>
+</resources>
diff --git a/docklib/res/values-sv/strings.xml b/docklib/res/values-sv/strings.xml
new file mode 100644
index 0000000..42f8c6f
--- /dev/null
+++ b/docklib/res/values-sv/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Utsändare till appraden"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Paketet behöver behörighet för att sända ut händelser till appraden."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Sändningsmottagare för appraden"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Behörighet krävs för att paketet ska kunna ta emot utsända händelser för appraden"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Det finns ingen plats att fästa"</string>
+</resources>
diff --git a/docklib/res/values-sw/strings.xml b/docklib/res/values-sw/strings.xml
new file mode 100644
index 0000000..f9588d1
--- /dev/null
+++ b/docklib/res/values-sw/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Mtumaji wa arifa ya Dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Ruhusa inahitajika ili kifurushi kitoe arifa za matukio kwenye Dock."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Kipokeaji cha matangazo ya Dock"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Inahitaji ruhusa ya kipengele cha kusikiliza kifurushi ili kusambaza matukio ya Dock."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Hakuna nafasi ya kubandika"</string>
+</resources>
diff --git a/docklib/res/values-ta/strings.xml b/docklib/res/values-ta/strings.xml
new file mode 100644
index 0000000..f90c400
--- /dev/null
+++ b/docklib/res/values-ta/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"டாக் பிராட்காஸ்ட் அனுப்புநர்"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"டாக்கில் நிகழ்வுகளை பிராட்காஸ்ட் செய்ய பேக்கேஜிற்கு அனுமதி தேவை."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"டாக் பிராட்காஸ்ட் பெறுநர்"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"டாக்கில் நிகழ்வுகளை பிராட்காஸ்ட் செய்ய பேக்கேஜ் லிஸனிற்கு அனுமதி தேவை."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"பின் செய்ய எதுவுமில்லை"</string>
+</resources>
diff --git a/docklib/res/values-te/strings.xml b/docklib/res/values-te/strings.xml
new file mode 100644
index 0000000..5e0beb0
--- /dev/null
+++ b/docklib/res/values-te/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"డాక్ ప్రసారం పంపే వారు"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"డాక్‌కి ఈవెంట్‌లను ప్రసారం చేయడానికి ప్యాకేజీకి అనుమతి అవసరం."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"డాక్ ప్రసార రిసీవర్"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"డాక్ చేయడం కోసం ప్రసార ఈవెంట్‌లను వినడానికి ప్యాకేజీకి అనుమతి అవసరం."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"పిన్ చేయడానికి స్పాట్ లేదు"</string>
+</resources>
diff --git a/docklib/res/values-th/strings.xml b/docklib/res/values-th/strings.xml
new file mode 100644
index 0000000..2cbe132
--- /dev/null
+++ b/docklib/res/values-th/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Broadcast Sender ของ Dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ต้องมีสิทธิ์สำหรับแพ็กเกจเพื่อประกาศเหตุการณ์ไปยัง Dock"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast Receiver ของ Dock"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"ต้องมีสิทธิ์สำหรับแพ็กเกจเพื่อฟังประกาศเหตุการณ์ที่ Dock"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"ไม่มีพื้นที่ว่างให้ปักหมุด"</string>
+</resources>
diff --git a/docklib/res/values-tl/strings.xml b/docklib/res/values-tl/strings.xml
new file mode 100644
index 0000000..0fe93b2
--- /dev/null
+++ b/docklib/res/values-tl/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Sender ng broadcast sa Dock"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Kinakailangan ang pahintulot para makapag-broadcast ng mga event sa Dock ang package."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Tagatanggap ng broadcast ng Dock"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Kinakailangan ang pahintulot para makarinig sa broadcast ng mga event sa Dock ang package."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Walang lugar na available para i-pin"</string>
+</resources>
diff --git a/docklib/res/values-tr/strings.xml b/docklib/res/values-tr/strings.xml
new file mode 100644
index 0000000..783298b
--- /dev/null
+++ b/docklib/res/values-tr/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Yuva yayını gönderici"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Etkinlikleri Yuvaya yayınlama paketi için izin gerekiyor."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Yuva yayını alıcı"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Paketin Yuva ile ilgili yayınlanan etkinlikleri dinlemesi için izin gerekiyor."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Sabitlemek için yer yok"</string>
+</resources>
diff --git a/docklib/res/values-uk/strings.xml b/docklib/res/values-uk/strings.xml
new file mode 100644
index 0000000..93724f8
--- /dev/null
+++ b/docklib/res/values-uk/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Відправник широкомовної розсилки на закріплену панель"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Пакету потрібен дозвіл, щоб здійснювати широкомовну розсилку подій на закріплену панель."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Приймач широкомовної розсилки на закріпленій панелі"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Пакету потрібен дозвіл, щоб слухати широкомовну розсилку подій на закріпленій панелі."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Немає місця, щоб закріпити"</string>
+</resources>
diff --git a/docklib/res/values-ur/strings.xml b/docklib/res/values-ur/strings.xml
new file mode 100644
index 0000000..ce2bbcf
--- /dev/null
+++ b/docklib/res/values-ur/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"ڈاک براڈ کاسٹ ارسال کنندہ"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"ڈاک پر ایونٹس کو براڈ کاسٹ کرنے کی خاطر پیکیج کیلئے اجازت درکار ہے۔"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"ڈاک براڈکاسٹ وصول کنندہ"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"پیکیج کے لیے اجازت درکار ہے ڈاک کے لیے براڈ کاسٹ ایونٹس سنیں۔"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"پن کرنے کے لیے کوئی جگہ دستیاب نہیں ہے"</string>
+</resources>
diff --git a/docklib/res/values-uz/strings.xml b/docklib/res/values-uz/strings.xml
new file mode 100644
index 0000000..0f23cbc
--- /dev/null
+++ b/docklib/res/values-uz/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dok-stansiyaga maʼlumotlarni yuboruvchi"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Hodisa haqidagi maʼlumotlarni dok-stansiyaga yuborish uchun ilova ruxsat talab qiladi"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dok translatsiya resiveri"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Dokka hodisalarni uzatishda paket tinglash uchun ruxsat talab etiladi."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Mahkamlash uchun joy qolmagan"</string>
+</resources>
diff --git a/docklib/res/values-vi/strings.xml b/docklib/res/values-vi/strings.xml
new file mode 100644
index 0000000..5a3f807
--- /dev/null
+++ b/docklib/res/values-vi/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Broadcast sender của thanh Dock ứng dụng"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Cần có quyền để gói ứng dụng truyền sự kiện lên thanh Dock ứng dụng."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Broadcast receiver của thanh Dock ứng dụng"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Cần có quyền để gói ứng dụng nghe được sự kiện truyền lên thanh Dock ứng dụng."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Không còn chỗ nào để ghim"</string>
+</resources>
diff --git a/docklib/res/values-zh-rCN/strings.xml b/docklib/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..7d3df20
--- /dev/null
+++ b/docklib/res/values-zh-rCN/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"基座广播发送器"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"您必须授予权限,软件包才能将事件广播到基座。"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"基座广播接收器"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"您必须授予权限,软件包监听才能广播针对基座的事件。"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"没有可固定的位置"</string>
+</resources>
diff --git a/docklib/res/values-zh-rHK/strings.xml b/docklib/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..81b428c
--- /dev/null
+++ b/docklib/res/values-zh-rHK/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"捷徑列廣播傳送器"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"檔案包必須獲得權限,才能在捷徑列廣播活動。"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"捷徑列廣播接收器"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"檔案包必須獲得權限,才能在捷徑列廣播活動。"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"沒有可用的位置固定"</string>
+</resources>
diff --git a/docklib/res/values-zh-rTW/strings.xml b/docklib/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..9ad0f02
--- /dev/null
+++ b/docklib/res/values-zh-rTW/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"工具列廣播傳送器"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"必須授予權限,套件才能將事件播送到工具列。"</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"工具列廣播接收器"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"必須授予權限,套件監聽才能播送工具列的事件。"</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"沒有可以固定的地點"</string>
+</resources>
diff --git a/docklib/res/values-zu/strings.xml b/docklib/res/values-zu/strings.xml
new file mode 100644
index 0000000..b07de97
--- /dev/null
+++ b/docklib/res/values-zu/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2023 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="broadcast_sender_permission_label" msgid="5269973644784898827">"Dokha umthumeli wokusakaza"</string>
+    <string name="broadcast_sender_permission_desc" msgid="5052882219053515363">"Kudingeka imvume yephakheji yokusakaza imicimbi Kudokhi."</string>
+    <string name="broadcast_receiver_permission_label" msgid="6015991948761587466">"Dokha isamukeli somsakazi"</string>
+    <string name="broadcast_receiver_permission_desc" msgid="1623002370607914795">"Kudingeka imvume yephakheji yokulalela imicimbi yokusakaza Yedokhi."</string>
+    <string name="pin_failed_no_spots" msgid="745687732976464502">"Ayikho indawo etholakalayo yokuphina"</string>
+</resources>
diff --git a/docklib/res/values/colors.xml b/docklib/res/values/colors.xml
index fd2f30e..ea791c6 100644
--- a/docklib/res/values/colors.xml
+++ b/docklib/res/values/colors.xml
@@ -14,6 +14,9 @@
 limitations under the License.
 -->
 <resources>
-    <color name="icon_default_stroke_color">#FFFFFF</color>
-    <color name="icon_excited_stroke_color">#737272</color>
+    <!-- todo(b/314859977): reset color to #000000 -->
+    <color name="icon_default_color">#FFFFFF</color>
+    <color name="icon_static_stroke_color">#000000</color>
+    <color name="icon_excited_stroke_color">@*android:color/car_grey_900</color>
+    <color name="icon_restricted_stroke_color">#69696952</color>
 </resources>
diff --git a/docklib/res/values/config.xml b/docklib/res/values/config.xml
index 2c355fd..e026e7f 100644
--- a/docklib/res/values/config.xml
+++ b/docklib/res/values/config.xml
@@ -21,22 +21,35 @@
 
     <!-- A list of components that are shown on Dock by default -->
     <string-array name="config_defaultDockApps" translatable="false">
-        <item>com.android.vending/com.google.android.finsky.carmainactivity.MainActivity</item>
         <item>com.android.car.settings/com.android.car.settings.Settings_Launcher_Homepage</item>
+        <item>com.android.car.radio/com.android.car.radio.service.RadioAppService</item>
         <item>com.android.car.dialer/com.android.car.dialer.ui.TelecomActivity</item>
-        <item>com.google.android.apps.maps/com.google.android.maps.MapsActivity</item>
     </string-array>
 
     <!-- A list of components that are excluded from being shown on Dock -->
     <string-array name="config_packagesExcludedFromDock" translatable="false">
         <item>com.android.car.carlauncher</item>
+        <item>android.car.usb.handler</item>
     </string-array>
 
     <!-- A list of components that are excluded from being shown on Dock -->
     <string-array name="config_componentsExcludedFromDock" translatable="false">
         <item>com.google.android.apps.maps/com.google.android.apps.gmm.car.embedded.activity.LimitedMapsActivity</item>
         <item>com.google.android.carassistant/com.google.android.libraries.assistant.auto.tng.assistant.ui.activity.AutoAssistantActivity</item>
+        <item>com.android.car.media/com.android.car.media.MediaDispatcherActivity</item>
+        <item>com.android.car.media/com.android.car.media.MediaBlockingActivity</item>
+        <item>com.android.systemui/com.android.systemui.car.wm.activity.LaunchOnPrivateDisplayRouterActivity</item>
+        <item>com.android.car.settings/com.android.car.settings.sound.AudioRouteSelectionActivity</item>
     </string-array>
 
-    <integer name="drag_drop_animate_in_duration">800</integer>
+    <!--
+    During drag and drop, when an item is dropped there are two consecutive animations:
+    1. move to the proper location and to a scaled down version (scale down)
+    2. change from the scaled down version to the final scale version (scale up)
+    -->
+    <integer name="drop_animation_scale_down_duration_ms">400</integer>
+    <integer name="drop_animation_scale_up_duration_ms">250</integer>
+
+    <!-- Duration of animation for when item is dragged over the drop location. -->
+    <integer name="excite_icon_animation_duration_ms">100</integer>
 </resources>
diff --git a/docklib/res/values/dimens.xml b/docklib/res/values/dimens.xml
index d38260f..0013f19 100644
--- a/docklib/res/values/dimens.xml
+++ b/docklib/res/values/dimens.xml
@@ -18,11 +18,14 @@
 <resources>
 
     <!-- Default size and spacing for an icon on the Dock  -->
-    <dimen name="dock_item_size">72dp</dimen>
-    <dimen name="dock_item_spacing">2dp</dimen>
+    <dimen name="dock_item_size">64dp</dimen>
 
-    <dimen name="static_icon_stroke_width">0dp</dimen>
-    <dimen name="dynamic_icon_stroke_width">6dp</dimen>
-    <dimen name="icon_stroke_width_excited">30dp</dimen>
+    <dimen name="icon_stroke_width_static">0dp</dimen>
+    <dimen name="icon_stroke_width_dynamic">8dp</dimen>
+    <dimen name="icon_stroke_width_excited">24dp</dimen>
 
+    <item name="icon_colorFilter_alpha_excited" format="float" type="dimen">0.2</item>
+
+    <!-- Width to scale down the drop animation before scaling it back to required size  -->
+    <dimen name="drop_animation_scale_down_width">6dp</dimen>
 </resources>
diff --git a/docklib/res/values/strings.xml b/docklib/res/values/strings.xml
index c3100a5..95aaa1b 100644
--- a/docklib/res/values/strings.xml
+++ b/docklib/res/values/strings.xml
@@ -24,4 +24,5 @@
     <string name="broadcast_receiver_permission_desc">
         Permission required for package listen to broadcast events for the Dock.
     </string>
+    <string name="pin_failed_no_spots">No spot available to pin</string>
 </resources>
diff --git a/docklib/res/values/styles.xml b/docklib/res/values/styles.xml
index 8fee4a8..24f400e 100644
--- a/docklib/res/values/styles.xml
+++ b/docklib/res/values/styles.xml
@@ -17,13 +17,14 @@
     <style name="ItemContainer">
         <item name="android:layout_width">wrap_content</item>
         <item name="android:layout_height">match_parent</item>
-        <item name="android:paddingStart">15dp</item>
-        <item name="android:paddingEnd">15dp</item>
+        <item name="android:paddingStart">10dp</item>
+        <item name="android:paddingEnd">10dp</item>
     </style>
 
     <style name="AppIcon">
         <item name="android:layout_width">@dimen/dock_item_size</item>
         <item name="android:layout_height">@dimen/dock_item_size</item>
+        <item name="android:layout_gravity">center</item>
         <item name="android:clipToOutline">true</item>
         <item name="android:scaleType">centerCrop</item>
         <item name="android:adjustViewBounds">false</item>
diff --git a/docklib/src/com/android/car/docklib/DockHelper.kt b/docklib/src/com/android/car/docklib/DockHelper.kt
deleted file mode 100644
index 152a413..0000000
--- a/docklib/src/com/android/car/docklib/DockHelper.kt
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2023 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.docklib
-
-import android.car.content.pm.CarPackageManager
-import android.content.ComponentName
-import android.content.Context
-import android.content.pm.PackageManager
-import com.android.car.docklib.data.DockAppItem
-
-/**
- * Helper that reads configs for defaults app to be showed on Dock and converts between
- * ComponentName and DockItem
- */
-class DockHelper(
-    private val context: Context,
-    private val carPackageManager: CarPackageManager,
-) {
-    private val packageManager: PackageManager = context.packageManager
-    val defaultApps by lazy {
-        val defaultComponents = context.resources.getStringArray(R.array.config_defaultDockApps)
-        defaultComponents.mapNotNull { component ->
-            val componentName = ComponentName.unflattenFromString(component)
-            componentName?.let { toDockAppItem(componentName) }
-        }
-    }
-    val excludedComponents by lazy {
-        HashSet<String>().apply {
-            addAll(context.resources.getStringArray(R.array.config_componentsExcludedFromDock))
-        }
-    }
-    val excludedPackages by lazy {
-        HashSet<String>().apply {
-            addAll(context.resources.getStringArray(R.array.config_packagesExcludedFromDock))
-        }
-    }
-
-    /* Convert to Dock item from a ComponentName. */
-    fun toDockAppItem(
-        componentName: ComponentName,
-        itemType: DockAppItem.Type = DockAppItem.Type.DYNAMIC
-    ): DockAppItem {
-        // TODO: Compare the component against LauncherApps to make sure the component
-        // is launchable, similar to what app grid has
-        val icon = packageManager.getApplicationIcon(componentName.packageName)
-        val name = packageManager.getActivityInfo(componentName, 0).name
-        return DockAppItem(
-            itemType,
-            componentName,
-            name,
-            icon,
-            carPackageManager.isActivityDistractionOptimized(
-                componentName.packageName,
-                componentName.className
-            )
-        )
-    }
-}
diff --git a/docklib/src/com/android/car/docklib/DockInterface.kt b/docklib/src/com/android/car/docklib/DockInterface.kt
index 0959099..0509e78 100644
--- a/docklib/src/com/android/car/docklib/DockInterface.kt
+++ b/docklib/src/com/android/car/docklib/DockInterface.kt
@@ -17,14 +17,46 @@
 package com.android.car.docklib
 
 import android.content.ComponentName
+import com.android.car.docklib.data.DockItemId
+import java.util.UUID
 
 interface DockInterface {
-    /** called when an app is statically pinned to the Dock */
+    /** called when an app is pinned to the Dock */
     fun appPinned(componentName: ComponentName)
 
+    /** called when an app is pinned to the Dock at a particular position */
+    fun appPinned(componentName: ComponentName, index: Int)
+
+    /** called when an app already in the dock is pinned */
+    fun appPinned(@DockItemId id: UUID)
+
+    /** called when an app already in the dock is unpinned */
+    fun appUnpinned(componentName: ComponentName)
+
+    /** called when an app already in the dock is unpinned */
+    fun appUnpinned(@DockItemId id: UUID)
+
     /** called when an app is launched */
     fun appLaunched(componentName: ComponentName)
 
-    /** called when an app should be removed from the Dock */
-    fun appUnpinned(componentName: ComponentName)
+    /**
+     * called when an app is uninstalled/removed from the system or is inaccessible in the dock.
+     * @param packageName packageName of removed package
+     */
+    fun packageRemoved(packageName: String)
+
+    /**
+     * called when an app is installed in the system or is enabled in the dock.
+     * @param packageName packageName of removed package
+     */
+    fun packageAdded(packageName: String)
+
+    /** called to launch an app */
+    fun launchApp(componentName: ComponentName, isMediaApp: Boolean)
+
+    /** @return the dominant color to be used with the icon corresponding to [componentName] */
+    fun getIconColorWithScrim(componentName: ComponentName): Int
+
+    /** get the set of all media service components */
+    fun getMediaServiceComponents(): Set<ComponentName>
 }
diff --git a/docklib/src/com/android/car/docklib/DockViewController.kt b/docklib/src/com/android/car/docklib/DockViewController.kt
index 8285253..a3b179e 100644
--- a/docklib/src/com/android/car/docklib/DockViewController.kt
+++ b/docklib/src/com/android/car/docklib/DockViewController.kt
@@ -16,107 +16,253 @@
 
 package com.android.car.docklib
 
+import android.annotation.CallSuper
+import android.app.ActivityOptions
+import android.app.NotificationManager
 import android.car.Car
 import android.car.content.pm.CarPackageManager
+import android.car.drivingstate.CarUxRestrictionsManager
+import android.car.media.CarMediaManager
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
+import android.content.pm.LauncherApps
+import android.media.session.MediaController
+import android.media.session.MediaSessionManager
+import android.media.session.PlaybackState
 import android.os.Build
+import android.os.RemoteException
+import android.os.UserHandle
 import android.util.Log
+import androidx.core.content.getSystemService
+import com.android.car.carlauncher.Flags
+import com.android.car.docklib.data.DockProtoDataController
 import com.android.car.docklib.events.DockEventsReceiver
+import com.android.car.docklib.events.DockPackageChangeReceiver
+import com.android.car.docklib.media.MediaUtils
 import com.android.car.docklib.task.DockTaskStackChangeListener
 import com.android.car.docklib.view.DockAdapter
 import com.android.car.docklib.view.DockView
+import com.android.launcher3.icons.IconFactory
 import com.android.systemui.shared.system.TaskStackChangeListeners
+import java.io.File
 import java.lang.ref.WeakReference
-import java.util.function.Consumer
+import java.util.UUID
 
 /**
  * Create a controller for DockView. It initializes the view with default and persisted icons. Upon
  * initializing, it will listen to broadcast events, and update the view.
  *
- * @param userContext the foreground user context, since the view may be hosted on system context
  * @param dockView the inflated dock view
- * @param intentDelegate the system context will need to handle clicks and actions on the icons
+ * @param userContext the foreground user context, since the view may be hosted on system context
+ * @param dataFile a file to store user's pinned apps with read and write permission
  */
-class DockViewController(
-    private val userContext: Context,
-    dockView: DockView,
-    intentDelegate: Consumer<Intent>
+open class DockViewController(
+        dockView: DockView,
+        private val userContext: Context = dockView.context,
+        dataFile: File,
 ) : DockInterface {
-    private companion object {
+    companion object {
         private const val TAG = "DockViewController"
         private val DEBUG = Build.isDebuggable()
     }
 
-    private val numItems: Int
+    private val numItems = dockView.context.resources.getInteger(R.integer.config_numDockApps)
     private val car: Car
     private val dockViewWeakReference: WeakReference<DockView>
     private val dockViewModel: DockViewModel
-    private var dockHelper: DockHelper? = null
+    private val adapter: DockAdapter
     private val dockEventsReceiver: DockEventsReceiver
+    private val dockPackageChangeReceiver: DockPackageChangeReceiver
     private val taskStackChangeListeners: TaskStackChangeListeners
     private val dockTaskStackChangeListener: DockTaskStackChangeListener
+    private val launcherApps = userContext.getSystemService<LauncherApps>()
+    private val excludedItemsProviders: Set<ExcludedItemsProvider> =
+        hashSetOf(ResourceExcludedItemsProvider(userContext))
+    private val mediaSessionManager: MediaSessionManager
+    private val sessionChangedListener: MediaSessionManager.OnActiveSessionsChangedListener =
+        MediaSessionManager.OnActiveSessionsChangedListener { mediaControllers ->
+            handleMediaSessionChange(mediaControllers)
+        }
 
     init {
-        numItems = userContext.resources.getInteger(R.integer.config_numDockApps)
-        val adapter = DockAdapter(numItems, intentDelegate, userContext)
+        if (DEBUG) Log.d(TAG, "Init DockViewController for user ${userContext.userId}")
+        adapter = DockAdapter(this, userContext)
         dockView.setAdapter(adapter)
         dockViewWeakReference = WeakReference(dockView)
-        dockViewModel = DockViewModel(numItems) { updatedApps ->
-            dockViewWeakReference.get()?.getAdapter()?.setItems(updatedApps)
-                ?: throw NullPointerException("the View referenced does not exist")
+
+        val launcherActivities = launcherApps
+                ?.getActivityList(null, userContext.user)
+                ?.map { it.componentName }
+                ?.toMutableSet() ?: mutableSetOf()
+
+        dockViewModel = DockViewModel(
+                maxItemsInDock = numItems,
+                context = userContext,
+                packageManager = userContext.packageManager,
+                launcherActivities = launcherActivities,
+                defaultPinnedItems = dockView.resources
+                        .getStringArray(R.array.config_defaultDockApps)
+                        .mapNotNull(ComponentName::unflattenFromString),
+                isPackageExcluded = { pkg ->
+                    getExcludedItemsProviders()
+                            .map { it.isPackageExcluded(pkg) }
+                            .reduce { res1, res2 -> res1 or res2 }
+                },
+                isComponentExcluded = { cmp ->
+                    getExcludedItemsProviders()
+                            .map { it.isComponentExcluded(cmp) }
+                            .reduce { res1, res2 -> res1 or res2 }
+                },
+                iconFactory = IconFactory.obtain(dockView.context),
+                dockProtoDataController = DockProtoDataController(dataFile),
+        ) { updatedApps ->
+            dockViewWeakReference.get()?.getAdapter()?.submitList(updatedApps)
+                    ?: throw NullPointerException("the View referenced does not exist")
         }
-        car =
-            Car.createCar(
+        car = Car.createCar(
                 userContext,
                 null, // handler
                 Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT
-            ) { car, ready ->
-                run {
-                    if (ready) {
-                        val carPackageManager = car.getCarManager(CarPackageManager::class.java)
-                        carPackageManager?.let { carPM ->
-                            adapter.setCarPackageManager(carPM)
-                            // todo(b/314859963): create the DockHelper without depending on carPM
-                            dockHelper = DockHelper(userContext, carPM)
-                            dockHelper?.let { dockViewModel.updateDefaultApps(it.defaultApps) }
+        ) { car, ready ->
+            run {
+                if (ready) {
+                    car.getCarManager(CarPackageManager::class.java)?.let { carPM ->
+                        dockViewModel.setCarPackageManager(carPM)
+                    }
+                    car.getCarManager(CarMediaManager::class.java)?.let { carMM ->
+                        adapter.setCarMediaManager(carMM)
+                    }
+                    car.getCarManager(CarUxRestrictionsManager::class.java)?.let {
+                        adapter.setUxRestrictions(
+                            isUxRestrictionEnabled =
+                            it.currentCarUxRestrictions?.isRequiresDistractionOptimization ?: false
+                        )
+                        it.registerListener { carUxRestrictions ->
+                            adapter.setUxRestrictions(
+                                isUxRestrictionEnabled =
+                                carUxRestrictions.isRequiresDistractionOptimization
+                            )
                         }
                     }
                 }
             }
+        }
+
+        mediaSessionManager =
+            userContext.getSystemService(MediaSessionManager::class.java) as MediaSessionManager
+        if (Flags.mediaSessionCard()) {
+            handleMediaSessionChange(mediaSessionManager.getActiveSessionsForUser(
+                /* notificationListener= */
+                null,
+                UserHandle.of(userContext.userId)
+            ))
+            mediaSessionManager.addOnActiveSessionsChangedListener(
+                /* notificationListener= */
+                null,
+                UserHandle.of(userContext.userId),
+                userContext.getMainExecutor(),
+                sessionChangedListener
+            )
+        }
+
         dockEventsReceiver = DockEventsReceiver.registerDockReceiver(userContext, this)
-        dockTaskStackChangeListener = DockTaskStackChangeListener { appLaunched(it) }
+        dockPackageChangeReceiver = DockPackageChangeReceiver.registerReceiver(userContext, this)
+        dockTaskStackChangeListener =
+                DockTaskStackChangeListener(userContext.userId, this)
         taskStackChangeListeners = TaskStackChangeListeners.getInstance()
         taskStackChangeListeners.registerTaskStackListener(dockTaskStackChangeListener)
     }
 
     /** Method to stop the dock. Call this upon View being destroyed. */
-    fun destroy() {
+    @CallSuper
+    open fun destroy() {
         if (DEBUG) Log.d(TAG, "Destroy called")
+        car.getCarManager(CarUxRestrictionsManager::class.java)?.unregisterListener()
         car.disconnect()
         userContext.unregisterReceiver(dockEventsReceiver)
-        dockViewModel.destroy()
+        userContext.unregisterReceiver(dockPackageChangeReceiver)
         taskStackChangeListeners.unregisterTaskStackListener(dockTaskStackChangeListener)
+        mediaSessionManager.removeOnActiveSessionsChangedListener(sessionChangedListener)
+        dockViewModel.destroy()
     }
 
-    override fun appPinned(componentName: ComponentName) {
-        // TODO("Not yet implemented")
-    }
+    open fun getExcludedItemsProviders(): Set<ExcludedItemsProvider> = excludedItemsProviders
 
-    override fun appLaunched(componentName: ComponentName) {
-        if (DEBUG) Log.d(TAG, "App launched: $componentName")
-        dockHelper?.let {
-            if (it.excludedPackages.contains(componentName.packageName)) return
-            if (it.excludedComponents.contains(componentName.flattenToString())) return
+    override fun appPinned(componentName: ComponentName) = dockViewModel.pinItem(componentName)
 
-            val appItem = it.toDockAppItem(componentName)
-            if (DEBUG) Log.d(TAG, "Dynamic app add to dock: $appItem")
-            dockViewModel.addDynamicItem(appItem)
-        }
-    }
+    override fun appPinned(componentName: ComponentName, index: Int) =
+            dockViewModel.pinItem(componentName, index)
+
+    override fun appPinned(id: UUID) = dockViewModel.pinItem(id)
 
     override fun appUnpinned(componentName: ComponentName) {
-        // TODO("Not yet implemented")
+        // TODO: Not yet implemented
+    }
+
+    override fun appUnpinned(id: UUID) = dockViewModel.removeItem(id)
+
+    override fun appLaunched(componentName: ComponentName) =
+            dockViewModel.addDynamicItem(componentName)
+
+    override fun launchApp(componentName: ComponentName, isMediaApp: Boolean) {
+        val intent = if (isMediaApp) {
+            MediaUtils.createLaunchIntent(componentName)
+        } else {
+            Intent(Intent.ACTION_MAIN)
+                .setComponent(componentName)
+                .addCategory(Intent.CATEGORY_LAUNCHER)
+                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        }
+        val options = ActivityOptions.makeBasic()
+        options.setLaunchDisplayId(userContext.display.displayId)
+        // todo(b/312718542): hidden api(context.startActivityAsUser) usage
+        userContext.startActivityAsUser(intent, options.toBundle(), userContext.user)
+    }
+
+    override fun getIconColorWithScrim(componentName: ComponentName) =
+            dockViewModel.getIconColorWithScrim(componentName)
+
+    override fun packageRemoved(packageName: String) = dockViewModel.removeItems(packageName)
+
+    override fun packageAdded(packageName: String) {
+        dockViewModel.addMediaComponents(packageName)
+        dockViewModel.addLauncherComponents(
+            launcherApps?.getActivityList(packageName, userContext.user)
+                ?.map { it.componentName } ?: listOf()
+        )
+    }
+
+    override fun getMediaServiceComponents(): Set<ComponentName> =
+        dockViewModel.getMediaServiceComponents()
+
+    private fun handleMediaSessionChange(mediaControllers: List<MediaController>?) {
+        val mediaNotificationPackages = getActiveMediaNotificationPackages()
+        val activeMediaSessions = mediaControllers?.filter {
+            it.playbackState?.let { playbackState ->
+                (playbackState.isActive || playbackState.state == PlaybackState.STATE_PAUSED)
+            } ?: false
+        }?.map { it.packageName }?.filter { mediaNotificationPackages.contains(it) } ?: emptyList()
+
+        adapter.onMediaSessionChange(activeMediaSessions)
+    }
+
+    private fun getActiveMediaNotificationPackages(): List<String> {
+        try {
+            // todo(b/312718542): hidden api(NotificationManager.getService()) usage
+            return NotificationManager.getService()
+                .getActiveNotificationsWithAttribution(
+                    userContext.packageName,
+                    null
+                ).toList().filter {
+                    it.notification.extras != null && it.notification.isMediaNotification
+                }.map { it.packageName }
+        } catch (e: RemoteException) {
+            Log.e(
+                TAG,
+                "Exception trying to get active notifications $e"
+            )
+            return listOf()
+        }
     }
 }
diff --git a/docklib/src/com/android/car/docklib/DockViewModel.kt b/docklib/src/com/android/car/docklib/DockViewModel.kt
index 8037ee0..c169c18 100644
--- a/docklib/src/com/android/car/docklib/DockViewModel.kt
+++ b/docklib/src/com/android/car/docklib/DockViewModel.kt
@@ -16,69 +16,349 @@
 
 package com.android.car.docklib
 
+import android.app.ActivityManager
+import android.app.ActivityTaskManager
+import android.car.content.pm.CarPackageManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageItemInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.util.Log
+import android.view.Display
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.Observer
 import com.android.car.docklib.data.DockAppItem
+import com.android.car.docklib.data.DockItemId
+import com.android.car.docklib.data.DockProtoDataController
+import com.android.car.docklib.media.MediaUtils
+import com.android.car.docklib.task.TaskUtils
+import com.android.launcher3.icons.BaseIconFactory
+import com.android.launcher3.icons.ColorExtractor
+import com.android.launcher3.icons.IconFactory
+import java.util.Collections
+import java.util.UUID
 
 /**
  * This class contains a live list of dock app items. All changes to dock items will go through it
  * and will be observed by the view layer.
  */
-class DockViewModel(private val numItems: Int, private val observer: Observer<List<DockAppItem?>>) {
-    private val currentItems = MutableLiveData<List<DockAppItem?>>()
+open class DockViewModel(
+        private val maxItemsInDock: Int,
+        private val context: Context,
+        private val packageManager: PackageManager,
+        private var carPackageManager: CarPackageManager? = null,
+        private val userId: Int = context.userId,
+        private var launcherActivities: MutableSet<ComponentName>,
+        defaultPinnedItems: List<ComponentName>,
+        private val isPackageExcluded: (pkg: String) -> Boolean,
+        private val isComponentExcluded: (component: ComponentName) -> Boolean,
+        private val iconFactory: IconFactory = IconFactory.obtain(context),
+        private val dockProtoDataController: DockProtoDataController,
+        private val observer: Observer<List<DockAppItem>>,
+) {
 
-    /* Maintain a mapping of dock index to dock item, with the order of addition,
+    private companion object {
+        private const val TAG = "DockViewModel"
+        private val DEBUG = Build.isDebuggable()
+        private const val MAX_UNIQUE_ID_TRIES = 20
+        private const val MAX_TASKS_TO_FETCH = 20
+    }
+
+    private val noSpotAvailableToPinToastMsg = context.getString(R.string.pin_failed_no_spots)
+    private val colorExtractor = ColorExtractor()
+    private val defaultIconColor = context.resources.getColor(
+            R.color.icon_default_color,
+            null // theme
+    )
+    private val currentItems = MutableLiveData<List<DockAppItem>>()
+    private val mediaServiceComponents = MediaUtils.fetchMediaServiceComponents(packageManager)
+
+    /*
+     * Maintain a mapping of dock index to dock item, with the order of addition,
      * so it's easier to find least recently updated position.
      * The order goes from least recently updated item to most recently updated item.
      * The key in each mapping is the index/position of the item being shown in Dock.
      */
-    private val internalItems = LinkedHashMap<Int, DockAppItem>()
+    @VisibleForTesting
+    val internalItems: MutableMap<Int, DockAppItem> =
+            Collections.synchronizedMap(LinkedHashMap<Int, DockAppItem>())
 
     init {
-        currentItems.value = List(numItems) { null }
+        initializeDockItems(defaultPinnedItems)
+        currentItems.value = createDockList()
         currentItems.observeForever(observer)
     }
 
-    /** Update default apps if the list is not populated */
-    fun updateDefaultApps(defaultApps: List<DockAppItem>) {
-        synchronized(internalItems) {
-            if (internalItems.size >= numItems) return
-            defaultApps.forEachIndexed { index, defaultAppItem ->
-                if (!internalItems.containsKey(index) && index < numItems) {
-                    // change to default app if the position is not populated
-                    internalItems[index] = defaultAppItem
+    private fun initializeDockItems(defaultPinnedItems: List<ComponentName>) {
+        dockProtoDataController.loadFromFile()?.let { savedPinnedDockItems ->
+            if (DEBUG) Log.d(TAG, "Initialized using saved items")
+            savedPinnedDockItems.forEach { (index, component) ->
+                createDockItem(component, DockAppItem.Type.STATIC, isMediaApp(component))?.let {
+                    internalItems[index] = it
                 }
             }
-            currentItems.value = convertMapToList(internalItems)
+        } ?: run {
+            if (DEBUG) Log.d(TAG, "Initialized using default items")
+            for (index in 0..<minOf(maxItemsInDock, defaultPinnedItems.size)) {
+                createDockItem(
+                    defaultPinnedItems[index],
+                    DockAppItem.Type.STATIC,
+                    isMediaApp(defaultPinnedItems[index])
+                )?.let {
+                    internalItems[index] = it
+                }
+            }
         }
     }
 
+    /** Pin an existing dock item with given [id]. It is assumed the item is not pinned/static. */
+    fun pinItem(@DockItemId id: UUID) {
+        if (DEBUG) Log.d(TAG, "Pin Item, id: $id")
+        internalItems
+                .filter { mapEntry -> mapEntry.value.id == id }
+                .firstNotNullOfOrNull { it }
+                ?.let { mapEntry ->
+                    if (DEBUG) {
+                        Log.d(TAG, "Pinning ${mapEntry.value.component} at ${mapEntry.key}")
+                    }
+                    internalItems[mapEntry.key] =
+                            mapEntry.value.copy(type = DockAppItem.Type.STATIC)
+                }
+        // update list regardless to update the listeners
+        currentItems.value = createDockList()
+        savePinnedItemsToProto()
+    }
+
+    /**
+     * Pin a new item that is not previously present in the dock. It is assumed the item is not
+     * pinned/static.
+     *
+     * @param component [ComponentName] of the pinned item.
+     * @param indexToPin the index to pin the item at. For null value, a suitable index is searched
+     * to pin to. If no index is suitable the user is notified.
+     */
+    fun pinItem(component: ComponentName, indexToPin: Int? = null) {
+        if (DEBUG) Log.d(TAG, "Pin Item, component: $component, indexToPin: $indexToPin")
+        createDockItem(
+            component,
+            DockAppItem.Type.STATIC,
+            isMediaApp(component)
+        )?.let { dockItem ->
+            if (indexToPin != null) {
+                if (indexToPin in 0..<maxItemsInDock) {
+                    if (DEBUG) Log.d(TAG, "Pinning $component at $indexToPin")
+                    internalItems[indexToPin] = dockItem
+                } else {
+                    if (DEBUG) Log.d(TAG, "Invalid index provided")
+                }
+            } else {
+                val index = findIndexToPin()
+                if (index == null) {
+                    if (DEBUG) Log.d(TAG, "No dynamic or empty spots available to pin")
+                    // if no dynamic or empty spots available, notify the user
+                    showToast(noSpotAvailableToPinToastMsg)
+                    return@pinItem
+                }
+                if (DEBUG) Log.d(TAG, "Pinning $component at $index")
+                internalItems[index] = dockItem
+            }
+        }
+        // update list regardless to update the listeners
+        currentItems.value = createDockList()
+        savePinnedItemsToProto()
+    }
+
+    /** Removes item with the given [id] from the dock. */
+    fun removeItem(id: UUID) {
+        if (DEBUG) Log.d(TAG, "Unpin Item, id: $id")
+        internalItems
+                .filter { mapEntry -> mapEntry.value.id == id }
+                .firstNotNullOfOrNull { it }
+                ?.let { mapEntry ->
+                    if (DEBUG) {
+                        Log.d(TAG, "Unpinning ${mapEntry.value.component} at ${mapEntry.key}")
+                    }
+                    internalItems.remove(mapEntry.key)
+                }
+        // update list regardless to update the listeners
+        currentItems.value = createDockList()
+        savePinnedItemsToProto()
+    }
+
+    /** Removes all items of the given [packageName] from the dock. */
+    fun removeItems(packageName: String) {
+        internalItems.entries.removeAll { it.value.component.packageName == packageName }
+        val areMediaComponentsRemoved =
+            mediaServiceComponents.removeIf { it.packageName == packageName }
+        if (areMediaComponentsRemoved && DEBUG) {
+            Log.d(TAG, "Media components were removed for $packageName")
+        }
+        launcherActivities.removeAll { it.packageName == packageName }
+        currentItems.value = createDockList()
+        savePinnedItemsToProto()
+    }
+
+    /** Adds all media service components for the given [packageName]. */
+    fun addMediaComponents(packageName: String) {
+        val components = MediaUtils.fetchMediaServiceComponents(packageManager, packageName)
+        if (DEBUG) Log.d(TAG, "Added media components: $components")
+        mediaServiceComponents.addAll(components)
+    }
+
+    /** Adds all launcher components. */
+    fun addLauncherComponents(components: List<ComponentName>) {
+        launcherActivities.addAll(components)
+    }
+
+    fun getMediaServiceComponents(): Set<ComponentName> = mediaServiceComponents
+
     /**
      * Add a new app to the dock. If the app is already in the dock, the recency of the app is
      * refreshed. If not, and the dock has dynamic item(s) to update, then it will replace the least
      * recent dynamic item.
      */
-    fun addDynamicItem(appItem: DockAppItem) {
-        synchronized(internalItems) {
-            val indexToUpdate =
-                indexOfItemWithPackageName(appItem.component.packageName)
-                    ?: indexOfLeastRecentDynamicItemInDock()
-
-            indexToUpdate?.let {
-                internalItems.remove(it)
-                internalItems[it] = appItem
-                currentItems.value = convertMapToList(internalItems)
-            }
+    fun addDynamicItem(component: ComponentName) {
+        if (DEBUG) Log.d(TAG, "Add dynamic item, component: $component")
+        if (isItemExcluded(component)) {
+            if (DEBUG) Log.d(TAG, "Dynamic item is excluded")
+            return
         }
+        if (isItemInDock(component, DockAppItem.Type.STATIC)) {
+            if (DEBUG) Log.d(TAG, "Dynamic item is already present in the dock as static item")
+            return
+        }
+        val indexToUpdate =
+                indexOfItemWithPackageName(component.packageName)
+                        ?: indexOfLeastRecentDynamicItemInDock()
+        if (indexToUpdate == null || indexToUpdate >= maxItemsInDock) return
+
+        createDockItem(
+            component,
+            DockAppItem.Type.DYNAMIC,
+            isMediaApp(component)
+        )?.let { newDockItem ->
+            if (DEBUG) Log.d(TAG, "Updating $component at $indexToUpdate")
+            internalItems.remove(indexToUpdate)
+            internalItems[indexToUpdate] = newDockItem
+            currentItems.value = createDockList()
+        }
+    }
+
+    fun getIconColorWithScrim(componentName: ComponentName): Int {
+        return DockAppItem.getIconColorWithScrim(getIconColor(componentName))
     }
 
     fun destroy() {
         currentItems.removeObserver(observer)
     }
 
+    fun setCarPackageManager(carPackageManager: CarPackageManager) {
+        this.carPackageManager = carPackageManager
+        internalItems.forEach { mapEntry ->
+            val item = mapEntry.value
+            internalItems[mapEntry.key] = item.copy(
+                    isDistractionOptimized = item.isMediaApp ||
+                            carPackageManager.isActivityDistractionOptimized(
+                                    item.component.packageName,
+                                    item.component.className
+                            )
+            )
+        }
+        currentItems.value = createDockList()
+    }
+
+    @VisibleForTesting
+    fun createDockList(): List<DockAppItem> {
+        if (DEBUG) Log.d(TAG, "createDockList called")
+        // todo(b/312718542): hidden api(ActivityTaskManager.getTasks) usage
+        val runningTaskList = getRunningTasks().filter { it.userId == userId }
+
+        for (index in 0..<maxItemsInDock) {
+            if (internalItems.contains(index)) continue
+
+            var isItemFound = false
+            for (component in runningTaskList.mapNotNull { TaskUtils.getComponentName(it) }) {
+                if (!isItemExcluded(component) && !isItemInDock(component)) {
+                    createDockItem(
+                        component,
+                        DockAppItem.Type.DYNAMIC,
+                        isMediaApp(component)
+                    )?.let { dockItem ->
+                        if (DEBUG) {
+                            Log.d(TAG, "Adding recent item(${dockItem.component}) at $index")
+                        }
+                        internalItems[index] = dockItem
+                        isItemFound = true
+                    }
+                }
+                if (isItemFound) break
+            }
+
+            if (isItemFound) continue
+
+            for (component in launcherActivities.shuffled()) {
+                if (!isItemExcluded(component) && !isItemInDock(component)) {
+                    createDockItem(
+                        componentName = component,
+                        DockAppItem.Type.DYNAMIC,
+                        isMediaApp(component)
+                    )?.let { dockItem ->
+                        if (DEBUG) {
+                            Log.d(TAG, "Adding recommended item(${dockItem.component}) at $index")
+                        }
+                        internalItems[index] = dockItem
+                        isItemFound = true
+                    }
+                }
+                if (isItemFound) break
+            }
+
+            if (!isItemFound) {
+                throw IllegalStateException("Cannot find enough apps to place in the dock")
+            }
+        }
+        return convertMapToList(internalItems)
+    }
+
+    private fun savePinnedItemsToProto() {
+        dockProtoDataController.savePinnedItemsToFile(
+            internalItems.filter { entry -> entry.value.type == DockAppItem.Type.STATIC }
+                    .mapValues { entry -> entry.value.component }
+        )
+    }
+
+    /** Use the mapping index->item to create the ordered list of Dock items */
+    private fun convertMapToList(map: Map<Int, DockAppItem>): List<DockAppItem> =
+            List(maxItemsInDock) { index -> map[index] }.filterNotNull()
+    // TODO b/314409899: use a default DockItem when a position is empty
+
+    private fun findIndexToPin(): Int? {
+        var index: Int? = null
+        for (i in 0..<maxItemsInDock) {
+            if (!internalItems.contains(i)) {
+                index = i
+                break
+            }
+            if (internalItems[i]?.type == DockAppItem.Type.DYNAMIC) {
+                index = i
+                break
+            }
+        }
+        return index
+    }
+
     private fun indexOfLeastRecentDynamicItemInDock(): Int? {
-        // edge case - if there is no apps being shown, update first position
-        if (internalItems.size == 0) return 0
+        if (DEBUG) {
+            Log.d(
+                    TAG,
+                    "internalItems.size = ${internalItems.size}, maxItemsInDock= $maxItemsInDock"
+            )
+        }
+        if (internalItems.size < maxItemsInDock) return internalItems.size
         // since map is ordered from least recent to most recent, return first dynamic entry found
         internalItems.forEach { appItemEntry ->
             if (appItemEntry.value.type == DockAppItem.Type.DYNAMIC) return appItemEntry.key
@@ -96,8 +376,103 @@
         return null
     }
 
-    /** Use the mapping index->item to create the ordered list of Dock items */
-    private fun convertMapToList(map: Map<Int, DockAppItem>) =
-        List(numItems) { index -> map[index] }
-    // TODO b/314409899: use a default DockItem when a position is empty
+    private fun isItemExcluded(component: ComponentName): Boolean =
+            (isPackageExcluded(component.packageName) || isComponentExcluded(component))
+
+    private fun isItemInDock(component: ComponentName, ofType: DockAppItem.Type? = null): Boolean {
+        return internalItems.values
+                .filter { (ofType == null) || (it.type == ofType) }
+                .map { it.component.packageName }
+                .contains(component.packageName)
+    }
+
+    /* Creates Dock item from a ComponentName. */
+    private fun createDockItem(
+            componentName: ComponentName,
+            itemType: DockAppItem.Type,
+            isMediaApp: Boolean,
+    ): DockAppItem? {
+        // TODO: Compare the component against LauncherApps to make sure the component
+        // is launchable, similar to what app grid has
+
+        val ai = getPackageItemInfo(componentName) ?: return null
+        // todo(b/315210225): handle getting icon lazily
+        val icon = ai.loadIcon(packageManager)
+        val iconColor = getIconColor(icon)
+        return DockAppItem(
+                id = getUniqueDockItemId(),
+                type = itemType,
+                component = componentName,
+                name = ai.loadLabel(packageManager).toString(),
+                icon = icon,
+                iconColor = iconColor,
+                isDistractionOptimized =
+                isMediaApp || (carPackageManager?.isActivityDistractionOptimized(
+                    componentName.packageName,
+                    componentName.className
+                ) ?: false),
+            isMediaApp = isMediaApp
+        )
+    }
+
+    private fun getPackageItemInfo(componentName: ComponentName): PackageItemInfo? {
+        try {
+            val isMediaApp = isMediaApp(componentName)
+            val pkgInfo = packageManager.getPackageInfo(
+                componentName.packageName,
+                PackageManager.PackageInfoFlags.of(
+                    (if (isMediaApp) PackageManager.GET_SERVICES else PackageManager.GET_ACTIVITIES)
+                        .toLong()
+                )
+            )
+            return if (isMediaApp) {
+                pkgInfo.services?.find { it.componentName == componentName }
+            } else {
+                pkgInfo.activities?.find { it.componentName == componentName }
+            }
+        } catch (e: PackageManager.NameNotFoundException) {
+            if (DEBUG) {
+                // don't need to crash for this failure, log error instead
+                Log.e(TAG, "Component $componentName not found", e)
+            }
+        }
+        return null
+    }
+
+    private fun getIconColor(componentName: ComponentName): Int {
+        val ai = getPackageItemInfo(componentName) ?: return defaultIconColor
+        return getIconColor(ai.loadIcon(packageManager))
+    }
+
+    private fun getIconColor(icon: Drawable) = colorExtractor.findDominantColorByHue(
+            iconFactory.createScaledBitmap(icon, BaseIconFactory.MODE_DEFAULT)
+    )
+
+    private fun getUniqueDockItemId(): @DockItemId UUID {
+        val existingKeys = internalItems.values.map { it.id }.toSet()
+        for (i in 0..MAX_UNIQUE_ID_TRIES) {
+            val id = UUID.randomUUID()
+            if (!existingKeys.contains(id)) return id
+        }
+        return UUID.randomUUID()
+    }
+
+    private fun isMediaApp(component: ComponentName) = mediaServiceComponents.contains(component)
+
+    /** To be disabled for tests since [Toast] cannot be shown on that process */
+    @VisibleForTesting
+    fun showToast(message: String) {
+        Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
+    }
+
+    /** To be overridden in tests to pass mock values for RunningTasks */
+    @VisibleForTesting
+    fun getRunningTasks(): List<ActivityManager.RunningTaskInfo> {
+        return ActivityTaskManager.getInstance().getTasks(
+                MAX_TASKS_TO_FETCH,
+                false, // filterOnlyVisibleRecents
+                false, // keepIntentExtra
+                Display.DEFAULT_DISPLAY // displayId
+        )
+    }
 }
diff --git a/docklib/src/com/android/car/docklib/ExcludedItemsProvider.kt b/docklib/src/com/android/car/docklib/ExcludedItemsProvider.kt
new file mode 100644
index 0000000..35b983c
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/ExcludedItemsProvider.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 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.docklib
+
+import android.content.ComponentName
+
+/**
+ * Interface to provide packages or components to be excluded from the dock.
+ */
+interface ExcludedItemsProvider {
+    /**
+     * @return if the components in the [pkg] are excluded from Dock
+     */
+    fun isPackageExcluded(pkg: String): Boolean
+
+    /**
+     * @return if the [component] is excluded from Dock
+     */
+    fun isComponentExcluded(component: ComponentName): Boolean
+}
diff --git a/docklib/src/com/android/car/docklib/ResourceExcludedItemsProvider.kt b/docklib/src/com/android/car/docklib/ResourceExcludedItemsProvider.kt
new file mode 100644
index 0000000..d8998cd
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/ResourceExcludedItemsProvider.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2024 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.docklib
+
+import android.content.ComponentName
+import android.content.Context
+
+/**
+ * [ExcludedItemsProvider] that reads from resources and excludes given packages and components.
+ */
+class ResourceExcludedItemsProvider(context: Context) : ExcludedItemsProvider {
+    private val excludedPackages = context.resources
+            .getStringArray(R.array.config_packagesExcludedFromDock).toHashSet()
+    private val excludedComponents = context.resources
+            .getStringArray(R.array.config_componentsExcludedFromDock)
+            .mapNotNull(ComponentName::unflattenFromString).toHashSet()
+
+    override fun isPackageExcluded(pkg: String) = excludedPackages.contains(pkg)
+
+    override fun isComponentExcluded(component: ComponentName) =
+        excludedComponents.contains(component)
+}
diff --git a/docklib/src/com/android/car/docklib/data/DockAppItem.kt b/docklib/src/com/android/car/docklib/data/DockAppItem.kt
index 3e67950..fdf2121 100644
--- a/docklib/src/com/android/car/docklib/data/DockAppItem.kt
+++ b/docklib/src/com/android/car/docklib/data/DockAppItem.kt
@@ -17,17 +17,54 @@
 package com.android.car.docklib.data
 
 import android.content.ComponentName
+import android.graphics.Color
 import android.graphics.drawable.Drawable
+import androidx.annotation.ColorInt
+import com.android.internal.graphics.ColorUtils
+import com.android.launcher3.icons.FastBitmapDrawable
+import com.android.launcher3.icons.GraphicsUtils
+import java.util.UUID
 
-/** Data class that describes an app being showed on Dock */
+/**
+ * Data class that describes an app being showed on Dock.
+ *
+ * @param iconColor dominant color to be used with the [icon].
+ * @param iconColorScrim color to be used to create a slightly dull/bright color variation of
+ * [iconColor]. Uses the default scrim if not provided.
+ */
 data class DockAppItem(
-    val type: Type,
-    val component: ComponentName,
-    val name: String,
-    val icon: Drawable,
-    val isDistractionOptimized: Boolean,
+        val id: @DockItemId UUID = UUID.randomUUID(),
+        val type: Type,
+        val component: ComponentName,
+        val name: String,
+        val icon: Drawable,
+        @ColorInt val iconColor: Int,
+        @ColorInt private val iconColorScrim: Int = defaultIconColorScrim,
+        val isDistractionOptimized: Boolean,
+        val isMediaApp: Boolean,
 ) {
-    // todo(b/315210225): handle getting icon lazily
+    companion object{
+        private val defaultIconColorScrim = GraphicsUtils.setColorAlphaBound(
+                Color.WHITE,
+                FastBitmapDrawable.WHITE_SCRIM_ALPHA
+        )
+
+        /**
+         * Composes the [iconColor] with the [iconColorScrim].
+         *
+         * @param iconColorScrim color to be used to create a slightly dull/bright color variation
+         * of [iconColor]. Uses the default scrim if not provided.
+         */
+        fun getIconColorWithScrim(
+                iconColor: Int,
+                iconColorScrim: Int = defaultIconColorScrim
+        ): Int {
+            return ColorUtils.compositeColors(iconColorScrim, iconColor)
+        }
+    }
+
+    @ColorInt val iconColorWithScrim = getIconColorWithScrim(iconColor, iconColorScrim)
+
     enum class Type(val value: String) {
         DYNAMIC("DYNAMIC"),
         STATIC("STATIC");
@@ -38,20 +75,23 @@
     }
 
     override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is DockAppItem) return false
-
-        if (this.type != other.type) return false
-        if (this.name != other.name) return false
-        if (this.component != other.component) return false
-        if (this.icon.constantState != other.icon.constantState) return false
-        if (this.isDistractionOptimized != other.isDistractionOptimized) return false
-
-        return true
+        return (this === other) ||
+                (other is DockAppItem &&
+                        this.id == other.id &&
+                        this.name == other.name &&
+                        this.type == other.type &&
+                        this.component == other.component &&
+                        this.icon.constantState == other.icon.constantState &&
+                        this.iconColor == other.iconColor &&
+                        this.iconColorWithScrim == other.iconColorWithScrim &&
+                        this.isDistractionOptimized == other.isDistractionOptimized &&
+                        this.isMediaApp == other.isMediaApp)
     }
 
     override fun toString(): String {
-        return ("DockAppItem#${hashCode()}{name: $name, component: $component, type: $type, " +
-                "isDistractionOptimized: $isDistractionOptimized, icon: $icon}")
+        return ("DockAppItem#${hashCode()}{id: $id, name: $name, component: $component, " +
+                "type: $type, isDistractionOptimized: $isDistractionOptimized, icon: $icon, " +
+                "iconColor: $iconColor, iconColorScrim: $iconColorScrim, " +
+                "iconColorWithScrim: $iconColorWithScrim}")
     }
 }
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java b/docklib/src/com/android/car/docklib/data/DockItemId.kt
similarity index 67%
copy from app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
copy to docklib/src/com/android/car/docklib/data/DockItemId.kt
index e30ffe5..0713039 100644
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
+++ b/docklib/src/com/android/car/docklib/data/DockItemId.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 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.
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher;
+package com.android.car.docklib.data
 
-/**
- * A callbacks interface for {@link LaunchRootCarTaskView}.
- */
-public interface LaunchRootCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {}
+@Target(
+        AnnotationTarget.VALUE_PARAMETER,
+        AnnotationTarget.TYPE
+)
+@Retention(AnnotationRetention.SOURCE)
+@MustBeDocumented
+annotation class DockItemId
diff --git a/docklib/src/com/android/car/docklib/data/DockProtoDataController.kt b/docklib/src/com/android/car/docklib/data/DockProtoDataController.kt
new file mode 100644
index 0000000..5695b1a
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/data/DockProtoDataController.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2024 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.docklib.data
+
+import android.content.ComponentName
+import android.os.Build
+import android.util.Log
+import com.android.car.docklib.DockItemProto.DockAppItemListMessage
+import com.android.car.docklib.DockItemProto.DockAppItemMessage
+import java.io.File
+
+/**
+ * Proto file controller to read and write to store dock data
+ * @param dataFile a file that the current user has read and write permission
+ */
+class DockProtoDataController(dataFile: File) {
+    companion object {
+        private const val TAG = "DockProtoDataController"
+        private val DEBUG = Build.isDebuggable()
+        const val FILE_NAME = "dock_item_data"
+    }
+    private val dataSource = DockProtoDataSource(dataFile)
+
+    /**
+     * Load data from storage file
+     * @return mapping of a position to the component pinned to that position,
+     * or an empty mapping if the storage file doesn't exist
+     */
+    fun loadFromFile(): Map<Int, ComponentName>? {
+        if (DEBUG) Log.d(TAG, "Loading dock from file $dataSource")
+        return dataSource.readFromFile()?.let { dockAppItemListMessage ->
+            val items = HashMap<Int, ComponentName>()
+            dockAppItemListMessage.dockAppItemMessageList.forEach {
+                val componentName = ComponentName(it.packageName, it.className)
+                items[it.relativePosition] = componentName
+            }
+            if (DEBUG) Log.d(TAG, "Loaded dock from file $dataSource")
+            items
+        }
+    }
+
+    /**
+     * Create and write pinned dock items to file
+     * @param pinnedDockItems mapping of position to the component pinned to that position
+     */
+    fun savePinnedItemsToFile(pinnedDockItems: Map<Int, ComponentName>) {
+        if (DEBUG) Log.d(TAG, "Save dock to file $dataSource")
+        val data = DockAppItemListMessage.newBuilder()
+        pinnedDockItems.forEach() {
+            data.addDockAppItemMessage(
+                DockAppItemMessage.newBuilder()
+                        .setPackageName(it.value.packageName)
+                        .setClassName(it.value.className)
+                        .setRelativePosition(it.key)
+                        .build()
+            )
+        }
+        dataSource.writeToFile(data.build())
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/data/DockProtoDataSource.kt b/docklib/src/com/android/car/docklib/data/DockProtoDataSource.kt
new file mode 100644
index 0000000..e9d5e6e
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/data/DockProtoDataSource.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2024 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.docklib.data
+
+import com.android.car.carlaunchercommon.proto.ProtoDataSource
+import com.android.car.docklib.DockItemProto.DockAppItemListMessage
+import java.io.File
+import java.io.InputStream
+import java.io.OutputStream
+
+/**
+ * Proto file wrapper with helper methods
+ * @param dataFile a file that the current user has read and write permission
+ */
+class DockProtoDataSource(dataFile: File) : ProtoDataSource<DockAppItemListMessage>(dataFile) {
+    override fun parseDelimitedFrom(inputStream: InputStream?): DockAppItemListMessage {
+        return DockAppItemListMessage.parseDelimitedFrom(inputStream)
+    }
+
+    override fun writeDelimitedTo(outputData: DockAppItemListMessage, outputStream: OutputStream?) {
+        return outputData.writeDelimitedTo(outputStream)
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/data/proto/dock_item.proto b/docklib/src/com/android/car/docklib/data/proto/dock_item.proto
new file mode 100644
index 0000000..43b0317
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/data/proto/dock_item.proto
@@ -0,0 +1,16 @@
+syntax = "proto2";
+
+package com.android.car.docklib.data;
+
+option java_package = "com.android.car.docklib";
+option java_outer_classname = "DockItemProto";
+
+message DockAppItemMessage {
+  required int32 relativePosition = 1;
+  required string package_name = 2;
+  required string class_name = 3;
+}
+
+message DockAppItemListMessage {
+  repeated DockAppItemMessage dockAppItemMessage = 1;
+}
diff --git a/docklib/src/com/android/car/docklib/events/DockEventsReceiver.java b/docklib/src/com/android/car/docklib/events/DockEventsReceiver.java
index 8442a9d..101bd73 100644
--- a/docklib/src/com/android/car/docklib/events/DockEventsReceiver.java
+++ b/docklib/src/com/android/car/docklib/events/DockEventsReceiver.java
@@ -40,7 +40,6 @@
 public class DockEventsReceiver extends BroadcastReceiver {
     private static final String TAG = "DockEventsReceiver";
     private static final boolean DEBUG = Build.isDebuggable();
-    // Extras key for the ComponentName associated with the Event
     private final DockInterface mDockController;
 
     public DockEventsReceiver(DockInterface dockController) {
@@ -81,6 +80,7 @@
      * @param context the context through which the DockEventsReceiver is registered
      * @return successfully registered DockEventsReceiver.
      */
+    @NonNull
     public static DockEventsReceiver registerDockReceiver(
             @NonNull Context context,
             @NonNull DockInterface dockController
diff --git a/docklib/src/com/android/car/docklib/events/DockPackageChangeReceiver.kt b/docklib/src/com/android/car/docklib/events/DockPackageChangeReceiver.kt
new file mode 100644
index 0000000..2a4164b
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/events/DockPackageChangeReceiver.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 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.docklib.events
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
+import android.os.Build
+import android.util.Log
+import com.android.car.docklib.DockInterface
+
+class DockPackageChangeReceiver(
+    private val dockController: DockInterface
+) : BroadcastReceiver() {
+    companion object {
+        private val DEBUG = Build.isDebuggable()
+        private const val TAG = "DockPackageChangeReceiver"
+
+        /**
+         * Helper method to register [DockPackageChangeReceiver] through context and listen to
+         * changes to packages in the system.
+         *
+         * @param context the context through which the [DockPackageChangeReceiver] is registered
+         * @return successfully registered [DockPackageChangeReceiver].
+         */
+        fun registerReceiver(
+            context: Context,
+            dockController: DockInterface
+        ): DockPackageChangeReceiver {
+            val receiver = DockPackageChangeReceiver(dockController)
+            val filter = IntentFilter()
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED)
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED)
+            filter.addDataScheme("package")
+            context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
+            if (DEBUG) {
+                Log.d(
+                    TAG,
+                    "DockPackageChangeReceiver registered from package: " +
+                            "${context.packageName}, for user ${context.userId}"
+                )
+            }
+            return receiver
+        }
+    }
+
+    override fun onReceive(context: Context?, intent: Intent?) {
+        intent?.data?.schemeSpecificPart?.let { packageName ->
+            if (DEBUG) Log.d(TAG, "package name: $packageName")
+            when (intent.action) {
+                Intent.ACTION_PACKAGE_ADDED -> {
+                    if (intent.getBooleanExtra(
+                            Intent.EXTRA_REPLACING,
+                            false // defaultValue
+                        )
+                    ) {
+                        return
+                    }
+                    if (DEBUG) Log.d(TAG, "ACTION_PACKAGE_ADDED")
+                    dockController.packageAdded(packageName)
+                }
+
+                Intent.ACTION_PACKAGE_REMOVED -> {
+                    if (intent.getBooleanExtra(
+                            Intent.EXTRA_REPLACING,
+                            false // defaultValue
+                        )
+                    ) {
+                        return
+                    }
+                    if (DEBUG) Log.d(TAG, "ACTION_PACKAGE_REMOVED")
+                    dockController.packageRemoved(packageName)
+                }
+
+                Intent.ACTION_PACKAGE_CHANGED -> {
+                    if (DEBUG) Log.d(TAG, "ACTION_PACKAGE_CHANGED")
+                    when (context?.packageManager?.getApplicationEnabledSetting(packageName)) {
+                        COMPONENT_ENABLED_STATE_DISABLED, COMPONENT_ENABLED_STATE_DISABLED_USER -> {
+                            if (DEBUG) Log.d(TAG, "package disabled")
+                            dockController.packageRemoved(packageName)
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/media/MediaUtils.kt b/docklib/src/com/android/car/docklib/media/MediaUtils.kt
new file mode 100644
index 0000000..99fc15f
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/media/MediaUtils.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 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.docklib.media
+
+import android.app.ActivityManager
+import android.car.media.CarMediaIntents
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.Build
+import android.service.media.MediaBrowserService
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import java.util.stream.Collectors
+
+class MediaUtils {
+    companion object {
+        private const val TAG = "MediaUtils"
+        private val DEBUG = Build.isDebuggable()
+
+        @VisibleForTesting
+        val CAR_MEDIA_ACTIVITY = ComponentName(
+            "com.android.car.media",
+            "com.android.car.media.MediaActivity"
+        )
+
+        @VisibleForTesting
+        const val CAR_MEDIA_DATA_SCHEME = "custom"
+
+        fun getMediaComponentName(taskInfo: ActivityManager.RunningTaskInfo): ComponentName? {
+            val data = taskInfo.baseIntent.data
+            if (data == null) {
+                if (DEBUG) Log.d(TAG, "No data attached to the base intent")
+                return null
+            }
+            if (CAR_MEDIA_DATA_SCHEME != data.scheme) {
+                if (DEBUG) Log.d(TAG, "Data scheme doesn't match")
+                return null
+            }
+            // should drop the first backslash that is part of the schemeSpecificPart
+            val ssp = data.schemeSpecificPart
+            val mediaComponentString = if (ssp.startsWith("/")) ssp.drop(1) else ssp
+            val mediaComponent = ComponentName.unflattenFromString(mediaComponentString)
+            if (DEBUG) Log.d(TAG, "Media component found: $mediaComponent")
+            return mediaComponent
+        }
+
+        fun isMediaComponent(component: ComponentName?) = component == CAR_MEDIA_ACTIVITY
+
+        fun createLaunchIntent(componentName: ComponentName) =
+            Intent(CarMediaIntents.ACTION_MEDIA_TEMPLATE)
+                .putExtra(CarMediaIntents.EXTRA_MEDIA_COMPONENT, componentName.flattenToString())
+
+        fun fetchMediaServiceComponents(
+            packageManager: PackageManager,
+            packageName: String? = null
+        ): MutableSet<ComponentName> {
+            val intent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+            if (packageName != null) intent.setPackage(packageName)
+            return packageManager.queryIntentServices(
+                intent,
+                PackageManager.GET_RESOLVED_FILTER
+            ).stream()
+                .map { resolveInfo: ResolveInfo -> resolveInfo.serviceInfo.componentName }
+                .collect(Collectors.toSet())
+        }
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/task/DockTaskStackChangeListener.java b/docklib/src/com/android/car/docklib/task/DockTaskStackChangeListener.java
index d23a98c..86903fa 100644
--- a/docklib/src/com/android/car/docklib/task/DockTaskStackChangeListener.java
+++ b/docklib/src/com/android/car/docklib/task/DockTaskStackChangeListener.java
@@ -17,34 +17,40 @@
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
+import android.os.Build;
+import android.util.Log;
+import android.view.Display;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
+import com.android.car.docklib.DockInterface;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
-import java.util.function.Consumer;
-
 public class DockTaskStackChangeListener implements TaskStackChangeListener {
-    Consumer<ComponentName> mTaskLaunchDelegate;
-    public DockTaskStackChangeListener(Consumer<ComponentName> taskLaunchDelegate) {
-        mTaskLaunchDelegate = taskLaunchDelegate;
+    private static final String TAG = "DockTaskStackChangeListener";
+    private static final boolean DEBUG = Build.isDebuggable();
+
+    private final DockInterface mDockController;
+    private final int mCurrentUserId;
+
+    public DockTaskStackChangeListener(int currentUserId, @NonNull DockInterface dockController) {
+        mDockController = dockController;
+        mCurrentUserId = currentUserId;
     }
 
     @Override
     public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
-        ComponentName component = getComponentName(taskInfo);
-        mTaskLaunchDelegate.accept(component);
-    }
-
-    @Nullable
-    private ComponentName getComponentName(@NonNull ActivityManager.RunningTaskInfo taskInfo) {
-        if (taskInfo.baseActivity == null && taskInfo.baseIntent.getComponent() == null) {
-            return null;
+        if (taskInfo.displayId != Display.DEFAULT_DISPLAY || taskInfo.userId != mCurrentUserId) {
+            if (DEBUG) {
+                Log.d(TAG, "New task on display " + taskInfo.displayId
+                        + " and for user " + taskInfo.userId + " is not added to the dock");
+            }
+            return;
         }
-        return taskInfo.baseActivity != null ? taskInfo.baseActivity
-                : taskInfo.baseIntent.getComponent();
+
+        ComponentName component = TaskUtils.Companion.getComponentName(taskInfo);
+        if (component != null) {
+            mDockController.appLaunched(component);
+        }
     }
-
-
 }
diff --git a/docklib/src/com/android/car/docklib/task/TaskUtils.kt b/docklib/src/com/android/car/docklib/task/TaskUtils.kt
new file mode 100644
index 0000000..bc074d1
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/task/TaskUtils.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.docklib.task
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import com.android.car.docklib.media.MediaUtils.Companion.getMediaComponentName
+import com.android.car.docklib.media.MediaUtils.Companion.isMediaComponent
+
+class TaskUtils {
+    companion object {
+        fun getComponentName(taskInfo: ActivityManager.RunningTaskInfo): ComponentName? {
+            if (taskInfo.baseActivity == null && taskInfo.baseIntent.component == null) {
+                return null
+            }
+            val component = if (taskInfo.baseActivity != null) {
+                taskInfo.baseActivity
+            } else {
+                taskInfo.baseIntent.component
+            }
+
+            return if (isMediaComponent(component)) getMediaComponentName(taskInfo) else component
+        }
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/view/DockAdapter.kt b/docklib/src/com/android/car/docklib/view/DockAdapter.kt
index 5a5d746..7b9a9eb 100644
--- a/docklib/src/com/android/car/docklib/view/DockAdapter.kt
+++ b/docklib/src/com/android/car/docklib/view/DockAdapter.kt
@@ -1,150 +1,159 @@
+/*
+ * Copyright (C) 2023 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.docklib.view
 
-import android.car.content.pm.CarPackageManager
-import android.content.ComponentName
+import android.car.media.CarMediaManager
 import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.NameNotFoundException
 import android.os.Build
 import android.util.Log
 import android.view.LayoutInflater
 import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
 import androidx.recyclerview.widget.RecyclerView
+import com.android.car.docklib.DockInterface
 import com.android.car.docklib.R
 import com.android.car.docklib.data.DockAppItem
-import java.util.function.Consumer
 
-/**
- * [RecyclerView.Adapter] used to bind Dock items
- * @param numItems maximum num of items present in the dock
- * @param items initial list of items in the Dock
- */
-class DockAdapter(
-    private val numItems: Int,
-    private val intentDelegate: Consumer<Intent>,
-    private val userContext: Context,
-    private val items: Array<DockAppItem?> = arrayOfNulls(numItems)
-) : RecyclerView.Adapter<DockItemViewHolder>() {
+/** [RecyclerView.Adapter] used to bind Dock items */
+class DockAdapter(private val dockController: DockInterface, private val userContext: Context) :
+        ListAdapter<DockAppItem, DockItemViewHolder>(DIFF_CALLBACK) {
     companion object {
         private val DEBUG = Build.isDebuggable()
         private const val TAG = "DockAdapter"
     }
 
-    private var carPackageManager: CarPackageManager? = null
+    private var carMediaManager: CarMediaManager? = null
 
     enum class PayloadType {
-        CHANGE_SAME_ITEM_TYPE,
+        CHANGE_ITEM_TYPE,
+        CHANGE_UX_RESTRICTION_STATE,
+        CHANGE_ACTIVE_MEDIA_SESSION,
+    }
+
+    private val positionToCallbackMap = HashMap<Int, Runnable>()
+    private var isUxRestrictionEnabled = false
+    private var activeMediaSessions: List<String> = emptyList()
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DockItemViewHolder {
+        val view = LayoutInflater.from(parent.context).inflate(
+                R.layout.dock_app_item_view, // resource
+                parent,
+                false // attachToRoot
+        )
+        return DockItemViewHolder(
+            dockController,
+            view,
+            userContext,
+            carMediaManager
+        )
     }
 
     override fun onBindViewHolder(
-        viewHolder: DockItemViewHolder,
-        position: Int,
-        payloads: MutableList<Any>
+            viewHolder: DockItemViewHolder,
+            position: Int,
+            payloads: MutableList<Any>
     ) {
-        if (payloads.isEmpty() ||
-            payloads.getOrNull(0) == null ||
-            payloads[0] !is PayloadType
-        ) {
+        if (payloads.isEmpty()) {
             return super.onBindViewHolder(viewHolder, position, payloads)
         }
-        when (payloads[0]) {
-            PayloadType.CHANGE_SAME_ITEM_TYPE ->
-                items[position]?.let {
-                    viewHolder.itemTypeChanged(it)
+        if (DEBUG) Log.d(TAG, "Binding at position $position with payloads")
+
+        payloads.forEach { payload ->
+            when (payload) {
+                PayloadType.CHANGE_ITEM_TYPE -> {
+                    if (DEBUG) Log.d(TAG, "Type changed for position $position")
+                    viewHolder.itemTypeChanged(currentList[position])
                 }
+                PayloadType.CHANGE_UX_RESTRICTION_STATE -> {
+                    if (DEBUG) Log.d(TAG, "UX restriction changed for position $position")
+                    viewHolder.setUxRestrictions(currentList[position], isUxRestrictionEnabled)
+                }
+                PayloadType.CHANGE_ACTIVE_MEDIA_SESSION -> {
+                    if (DEBUG) Log.d(TAG, "Active MediaSession changed for position $position")
+                    viewHolder.setHasActiveMediaSession(
+                        activeMediaSessions.contains(currentList[position].component.packageName)
+                    )
+                }
+            }
         }
     }
 
-    override fun onCreateViewHolder(parent: ViewGroup, p1: Int): DockItemViewHolder {
-        val view = LayoutInflater.from(parent.context).inflate(
-            R.layout.dock_app_item_view, // resource
-            parent,
-            false // attachToRoot
-        )
-        return DockItemViewHolder(view, intentDelegate)
-    }
-
-    override fun getItemCount() = numItems
-
     override fun onBindViewHolder(viewHolder: DockItemViewHolder, position: Int) {
-        viewHolder.bind(items[position])
+        if (DEBUG) Log.d(TAG, "Binding at position $position without payloads")
+        val cleanupCallback = positionToCallbackMap.getOrDefault(
+                position,
+                null // defaultValue
+        )
+        if (DEBUG) Log.d(TAG, "Is callback set for $position: ${cleanupCallback != null}")
+        positionToCallbackMap.remove(position)
+        viewHolder.bind(
+            currentList[position],
+            isUxRestrictionEnabled,
+            cleanupCallback,
+            activeMediaSessions.contains(currentList[position].component.packageName)
+        )
     }
 
-    fun setItems(items: List<DockAppItem?>) {
-        for (i in 0..<numItems) {
-            if (this.items[i] != items.getOrNull(i)) {
-                this.items[i] = items.getOrNull(i)
-                notifyItemChanged(i)
-            }
-        }
+    /** Used to set a callback for the [position] to be passed to the ViewHolder on the next bind. */
+    fun setCallback(position: Int, callback: Runnable?) {
+        callback?.let { positionToCallbackMap[position] = it }
     }
 
     /**
-     * Pin new app to the given position
+     * Setter for CarMediaManager
      */
-    fun pinItemAt(position: Int, componentName: ComponentName) {
-        // todo(b/315222570): move to controller
-        if (!isValidPosition(position)) {
-            return
-        }
-        try {
-            val ai = userContext.packageManager
-                .getActivityInfo(componentName, PackageManager.ComponentInfoFlags.of(0L))
-            items[position] = DockAppItem(
-                DockAppItem.Type.STATIC,
-                componentName,
-                ai.name,
-                ai.loadIcon(userContext.packageManager),
-                carPackageManager?.isActivityDistractionOptimized(
-                    componentName.packageName,
-                    componentName.className
-                ) ?: false
-            )
-            notifyItemChanged(position)
-        } catch (e: NameNotFoundException) {
-            if (DEBUG) {
-                // don't need to crash for a failed pin, log error instead
-                Log.e(TAG, "Component $componentName not found, pinning failed $e")
-            }
+    fun setCarMediaManager(carMediaManager: CarMediaManager) {
+        this.carMediaManager = carMediaManager
+    }
+
+    /** Set if the Ux restrictions are enabled */
+    fun setUxRestrictions(isUxRestrictionEnabled: Boolean) {
+        if (this.isUxRestrictionEnabled != isUxRestrictionEnabled) {
+            this.isUxRestrictionEnabled = isUxRestrictionEnabled
+            notifyItemRangeChanged(0, itemCount, PayloadType.CHANGE_UX_RESTRICTION_STATE)
         }
     }
 
-    /**
-     * Pin the DockItem at the given position. If the app is already pinned this call is a no-op.
-     */
-    fun pinItemAt(position: Int) {
-        // todo(b/315222570): move to controller
-        changeItemType(position, DockAppItem.Type.STATIC)
-    }
-
-    /**
-     * Unpin the DockItem at the given position. If the app is already unpinned this call is a
-     * no-op.
-     */
-    fun unpinItemAt(position: Int) {
-        // todo(b/315222570): move to controller
-        changeItemType(position, DockAppItem.Type.DYNAMIC)
-    }
-
-    private fun changeItemType(position: Int, newItemType: DockAppItem.Type) {
-        if (!isValidPosition(position) || items[position]?.type == newItemType) {
-            return
-        }
-        items[position]?.let {
-            items[position] = it.copy(type = newItemType)
-            notifyItemChanged(position, PayloadType.CHANGE_SAME_ITEM_TYPE)
+    /** Be notified that active media sessions have been changed */
+    fun onMediaSessionChange(activeMediaSessions: List<String>) {
+        if (this.activeMediaSessions != activeMediaSessions) {
+            this.activeMediaSessions = activeMediaSessions
+            notifyItemRangeChanged(0, itemCount, PayloadType.CHANGE_ACTIVE_MEDIA_SESSION)
         }
     }
+}
 
-    /**
-     * Setter for CarPackageManager
-     */
-    fun setCarPackageManager(carPackageManager: CarPackageManager) {
-        this.carPackageManager = carPackageManager
+private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<DockAppItem>() {
+    override fun areItemsTheSame(p0: DockAppItem, p1: DockAppItem): Boolean {
+        return p0.id == p1.id
     }
 
-    private fun isValidPosition(position: Int): Boolean {
-        return position >= 0 && position < items.size
+    override fun areContentsTheSame(p0: DockAppItem, p1: DockAppItem): Boolean {
+        return p0 == p1
+    }
+
+    override fun getChangePayload(
+            oldItem: DockAppItem,
+            newItem: DockAppItem
+    ): Any? {
+        if (oldItem.type != newItem.type) {
+            return DockAdapter.PayloadType.CHANGE_ITEM_TYPE
+        }
+        return super.getChangePayload(oldItem, newItem)
     }
 }
diff --git a/docklib/src/com/android/car/docklib/view/DockDragListener.kt b/docklib/src/com/android/car/docklib/view/DockDragListener.kt
index 7563bd4..fae4579 100644
--- a/docklib/src/com/android/car/docklib/view/DockDragListener.kt
+++ b/docklib/src/com/android/car/docklib/view/DockDragListener.kt
@@ -1,7 +1,24 @@
+/*
+ * Copyright (C) 2023 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.docklib.view
 
 import android.content.ClipData
 import android.content.ComponentName
+import android.content.res.Resources
 import android.graphics.Point
 import android.os.Build
 import android.util.Log
@@ -9,22 +26,23 @@
 import android.view.SurfaceControl
 import android.view.SurfaceControl.Transaction
 import android.view.View
+import androidx.annotation.OpenForTesting
 import androidx.annotation.VisibleForTesting
 import androidx.core.animation.Animator
+import androidx.core.animation.PathInterpolator
 import androidx.core.animation.PropertyValuesHolder
 import androidx.core.animation.ValueAnimator
-import androidx.recyclerview.widget.RecyclerView
 import com.android.car.docklib.R
-import java.lang.Exception
-import java.lang.IndexOutOfBoundsException
+import java.util.function.Consumer
 
 /**
- * {@link View.OnDragListener} for Dock. Receives a drop and moves it to correct location,
+ * [View.OnDragListener] for Dock. Receives a drop and moves it to correct location,
  * transformed to the given size. This should be applied to all individual items in the dock that
  * wants to receive a drop.
  */
+@OpenForTesting
 open class DockDragListener(
-    private val viewHolder: RecyclerView.ViewHolder,
+    resources: Resources,
     private val callback: Callback
 ) : View.OnDragListener {
     companion object {
@@ -47,12 +65,13 @@
         private val DEBUG = Build.isDebuggable()
     }
 
-    private val animateInDuration: Int
-
-    init {
-        val resources = viewHolder.itemView.context.resources
-        animateInDuration = resources.getInteger(R.integer.drag_drop_animate_in_duration)
-    }
+    private val scaleDownDuration =
+        resources.getInteger(R.integer.drop_animation_scale_down_duration_ms).toLong()
+    private val scaleUpDuration =
+        resources.getInteger(R.integer.drop_animation_scale_up_duration_ms).toLong()
+    private val scaleDownWidth =
+        resources.getDimension(R.dimen.drop_animation_scale_down_width)
+    private var surfaceControl: SurfaceControl? = null
 
     override fun onDrag(view: View, dragEvent: DragEvent): Boolean {
         when (dragEvent.action) {
@@ -70,13 +89,6 @@
             }
 
             DragEvent.ACTION_DROP -> {
-                if (viewHolder.bindingAdapterPosition == RecyclerView.NO_POSITION) {
-                    if (DEBUG) Log.d(TAG, "Drop at invalid position")
-                    callback.resetView()
-                    return false
-                }
-                if (DEBUG) Log.d(TAG, "Drop at position: " + viewHolder.bindingAdapterPosition)
-
                 val item: ClipData.Item
                 try {
                     item = dragEvent.clipData.getItemAt(0)
@@ -108,24 +120,16 @@
 
                 // todo(b/312718542): hidden api(dragEvent.dragSurface) usage
                 dragEvent.dragSurface?.let {
-                    callback.dragAccepted(component)
-                    animateSurfaceIn(it, dragEvent)
+                    surfaceControl = it
+                    animateSurfaceIn(it, dragEvent, component)
                     return true
                 } ?: run {
                     if (DEBUG) Log.d(TAG, "Could not retrieve the drag surface")
                     // drag is success but animation is not possible since there is no dragSurface
-                    callback.dragAccepted(component)
+                    callback.dropSuccessful(component)
                     return false
                 }
             }
-
-            DragEvent.ACTION_DRAG_ENDED -> {
-                if (!dragEvent.result) {
-                    // if drop was accepted the drop action should handle resetting
-                    callback.resetView()
-                }
-                return true
-            }
         }
         return false
     }
@@ -137,26 +141,76 @@
     private fun animateSurfaceIn(
         surfaceControl: SurfaceControl,
         dragEvent: DragEvent,
+        component: ComponentName
     ) {
+        callback.dropAnimationsStarting(component)
         val dropContainerLocation = callback.getDropContainerLocation()
         // todo(b/312718542): hidden api(offsetX and offsetY) usage
         val fromX: Float = dropContainerLocation.x + (dragEvent.x - dragEvent.offsetX)
         val fromY: Float = dropContainerLocation.y + (dragEvent.y - dragEvent.offsetY)
 
         val dropLocation = callback.getDropLocation()
-        val toX: Float = dropLocation.x.toFloat()
-        val toY: Float = dropLocation.y.toFloat()
+        val toFinalX: Float = dropLocation.x.toFloat()
+        val toFinalY: Float = dropLocation.y.toFloat()
+        val toScaleDownLocationX = toFinalX + scaleDownWidth
+        val toScaleDownLocationY = toFinalY + scaleDownWidth
 
-        val toScaleX: Float = callback.getDropWidth() / surfaceControl.width
-        val toScaleY: Float = callback.getDropHeight() / surfaceControl.height
+        val toFinalWidth: Float = callback.getDropWidth()
+        val toFinalHeight: Float = callback.getDropHeight()
+        val toFinalScaleX: Float = toFinalWidth / surfaceControl.width
+        val toFinalScaleY: Float = toFinalHeight / surfaceControl.height
+        val toScaleDownX: Float =
+            ((toFinalWidth - (scaleDownWidth * 2)) / surfaceControl.width).coerceAtLeast(0f)
+        val toScaleDownY: Float =
+            ((toFinalHeight - (scaleDownWidth * 2)) / surfaceControl.height).coerceAtLeast(0f)
+        if (DEBUG && (toScaleDownX <= 0 || toScaleDownY <= 0)) {
+            Log.w(
+                TAG,
+                "Reached negative/zero scale, decrease the value of " +
+                        "drop_animation_scale_down_width"
+            )
+        }
 
-        getAnimator(surfaceControl, fromX, fromY, toX, toY, toScaleX, toScaleY).start()
+        val scaleDownAnimator = getAnimator(
+            surfaceControl,
+            fromX = fromX,
+            fromY = fromY,
+            toX = toScaleDownLocationX,
+            toY = toScaleDownLocationY,
+            toScaleX = toScaleDownX,
+            toScaleY = toScaleDownY,
+            animationDuration = scaleDownDuration,
+        )
+        val scaleUpAnimator = getAnimator(
+            surfaceControl,
+            fromX = toScaleDownLocationX,
+            fromY = toScaleDownLocationY,
+            toX = toFinalX,
+            toY = toFinalY,
+            fromScaleX = toScaleDownX,
+            fromScaleY = toScaleDownY,
+            toScaleX = toFinalScaleX,
+            toScaleY = toFinalScaleY,
+            animationDuration = scaleUpDuration,
+        )
+
+        scaleDownAnimator.addListener(getAnimatorListener(onAnimationEnd = { isCancelled ->
+            if (!isCancelled) {
+                callback.dropAnimationScaleDownComplete(component)
+                scaleUpAnimator.start()
+            }
+        }))
+        scaleUpAnimator.addListener(getAnimatorListener(onAnimationEnd = { isCancelled ->
+            callback.dropAnimationComplete(component)
+            if (!isCancelled) callback.dropSuccessful(component, getCleanUpCallback(surfaceControl))
+        }))
+
+        scaleDownAnimator.start()
     }
 
     /**
-     * Get the animator responsible for animating the {@code surfaceControl} from
-     * {@code fromX, fromY} to its final position {@code toX, toY} with correct scale
-     * {@code toScaleX, toScaleY}.
+     * Get the animator responsible for animating the [surfaceControl] from [fromX], [fromY]
+     * to its final position [toX], [toY] with correct scale [toScaleX], [toScaleY].
      * Default values are added to make this method easier to test. Generally all parameters are
      * expected to be sent by the caller.
      */
@@ -167,17 +221,33 @@
         fromY: Float = 0f,
         toX: Float = 0f,
         toY: Float = 0f,
+        fromScaleX: Float = 1f,
+        fromScaleY: Float = 1f,
         toScaleX: Float = 1f,
-        toScaleY: Float = 1f
+        toScaleY: Float = 1f,
+        animationDuration: Long = 0L,
     ): ValueAnimator {
+        if (DEBUG) {
+            Log.d(
+                TAG,
+                "getAnimator{ " +
+                        "surfaceControl: $surfaceControl, " +
+                        "(fromX: $fromX, fromY: $fromY), " +
+                        "(toX: $toX, toY: $toY), " +
+                        "(fromScaleX: $fromScaleX, fromScaleY: $fromScaleY), " +
+                        "(toScaleX: $toScaleX, toScaleY: $toScaleY) " +
+                        "}"
+            )
+        }
+
         val pvhX: PropertyValuesHolder =
             PropertyValuesHolder.ofFloat(PVH_POSITION_X, fromX, toX)
         val pvhY: PropertyValuesHolder =
             PropertyValuesHolder.ofFloat(PVH_POSITION_Y, fromY, toY)
         val pvhScaleX =
-            PropertyValuesHolder.ofFloat(PVH_SCALE_X, 1f, toScaleX)
+            PropertyValuesHolder.ofFloat(PVH_SCALE_X, fromScaleX, toScaleX)
         val pvhScaleY =
-            PropertyValuesHolder.ofFloat(PVH_SCALE_Y, 1f, toScaleY)
+            PropertyValuesHolder.ofFloat(PVH_SCALE_Y, fromScaleY, toScaleY)
 
         val animator: ValueAnimator =
             ValueAnimator.ofPropertyValuesHolder(
@@ -186,27 +256,25 @@
                 pvhScaleX,
                 pvhScaleY
             )
-        animator.setDuration(animateInDuration.toLong())
+        animator.setDuration(animationDuration)
+        animator.interpolator = PathInterpolator(0f, 0f, 0f, 1f)
         val trx = Transaction()
         animator.addUpdateListener(getAnimatorUpdateListener(surfaceControl, trx))
-        animator.addListener(
-            getAnimatorListener(surfaceControl, trx, cleanupTrx = Transaction())
-        )
+        animator.addListener(getAnimatorListener { trx.close() })
         return animator
     }
 
     /**
      * Not expected to be used directly or overridden.
      *
-     * @param trx Transaction used to animate the {@code SurfaceControl} in place.
+     * @param trx Transaction used to animate the [surfaceControl] in place.
      */
     @VisibleForTesting
     fun getAnimatorUpdateListener(
         surfaceControl: SurfaceControl,
         trx: Transaction
     ): Animator.AnimatorUpdateListener {
-        return Animator.AnimatorUpdateListener {
-                updatedAnimation ->
+        return Animator.AnimatorUpdateListener { updatedAnimation ->
             if (updatedAnimation is ValueAnimator) {
                 trx.setPosition(
                     surfaceControl,
@@ -222,17 +290,12 @@
     }
 
     /**
-     * Not expected to be used directly or overridden.
-     * {@code trx} and {@code cleanupTrx} will be closed by this listener.
-     *
-     * @param trx Transaction used to animate the {@code SurfaceControl} in place.
-     * @param cleanupTrx Transaction used to animate out and hide {@code SurfaceControl}
+     * @param onAnimationEnd called with boolean(isCancelled) set to false when animation is ended
+     * and to true when cancelled.
      */
     @VisibleForTesting
     fun getAnimatorListener(
-        surfaceControl: SurfaceControl,
-        trx: Transaction,
-        cleanupTrx: Transaction
+        onAnimationEnd: Consumer<Boolean>
     ): Animator.AnimatorListener {
         return object : Animator.AnimatorListener {
             private var isCancelled = false
@@ -242,41 +305,61 @@
 
             override fun onAnimationEnd(var1: Animator) {
                 if (!isCancelled) {
-                    cleanup()
+                    onAnimationEnd.accept(isCancelled)
                 }
             }
 
             override fun onAnimationCancel(var1: Animator) {
                 isCancelled = true
-                cleanup()
+                onAnimationEnd.accept(isCancelled)
             }
 
             override fun onAnimationRepeat(var1: Animator) {
                 // no-op
             }
+        }
+    }
 
-            fun cleanup() {
-                trx.close()
-                if (surfaceControl.isValid) {
-                    // todo(b/312737692): add animations
-                    cleanupTrx.hide(surfaceControl)
-                    cleanupTrx.remove(surfaceControl)
-                    cleanupTrx.apply()
-                    cleanupTrx.close()
-                }
+    private fun getCleanUpCallback(surfaceControl: SurfaceControl): () -> Unit {
+        return {
+            if (DEBUG) Log.d(TAG, "cleanup callback called")
+            if (surfaceControl.isValid) {
+                if (DEBUG) Log.d(TAG, "Surface is valid")
+                val cleanupTrx = Transaction()
+                cleanupTrx.hide(surfaceControl)
+                cleanupTrx.remove(surfaceControl)
+                cleanupTrx.apply()
+                cleanupTrx.close()
             }
         }
     }
 
     /**
-     * {@link DockDragListener} communicates events back and requests data from the caller using
+     * [DockDragListener] communicates events back and requests data from the caller using
      * this callback.
      */
     interface Callback {
         /**
-         * Drag is accepted/successful for the {@code componentName}
+         * Drop is accepted/successful for the [componentName]
+         *
+         * @param cleanupCallback [Runnable] to be called when the dropped item is ready/drawn.
          */
-        fun dragAccepted(componentName: ComponentName) {}
+        fun dropSuccessful(componentName: ComponentName, cleanupCallback: Runnable? = null) {}
+
+        /**
+         * Drop animations about to start.
+         */
+        fun dropAnimationsStarting(componentName: ComponentName) {}
+
+        /**
+         * Drop animation scale down completed.
+         */
+        fun dropAnimationScaleDownComplete(componentName: ComponentName) {}
+
+        /**
+         * Drop animation completed.
+         */
+        fun dropAnimationComplete(componentName: ComponentName) {}
 
         /**
          * Excite the view to indicate the item can be dropped in this position when dragged inside
diff --git a/docklib/src/com/android/car/docklib/view/DockItemClickListener.kt b/docklib/src/com/android/car/docklib/view/DockItemClickListener.kt
new file mode 100644
index 0000000..7e11ef8
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/view/DockItemClickListener.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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.docklib.view
+
+import android.view.View
+import com.android.car.carlaunchercommon.toasts.NonDrivingOptimizedLaunchFailedToast.Companion.showToast
+import com.android.car.docklib.DockInterface
+import com.android.car.docklib.data.DockAppItem
+
+/**
+ * [View.OnClickListener] for handling clicks on dock item.
+ *
+ * @property isRestricted if the item is restricted
+ */
+class DockItemClickListener(
+    private val dockController: DockInterface,
+    private val dockAppItem: DockAppItem,
+    private var isRestricted: Boolean,
+) : View.OnClickListener {
+    override fun onClick(v: View?) {
+        if (isRestricted) {
+            v?.context?.let { showToast(it, dockAppItem.name) }
+            return
+        }
+        dockController.launchApp(dockAppItem.component, dockAppItem.isMediaApp)
+    }
+
+    /**
+     * Set if the item is restricted.
+     */
+    fun setIsRestricted(isRestricted: Boolean) {
+        this.isRestricted = isRestricted
+    }
+}
diff --git a/docklib/src/com/android/car/docklib/view/DockItemLongClickListener.kt b/docklib/src/com/android/car/docklib/view/DockItemLongClickListener.kt
index 2e65b74..11a1eac 100644
--- a/docklib/src/com/android/car/docklib/view/DockItemLongClickListener.kt
+++ b/docklib/src/com/android/car/docklib/view/DockItemLongClickListener.kt
@@ -1,31 +1,68 @@
+/*
+ * Copyright (C) 2023 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.docklib.view
 
+import android.car.media.CarMediaManager
+import android.content.ComponentName
+import android.content.Context
 import android.content.res.Resources
 import android.view.View
 import androidx.annotation.OpenForTesting
 import androidx.annotation.VisibleForTesting
+import com.android.car.carlaunchercommon.shortcuts.AppInfoShortcutItem
+import com.android.car.carlaunchercommon.shortcuts.ForceStopShortcutItem
+import com.android.car.carlaunchercommon.shortcuts.PinShortcutItem
 import com.android.car.docklib.data.DockAppItem
-import com.android.car.dockutil.shortcuts.PinShortcutItem
 import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
 
 /**
- * {@link View.OnLongClickListener} for handling long clicks on dock item.
+ * [View.OnLongClickListener] for handling long clicks on dock item.
  * It is responsible to create and show th popup window
  *
- * @param dockAppItem the {@link DockAppItem} to be used on long click.
- * @param pinItemClickDelegate called when item should be pinned at that position
- * @param unpinItemClickDelegate called when item should be unpinned at that position
+ * @property dockAppItem the [DockAppItem] to be used on long click.
+ * @property pinItemClickDelegate called when item should be pinned at that position
+ * @property unpinItemClickDelegate called when item should be unpinned at that position
+ * @property component [ComponentName] of the item at this position
+ * @property userContext [Context] for the current running user
+ * @property mediaServiceComponents list of [ComponentName] of the services the adhere to the media
+ * service interface
  */
 @OpenForTesting
 open class DockItemLongClickListener(
     private var dockAppItem: DockAppItem,
     private val pinItemClickDelegate: Runnable,
-    private val unpinItemClickDelegate: Runnable
+    private val unpinItemClickDelegate: Runnable,
+    private val component: ComponentName,
+    private val userContext: Context,
+    private val carMediaManager: CarMediaManager?,
+    private val mediaServiceComponents: Set<ComponentName>
 ) : View.OnLongClickListener {
     override fun onLongClick(view: View?): Boolean {
         if (view == null) return false
 
         createCarUiShortcutsPopupBuilder()
+            .addShortcut(ForceStopShortcutItem(
+                userContext,
+                component.packageName,
+                dockAppItem.name,
+                carMediaManager,
+                mediaServiceComponents
+            ))
+            .addShortcut(AppInfoShortcutItem(userContext, component.packageName, userContext.user))
             .addShortcut(
                 createPinShortcutItem(
                     view.context.resources,
@@ -40,7 +77,7 @@
     }
 
     /**
-     * Set the {@link DockAppItem} to be used on long click.
+     * Set the [DockAppItem] to be used on long click.
      */
     fun setDockAppItem(dockAppItem: DockAppItem) {
         this.dockAppItem = dockAppItem
diff --git a/docklib/src/com/android/car/docklib/view/DockItemViewController.kt b/docklib/src/com/android/car/docklib/view/DockItemViewController.kt
new file mode 100644
index 0000000..ad7aa52
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/view/DockItemViewController.kt
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2024 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.docklib.view
+
+import android.content.res.ColorStateList
+import android.graphics.ColorFilter
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.os.Build
+import android.util.Log
+import androidx.core.animation.Animator
+import com.android.car.docklib.data.DockAppItem
+import com.android.car.docklib.view.animation.ExcitementAnimationHelper
+import com.google.android.material.imageview.ShapeableImageView
+import java.util.EnumSet
+import kotlin.math.floor
+
+/**
+ * Controller to help manage states for individual DockItemViews.
+ */
+class DockItemViewController(
+    private val staticIconStrokeWidth: Float,
+    private val dynamicIconStrokeWidth: Float,
+    private val excitedIconStrokeWidth: Float,
+    private val staticIconStrokeColor: Int,
+    private val excitedIconStrokeColor: Int,
+    private val restrictedIconStrokeColor: Int,
+    private val defaultIconColor: Int,
+    private val excitedColorFilter: ColorFilter,
+    private val restrictedColorFilter: ColorFilter,
+    private val excitedIconColorFilterAlpha: Float,
+    private val exciteAnimationDuration: Int,
+) {
+
+    companion object {
+        private val TAG = DockItemViewController::class.simpleName
+        private val DEBUG = Build.isDebuggable()
+        private const val DEFAULT_STROKE_WIDTH = 0f
+        private const val INITIAL_COLOR_FILTER_ALPHA = 0f
+    }
+
+    private enum class TypeStates {
+        DYNAMIC, STATIC
+    }
+
+    private enum class OptionalStates {
+        EXCITED, UPDATING, RESTRICTED, ACTIVE_MEDIA
+    }
+
+    private var dynamicIconStrokeColor: Int = defaultIconColor
+    private var updatingColor: Int = defaultIconColor
+    private var exciteAnimator: Animator? = null
+
+    private var typeState: Enum<TypeStates> = TypeStates.STATIC
+    private val optionalState: EnumSet<OptionalStates> = EnumSet.noneOf(OptionalStates::class.java)
+
+    /**
+     * Setter to set if the DockItem is dynamic.
+     */
+    fun setDynamic(dynamicIconStrokeColor: Int) {
+        typeState = TypeStates.DYNAMIC
+        this.dynamicIconStrokeColor = dynamicIconStrokeColor
+    }
+
+    /**
+     * Setter to set if the DockItem is static.
+     */
+    fun setStatic() {
+        typeState = TypeStates.STATIC
+        this.dynamicIconStrokeColor = defaultIconColor
+    }
+
+    /**
+     * Setter to set if the DockItem is excited. Returns true if the state was changed.
+     */
+    fun setExcited(isExcited: Boolean): Boolean {
+        if ((isExcited && optionalState.contains(OptionalStates.EXCITED)) ||
+            (!isExcited && !optionalState.contains(OptionalStates.EXCITED))
+        ) {
+            return false
+        }
+
+        if (isExcited) {
+            optionalState.add(OptionalStates.EXCITED)
+        } else {
+            optionalState.remove(OptionalStates.EXCITED)
+        }
+        return true
+    }
+
+    /**
+     * Setter to set if the DockItem is updating to another [DockAppItem]
+     * @param updatingColor color to use when app is updating. Generally the icon color of the next
+     * [DockAppItem]
+     * @return Returns true if the state was changed, false otherwise
+     */
+    fun setUpdating(isUpdating: Boolean, updatingColor: Int?): Boolean {
+        if ((isUpdating && optionalState.contains(OptionalStates.UPDATING)) ||
+            (!isUpdating && !optionalState.contains(OptionalStates.UPDATING))
+        ) {
+            return false
+        }
+
+        if (isUpdating) {
+            optionalState.add(OptionalStates.UPDATING)
+        } else {
+            optionalState.remove(OptionalStates.UPDATING)
+        }
+        this.updatingColor = updatingColor ?: defaultIconColor
+        return true
+    }
+
+    /**
+     * Setter to set if the DockItem is restricted. Returns true if the state was changed.
+     */
+    fun setRestricted(isRestricted: Boolean): Boolean {
+        if ((isRestricted && optionalState.contains(OptionalStates.RESTRICTED)) ||
+            (!isRestricted && !optionalState.contains(OptionalStates.RESTRICTED))
+        ) {
+            return false
+        }
+
+        if (isRestricted) {
+            optionalState.add(OptionalStates.RESTRICTED)
+        } else {
+            optionalState.remove(OptionalStates.RESTRICTED)
+        }
+        return true
+    }
+
+    /** Tracks whether the app item has an active media session or not */
+    fun setHasActiveMediaSession(
+        hasMediaSession: Boolean
+    ): Boolean {
+        if ((hasMediaSession && optionalState.contains(OptionalStates.ACTIVE_MEDIA)) ||
+            (!hasMediaSession && !optionalState.contains(OptionalStates.ACTIVE_MEDIA))
+        ) {
+            return false
+        }
+
+        if (hasMediaSession) {
+            optionalState.add(OptionalStates.ACTIVE_MEDIA)
+        } else {
+            optionalState.remove(OptionalStates.ACTIVE_MEDIA)
+        }
+        return true
+    }
+
+    /** @return whether the view should be restricted or not */
+    fun shouldBeRestricted(): Boolean {
+        return optionalState.contains(OptionalStates.RESTRICTED) &&
+                !optionalState.contains(OptionalStates.ACTIVE_MEDIA)
+    }
+
+    /**
+     * Updates the [appIcon] based on the current state
+     */
+    fun updateViewBasedOnState(appIcon: ShapeableImageView) {
+        if (DEBUG) {
+            Log.d(
+                TAG,
+                "updateViewBasedOnState, typeState: $typeState, optionalState: $optionalState"
+            )
+        }
+        if (exciteAnimator != null) {
+            exciteAnimator?.cancel()
+            exciteAnimator = null
+        }
+        appIcon.strokeColor = ColorStateList.valueOf(getStrokeColor())
+        appIcon.strokeWidth = getStrokeWidth()
+        appIcon.colorFilter = getColorFilter()
+        val cp = getContentPadding()
+        // ContentPadding should not be set before the measure phase of the view otherwise it might
+        // set incorrect padding values on the view.
+        appIcon.post { appIcon.setContentPadding(cp, cp, cp, cp) }
+    }
+
+    /**
+     * Animate the [appIcon] to be excited or reset after being excited.
+     */
+    fun animateAppIconExcited(appIcon: ShapeableImageView) {
+        val isAnimationOngoing = exciteAnimator?.isRunning ?: false
+        if (DEBUG) {
+            Log.d(
+                TAG,
+                "Excite animation{ " +
+                        "isExciting: ${optionalState.contains(OptionalStates.EXCITED)}, " +
+                        "isAnimationOngoing: $isAnimationOngoing }"
+            )
+        }
+        exciteAnimator?.cancel()
+
+        val toStrokeWidth: Float = getStrokeWidth()
+        val toContentPadding: Int = getContentPadding()
+        val toStrokeColor: Int = getStrokeColor()
+        val toColorFilterAlpha: Float = if (optionalState.contains(OptionalStates.EXCITED)) {
+            excitedIconColorFilterAlpha
+        } else {
+            INITIAL_COLOR_FILTER_ALPHA
+        }
+
+        val successCallback = {
+            exciteAnimator = null
+            updateViewBasedOnState(appIcon)
+        }
+
+        val failureCallback = {
+            exciteAnimator = null
+            updateViewBasedOnState(appIcon)
+        }
+
+        exciteAnimator = ExcitementAnimationHelper.getExcitementAnimator(
+            appIcon,
+            exciteAnimationDuration.toLong(),
+            toStrokeWidth,
+            toStrokeColor,
+            toContentPadding,
+            toColorFilterAlpha,
+            successCallback,
+            failureCallback
+        )
+        exciteAnimator?.start()
+    }
+
+    private fun getStrokeColor(): Int {
+        if (optionalState.contains(OptionalStates.UPDATING)) {
+            return updatingColor
+        } else if (shouldBeRestricted()) {
+            return restrictedIconStrokeColor
+        } else if (optionalState.contains(OptionalStates.EXCITED)) {
+            return excitedIconStrokeColor
+        } else if (typeState == TypeStates.STATIC) {
+            return staticIconStrokeColor
+        } else if (typeState == TypeStates.DYNAMIC) {
+            return dynamicIconStrokeColor
+        }
+        return defaultIconColor
+    }
+
+    private fun getStrokeWidth(): Float {
+        if (optionalState.contains(OptionalStates.EXCITED)) {
+            return excitedIconStrokeWidth
+        } else if (typeState == TypeStates.STATIC) {
+            return staticIconStrokeWidth
+        } else if (typeState == TypeStates.DYNAMIC) {
+            return dynamicIconStrokeWidth
+        }
+        return DEFAULT_STROKE_WIDTH
+    }
+
+    private fun getContentPadding(): Int {
+        return getContentPaddingFromStrokeWidth(getStrokeWidth())
+    }
+
+    private fun getColorFilter(): ColorFilter? {
+        if (optionalState.contains(OptionalStates.UPDATING)) {
+            return PorterDuffColorFilter(updatingColor, PorterDuff.Mode.SRC_OVER)
+        } else if (shouldBeRestricted()){
+            return restrictedColorFilter
+        } else if (optionalState.contains(OptionalStates.EXCITED)) {
+            return excitedColorFilter
+        }
+        return null
+    }
+
+    private fun getContentPaddingFromStrokeWidth(strokeWidth: Float): Int =
+        floor(strokeWidth / 2).toInt()
+}
diff --git a/docklib/src/com/android/car/docklib/view/DockItemViewHolder.kt b/docklib/src/com/android/car/docklib/view/DockItemViewHolder.kt
index 9750f36..a718265 100644
--- a/docklib/src/com/android/car/docklib/view/DockItemViewHolder.kt
+++ b/docklib/src/com/android/car/docklib/view/DockItemViewHolder.kt
@@ -1,92 +1,170 @@
+/*
+ * Copyright (C) 2023 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.docklib.view
 
+import android.car.media.CarMediaManager
 import android.content.ComponentName
-import android.content.Intent
-import android.content.res.ColorStateList
+import android.content.Context
 import android.graphics.Color
+import android.graphics.ColorMatrix
+import android.graphics.ColorMatrixColorFilter
 import android.graphics.Point
 import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.os.Build
+import android.util.TypedValue
 import android.view.View
-import androidx.core.view.setPadding
 import androidx.recyclerview.widget.RecyclerView
+import com.android.car.docklib.DockInterface
 import com.android.car.docklib.R
 import com.android.car.docklib.data.DockAppItem
 import com.google.android.material.imageview.ShapeableImageView
-import java.util.function.Consumer
+import java.util.concurrent.Callable
+import java.util.concurrent.Executors
+import java.util.concurrent.Future
+import java.util.concurrent.TimeUnit
 
+/**
+ * ViewHolder of {@link DockAppItem}
+ */
 class DockItemViewHolder(
-    itemView: View,
-    private val intentDelegate: Consumer<Intent>,
+        private val dockController: DockInterface,
+        itemView: View,
+        private val userContext: Context,
+        private val carMediaManager: CarMediaManager?
 ) : RecyclerView.ViewHolder(itemView) {
 
     companion object {
-        private const val DEFAULT_STROKE_WIDTH = 0f
+        private const val TAG = "DockItemViewHolder"
+        private val DEBUG = Build.isDebuggable()
+
+        /**
+         * Cleanup callback is used to reset/remove any pending views so it should be called after
+         * the new item is ready to be shown. This delay ensures new view is ready before the
+         * cleanup.
+         *
+         * todo(b/319285942): Remove fixed timer
+         */
+        private const val CLEANUP_DELAY = 500L
+        private const val MAX_WAIT_TO_COMPUTE_ICON_COLOR_MS = 500L
     }
 
-    private val staticIconStrokeWidth: Float
-    private val dynamicIconStrokeWidth: Float
-    private val excitedIconStrokeWidth: Float
-    private val iconStrokeColor: Int
-    private val excitedIconStrokeColor: Int
-    private val appIcon: ShapeableImageView
+    private val staticIconStrokeWidth = itemView.resources
+            .getDimension(R.dimen.icon_stroke_width_static)
+    private val defaultIconColor = itemView.resources.getColor(
+            R.color.icon_default_color,
+            null // theme
+    )
+    private val appIcon: ShapeableImageView = itemView.requireViewById(R.id.dock_app_icon)
+    private val iconColorExecutor = Executors.newSingleThreadExecutor()
+    private val dockDragListener: DockDragListener
+    private val dockItemViewController: DockItemViewController
+    private var dockItemClickListener: DockItemClickListener? = null
     private var dockItemLongClickListener: DockItemLongClickListener? = null
-    private var iconStrokeWidth: Float = DEFAULT_STROKE_WIDTH
+    private var droppedIconColor: Int = defaultIconColor
+    private var iconColorFuture: Future<Int>? = null
 
     init {
-        staticIconStrokeWidth = itemView.resources.getDimension(R.dimen.static_icon_stroke_width)
-        dynamicIconStrokeWidth = itemView.resources.getDimension(R.dimen.dynamic_icon_stroke_width)
-        excitedIconStrokeWidth = itemView.resources.getDimension(R.dimen.icon_stroke_width_excited)
-        // todo(b/314859977): iconStrokeColor should be decided by the app primary color
-        iconStrokeColor = itemView.resources.getColor(
-            R.color.icon_default_stroke_color,
-            null // theme
+        val typedValue = TypedValue()
+        itemView.resources.getValue(
+                R.dimen.icon_colorFilter_alpha_excited,
+                typedValue,
+                true // resolveRefs
         )
-        excitedIconStrokeColor = itemView.resources.getColor(
-            R.color.icon_excited_stroke_color,
-            null // theme
+        val excitedIconColorFilterAlpha = typedValue.float
+
+        dockItemViewController = DockItemViewController(
+            staticIconStrokeWidth,
+            dynamicIconStrokeWidth = itemView.resources
+                .getDimension(R.dimen.icon_stroke_width_dynamic),
+            excitedIconStrokeWidth = itemView.resources
+                .getDimension(R.dimen.icon_stroke_width_excited),
+            staticIconStrokeColor = itemView.resources.getColor(
+                R.color.icon_static_stroke_color,
+                null // theme
+            ),
+            excitedIconStrokeColor = itemView.resources.getColor(
+                R.color.icon_excited_stroke_color,
+                null // theme
+            ),
+            restrictedIconStrokeColor = itemView.resources.getColor(
+                R.color.icon_restricted_stroke_color,
+                null // theme
+            ),
+            defaultIconColor,
+            excitedColorFilter = PorterDuffColorFilter(
+                Color.argb(excitedIconColorFilterAlpha, 0f, 0f, 0f),
+                PorterDuff.Mode.DARKEN
+            ),
+            restrictedColorFilter = ColorMatrixColorFilter(
+                    ColorMatrix().apply { setSaturation(0f) }
+            ),
+            excitedIconColorFilterAlpha,
+            exciteAnimationDuration = itemView.resources
+                .getInteger(R.integer.excite_icon_animation_duration_ms)
         )
-        appIcon = itemView.requireViewById(R.id.dock_app_icon)
-    }
 
-    fun bind(dockAppItem: DockAppItem?) {
-        reset()
-        if (dockAppItem == null) return
-
-        itemTypeChanged(dockAppItem)
-
-        appIcon.contentDescription = dockAppItem.name
-        appIcon.setImageDrawable(dockAppItem.icon)
-        appIcon.setOnClickListener {
-            val intent =
-                Intent(Intent.ACTION_MAIN)
-                    .setComponent(dockAppItem.component)
-                    .addCategory(Intent.CATEGORY_LAUNCHER)
-                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-            intentDelegate.accept(intent)
-        }
-        dockItemLongClickListener = DockItemLongClickListener(
-            dockAppItem,
-            pinItemClickDelegate =
-            { (bindingAdapter as? DockAdapter)?.pinItemAt(bindingAdapterPosition) },
-            unpinItemClickDelegate =
-            { (bindingAdapter as? DockAdapter)?.unpinItemAt(bindingAdapterPosition) }
-        )
-        appIcon.onLongClickListener = dockItemLongClickListener
-
-        itemView.setOnDragListener(
-            DockDragListener(
-                viewHolder = this,
+        dockDragListener = DockDragListener(
+                resources = this.itemView.resources,
                 object : DockDragListener.Callback {
-                    override fun dragAccepted(componentName: ComponentName) {
-                        pinNewItem(componentName)
+                    override fun dropSuccessful(
+                            componentName: ComponentName,
+                            cleanupCallback: Runnable?
+                    ) {
+                        (bindingAdapter as? DockAdapter)
+                                ?.setCallback(bindingAdapterPosition, cleanupCallback)
+                        dockController.appPinned(componentName, bindingAdapterPosition)
+                    }
+
+                    override fun dropAnimationsStarting(componentName: ComponentName) {
+                        dockItemViewController.setExcited(isExcited = false)
+                        // todo(b/320543972): Increase efficiency of dropping
+                        iconColorFuture = iconColorExecutor.submit(
+                                Callable { dockController.getIconColorWithScrim(componentName) }
+                        )
+                    }
+
+                    override fun dropAnimationScaleDownComplete(componentName: ComponentName) {
+                        droppedIconColor = iconColorFuture?.get(
+                                MAX_WAIT_TO_COMPUTE_ICON_COLOR_MS,
+                                TimeUnit.MILLISECONDS
+                        ) ?: defaultIconColor
+                        if (dockItemViewController.setUpdating(
+                            isUpdating = true,
+                            updatingColor = droppedIconColor
+                        )) {
+                            dockItemViewController.updateViewBasedOnState(appIcon)
+                        }
+                    }
+
+                    override fun dropAnimationComplete(componentName: ComponentName) {
+                        dockItemViewController.setUpdating(isUpdating = false, updatingColor = null)
                     }
 
                     override fun exciteView() {
-                        exciteAppIcon()
+                        if (dockItemViewController.setExcited(isExcited = true)) {
+                            dockItemViewController.animateAppIconExcited(appIcon)
+                        }
                     }
 
                     override fun resetView() {
-                        resetAppIcon()
+                        if (dockItemViewController.setExcited(isExcited = false)) {
+                            dockItemViewController.animateAppIconExcited(appIcon)
+                        }
                     }
 
                     override fun getDropContainerLocation(): Point {
@@ -97,65 +175,83 @@
                     override fun getDropLocation(): Point {
                         val iconLocation = appIcon.locationOnScreen
                         return Point(
-                            (iconLocation[0] + iconStrokeWidth.toInt()),
-                            (iconLocation[1] + iconStrokeWidth.toInt())
+                                (iconLocation[0] + staticIconStrokeWidth.toInt()),
+                                (iconLocation[1] + staticIconStrokeWidth.toInt())
                         )
                     }
 
                     override fun getDropWidth(): Float {
-                        return (appIcon.width.toFloat() - iconStrokeWidth * 2)
+                        return (appIcon.width.toFloat() - staticIconStrokeWidth * 2)
                     }
 
                     override fun getDropHeight(): Float {
-                        return (appIcon.height.toFloat() - iconStrokeWidth * 2)
+                        return (appIcon.height.toFloat() - staticIconStrokeWidth * 2)
                     }
-                }
-            )
+                })
+    }
+
+    /**
+     * @param callback [Runnable] to be called after the new item is bound
+     */
+    fun bind(
+        dockAppItem: DockAppItem,
+        isUxRestrictionEnabled: Boolean,
+        callback: Runnable? = null,
+        hasActiveMediaSessions: Boolean
+    ) {
+        itemTypeChanged(dockAppItem)
+        appIcon.contentDescription = dockAppItem.name
+        appIcon.setImageDrawable(dockAppItem.icon)
+        appIcon.postDelayed({ callback?.run() }, CLEANUP_DELAY)
+        dockItemClickListener = DockItemClickListener(
+            dockController,
+            dockAppItem,
+            isRestricted = !dockAppItem.isDistractionOptimized && isUxRestrictionEnabled &&
+              !hasActiveMediaSessions
         )
+        appIcon.setOnClickListener(dockItemClickListener)
+        setUxRestrictions(dockAppItem, isUxRestrictionEnabled)
+        setHasActiveMediaSession(hasActiveMediaSessions)
+        dockItemLongClickListener = DockItemLongClickListener(
+                dockAppItem,
+                pinItemClickDelegate = { dockController.appPinned(dockAppItem.id) },
+                unpinItemClickDelegate = { dockController.appUnpinned(dockAppItem.id) },
+            dockAppItem.component,
+            userContext,
+            carMediaManager,
+            dockController.getMediaServiceComponents()
+        )
+        appIcon.onLongClickListener = dockItemLongClickListener
+
+        itemView.setOnDragListener(dockDragListener)
     }
 
     fun itemTypeChanged(dockAppItem: DockAppItem) {
-        iconStrokeWidth = when (dockAppItem.type) {
-            DockAppItem.Type.STATIC -> staticIconStrokeWidth
-            DockAppItem.Type.DYNAMIC -> dynamicIconStrokeWidth
-        }
-        appIcon.strokeWidth = iconStrokeWidth
+        when (dockAppItem.type) {
+            DockAppItem.Type.DYNAMIC ->
+                dockItemViewController.setDynamic(dockAppItem.iconColorWithScrim)
 
-        appIcon.invalidate()
+            DockAppItem.Type.STATIC -> dockItemViewController.setStatic()
+        }
+        dockItemViewController.updateViewBasedOnState(appIcon)
         dockItemLongClickListener?.setDockAppItem(dockAppItem)
     }
 
-    private fun pinNewItem(componentName: ComponentName) {
-        (bindingAdapter as? DockAdapter)?.pinItemAt(bindingAdapterPosition, componentName)
+    /** Set if the Ux restrictions are enabled */
+    fun setUxRestrictions(dockAppItem: DockAppItem, isUxRestrictionEnabled: Boolean) {
+        val shouldBeRestricted = !dockAppItem.isDistractionOptimized && isUxRestrictionEnabled
+        if (dockItemViewController.setRestricted(shouldBeRestricted)) {
+            dockItemViewController.updateViewBasedOnState(appIcon)
+            dockItemClickListener?.setIsRestricted(dockItemViewController.shouldBeRestricted())
+        }
     }
 
-    private fun exciteAppIcon() {
-        // todo(b/312737692): add animations
-        appIcon.strokeColor = ColorStateList.valueOf(excitedIconStrokeColor)
-        appIcon.setColorFilter(Color.argb(0.3f, 0f, 0f, 0f), PorterDuff.Mode.DARKEN)
-        appIcon.strokeWidth = excitedIconStrokeWidth
-        appIcon.setPadding(getPaddingFromStrokeWidth(excitedIconStrokeWidth))
-        appIcon.invalidate()
+    /** Set if item has an active media session */
+    fun setHasActiveMediaSession(hasActiveMediaSession: Boolean) {
+        if (dockItemViewController.setHasActiveMediaSession(hasActiveMediaSession)) {
+            dockItemViewController.updateViewBasedOnState(appIcon)
+            dockItemClickListener?.setIsRestricted(dockItemViewController.shouldBeRestricted())
+        }
     }
-
-    private fun resetAppIcon() {
-        appIcon.strokeColor = ColorStateList.valueOf(iconStrokeColor)
-        appIcon.colorFilter = null
-        appIcon.strokeWidth = iconStrokeWidth
-        appIcon.setPadding(getPaddingFromStrokeWidth(iconStrokeWidth))
-        appIcon.invalidate()
-    }
-
-    private fun reset() {
-        iconStrokeWidth = DEFAULT_STROKE_WIDTH
-        resetAppIcon()
-        appIcon.contentDescription = null
-        appIcon.setImageDrawable(null)
-        appIcon.setOnClickListener(null)
-        itemView.setOnDragListener(null)
-    }
-
-    private fun getPaddingFromStrokeWidth(strokeWidth: Float): Int = (strokeWidth / 2).toInt()
-
     // TODO: b/301484526 Add animation when app icon is changed
 }
diff --git a/docklib/src/com/android/car/docklib/view/DockView.kt b/docklib/src/com/android/car/docklib/view/DockView.kt
index c0b2564..858ce3d 100644
--- a/docklib/src/com/android/car/docklib/view/DockView.kt
+++ b/docklib/src/com/android/car/docklib/view/DockView.kt
@@ -1,12 +1,25 @@
+/*
+ * Copyright (C) 2023 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.docklib.view
 
 import android.content.Context
-import android.graphics.Rect
 import android.util.AttributeSet
-import android.view.View
 import android.widget.FrameLayout
 import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.ItemDecoration
 import com.android.car.docklib.R
 
 class DockView
@@ -23,23 +36,6 @@
     init {
         inflate(context, R.layout.dock_view, this)
         recyclerView = requireViewById(R.id.recycler_view)
-        recyclerView.addItemDecoration(
-            object : ItemDecoration() {
-                override fun getItemOffsets(
-                    outRect: Rect,
-                    view: View,
-                    parent: RecyclerView,
-                    state: RecyclerView.State
-                ) {
-                    with(outRect) {
-                        if (parent.getChildAdapterPosition(view) != 0) {
-                            // TODO: b/301484526 set margins in case of RTL and vertical
-                            left = resources.getDimensionPixelSize(R.dimen.dock_item_spacing)
-                        }
-                    }
-                }
-            }
-        )
     }
 
     fun getAdapter() = recyclerView.adapter as DockAdapter
diff --git a/docklib/src/com/android/car/docklib/view/animation/ExcitementAnimationHelper.kt b/docklib/src/com/android/car/docklib/view/animation/ExcitementAnimationHelper.kt
new file mode 100644
index 0000000..898300b
--- /dev/null
+++ b/docklib/src/com/android/car/docklib/view/animation/ExcitementAnimationHelper.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 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.docklib.view.animation
+
+import android.content.res.ColorStateList
+import android.graphics.Color
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.os.Build
+import android.util.Log
+import android.util.Property
+import android.view.View
+import android.widget.ImageView
+import androidx.annotation.ColorInt
+import androidx.annotation.VisibleForTesting
+import androidx.core.animation.Animator
+import androidx.core.animation.ArgbEvaluator
+import androidx.core.animation.ObjectAnimator
+import androidx.core.animation.PathInterpolator
+import androidx.core.animation.PropertyValuesHolder
+import androidx.core.graphics.alpha
+import androidx.core.graphics.toColorLong
+import com.google.android.material.imageview.ShapeableImageView
+
+/**
+ * Helper class to build an [Animator] that can animate a view to be excited or reset. This is
+ * generally used to excite the dock item when something is hovered over it.
+ */
+class ExcitementAnimationHelper {
+    companion object {
+        private const val TAG = "ExcitementAnimator"
+        private val DEBUG = Build.isDebuggable()
+        private const val PVH_STROKE_WIDTH = "strokeWidth"
+        private const val PVH_STROKE_COLOR = "strokeColor"
+        private const val PVH_PADDING = "padding"
+        private const val PVH_COLOR_FILTER = "colorFilter"
+
+        /**
+         * Creates an [Animator] using the final values to be animated to. The initial values to
+         * start the animation from are taken from the View.
+         *
+         * Some assumptions:
+         * 1. strokeWidth, strokeColor and contentPadding will only be animated if the given [view]
+         *   is of type [ShapeableImageView]
+         * 2. colorFilter will only be animated if the given [view] is of type [ImageView]
+         * 3. The colorFilter set on the [view] should be [PorterDuffColorFilter] to be able to get
+         *   the initial value from.
+         *
+         *   @param view [View] that should be animated and sourced the initial animation values
+         *   from.
+         *   @param animationDuration length of the animation
+         *   @param toStrokeWidth final strokeWidth value
+         *   @param toStrokeColor final strokeColor value
+         *   @param toContentPadding final content padding value, applied to all sides
+         *   @param toColorFilterAlpha final alpha value of the colorFilter
+         *   @param successCallback called when the animation has completed successfully
+         *   @param failureCallback called when the animation is unsuccessful/cancelled
+         */
+        fun getExcitementAnimator(
+            view: View,
+            animationDuration: Long,
+            toStrokeWidth: Float,
+            @ColorInt toStrokeColor: Int,
+            toContentPadding: Int,
+            toColorFilterAlpha: Float,
+            successCallback: Runnable,
+            failureCallback: Runnable
+        ): Animator {
+            // todo(b/312718542): hidden api(PorterDuffColorFilter.getAlpha) usage
+            return getExcitementAnimator(
+                view,
+                animationDuration,
+                fromStrokeWidth = getStrokeWidth(view, defaultWidth = 0f),
+                toStrokeWidth = toStrokeWidth,
+                fromStrokeColor = getStrokeColor(view, defaultColor = Color.rgb(0f, 0f, 0f)),
+                toStrokeColor = toStrokeColor,
+                fromPadding = getAverageContentPadding(view, defaultContentPadding = 0),
+                toContentPadding = toContentPadding,
+                fromColorFilterAlpha = getColorFilterAlpha(view, defaultColorFilterAlpha = 0f),
+                toColorFilterAlpha = toColorFilterAlpha,
+                successCallback,
+                failureCallback
+            )
+        }
+
+        private fun getExcitementAnimator(
+            view: View,
+            animationDuration: Long,
+            fromStrokeWidth: Float,
+            toStrokeWidth: Float,
+            @ColorInt fromStrokeColor: Int,
+            @ColorInt toStrokeColor: Int,
+            fromPadding: Int,
+            toContentPadding: Int,
+            fromColorFilterAlpha: Float,
+            toColorFilterAlpha: Float,
+            successCallback: Runnable,
+            failureCallback: Runnable
+        ): Animator {
+            if (DEBUG) {
+                Log.d(
+                    TAG,
+                    "getExcitementAnimator{" +
+                            "view: $view, " +
+                            "animationDuration: $animationDuration, " +
+                            "fromStrokeWidth: $fromStrokeWidth," +
+                            "toStrokeWidth: $toStrokeWidth," +
+                            "fromStrokeColor: $fromStrokeColor," +
+                            "toStrokeColor: $toStrokeColor," +
+                            "fromPadding: $fromPadding," +
+                            "toContentPadding: $toContentPadding," +
+                            "fromColorFilterAlpha: $fromColorFilterAlpha," +
+                            "toColorFilterAlpha: $toColorFilterAlpha," +
+                            "}"
+                )
+            }
+            var pvhStrokeWidth: PropertyValuesHolder? = null
+            val pvhPadding: PropertyValuesHolder?
+            var pvhColorFilter: PropertyValuesHolder? = null
+            var pvhStrokeColor: PropertyValuesHolder? = null
+
+            if (view is ShapeableImageView) {
+                pvhStrokeWidth = PropertyValuesHolder.ofFloat(
+                    PVH_STROKE_WIDTH,
+                    fromStrokeWidth,
+                    toStrokeWidth
+                )
+
+                pvhStrokeColor =
+                    PropertyValuesHolder.ofObject(
+                        object : Property<View, Int>(Int::class.java, PVH_STROKE_COLOR) {
+                            override fun get(view: View?): Int {
+                                return getStrokeColor(view, defaultColor = fromStrokeColor)
+                            }
+
+                            override fun set(view: View?, value: Int?) {
+                                if (view is ShapeableImageView && value != null) {
+                                    view.strokeColor = ColorStateList.valueOf(value)
+                                }
+                            }
+                        },
+                        ArgbEvaluator.getInstance(),
+                        fromStrokeColor,
+                        toStrokeColor
+                    )
+            }
+
+            pvhPadding = PropertyValuesHolder.ofInt(
+                object : Property<View, Int>(Int::class.java, PVH_PADDING) {
+                    override fun get(view: View?): Int {
+                        return if (view != null) {
+                            getAverageContentPadding(view, defaultContentPadding = 0)
+                        } else {
+                            0
+                        }
+                    }
+
+                    override fun set(view: View?, value: Int?) {
+                        if (view != null && value != null) setContentPadding(view, value)
+                    }
+                },
+                fromPadding,
+                toContentPadding
+            )
+
+            if (view is ImageView) {
+                pvhColorFilter =
+                    PropertyValuesHolder.ofFloat(
+                        object : Property<View, Float>(
+                            Float::class.java,
+                            PVH_COLOR_FILTER
+                        ) {
+                            override fun get(view: View?): Float {
+                                return getColorFilterAlpha(view, defaultColorFilterAlpha = 0f)
+                            }
+
+                            override fun set(view: View?, value: Float?) {
+                                if (view is ImageView && value != null) {
+                                    view.colorFilter = PorterDuffColorFilter(
+                                        Color.argb(value, 0f, 0f, 0f),
+                                        PorterDuff.Mode.DARKEN
+                                    )
+                                }
+                            }
+                        },
+                        fromColorFilterAlpha,
+                        toColorFilterAlpha
+                    )
+            }
+
+            val animator = ObjectAnimator.ofPropertyValuesHolder(
+                view,
+                pvhStrokeWidth,
+                pvhPadding,
+                pvhColorFilter,
+                pvhStrokeColor
+            )
+            animator.setDuration(animationDuration)
+            animator.interpolator = PathInterpolator(0f, 0f, 0f, 1f)
+            animator.addListener(getAnimatorListener(successCallback, failureCallback))
+            return animator
+        }
+
+        @VisibleForTesting
+        fun getAnimatorListener(
+            successCallback: Runnable,
+            failureCallback: Runnable
+        ): Animator.AnimatorListener {
+            return object : Animator.AnimatorListener {
+                private var isCancelled = false
+                override fun onAnimationStart(animator: Animator) {
+                    isCancelled = false
+                }
+
+                override fun onAnimationEnd(animator: Animator) {
+                    if (!isCancelled) successCallback.run()
+                }
+
+                override fun onAnimationCancel(animator: Animator) {
+                    isCancelled = true
+                    failureCallback.run()
+                }
+
+                override fun onAnimationRepeat(animator: Animator) {
+                    // no-op
+                }
+            }
+        }
+
+        private fun setContentPadding(view: View, contentPadding: Int) {
+            (view as? ShapeableImageView)?.setContentPadding(
+                contentPadding,
+                contentPadding,
+                contentPadding,
+                contentPadding
+            )
+        }
+
+        private fun getAverageContentPadding(view: View, defaultContentPadding: Int): Int {
+            (view as? ShapeableImageView)?.let {
+                return (it.contentPaddingStart + it.contentPaddingEnd +
+                        it.contentPaddingTop + it.contentPaddingBottom) / 4
+            }
+            return defaultContentPadding
+        }
+
+        private fun getColorFilterAlpha(view: View?, defaultColorFilterAlpha: Float): Float {
+            return ((view as? ImageView)
+                ?.colorFilter as? PorterDuffColorFilter)
+                ?.color?.toColorLong()?.alpha ?: defaultColorFilterAlpha
+        }
+
+        private fun getStrokeWidth(view: View?, defaultWidth: Float): Float {
+            return (view as? ShapeableImageView)?.strokeWidth ?: defaultWidth
+        }
+
+        private fun getStrokeColor(view: View?, @ColorInt defaultColor: Int): Int {
+            return (view as? ShapeableImageView)
+                ?.strokeColor?.getColorForState(null, defaultColor) ?: defaultColor
+        }
+    }
+}
diff --git a/docklib/tests/Android.bp b/docklib/tests/Android.bp
index dd6432e..49b2272 100644
--- a/docklib/tests/Android.bp
+++ b/docklib/tests/Android.bp
@@ -1,5 +1,5 @@
 //
-// Copyright (C) 2023 Google Inc.
+// Copyright (C) 2024 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.
@@ -16,6 +16,7 @@
 
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
 }
 
 android_test {
@@ -23,12 +24,12 @@
 
     srcs: [
         "src/**/*.java",
-        "src/**/*.kt"
+        "src/**/*.kt",
     ],
 
     libs: [
         "android.car",
-        "android.test.base",
+        "android.test.base.stubs.system",
     ],
 
     optimize: {
diff --git a/docklib/tests/src/com/android/car/docklib/DockHelperTest.kt b/docklib/tests/src/com/android/car/docklib/DockHelperTest.kt
deleted file mode 100644
index 1e788f6..0000000
--- a/docklib/tests/src/com/android/car/docklib/DockHelperTest.kt
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (C) 2023 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.docklib
-
-import android.car.content.pm.CarPackageManager
-import android.content.Context
-import android.content.pm.ActivityInfo
-import android.content.pm.PackageManager
-import android.content.res.Resources
-import android.graphics.drawable.Drawable
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.anyInt
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-
-@RunWith(AndroidJUnit4::class)
-class DockHelperTest {
-
-    private val context = mock(Context::class.java)
-    private val carPackageManager = mock(CarPackageManager::class.java)
-    private val packageManager = mock(PackageManager::class.java)
-
-    @Test
-    fun defaultApps_getCorrectAppsFromConfig() {
-        val resources = mock(Resources::class.java)
-        val item1 =
-            TestUtils.createAppItem(
-                app = "item1",
-                name = "item1",
-                icon = mock(Drawable::class.java),
-                isDrivingOptimized = true
-            )
-        val item2 =
-            TestUtils.createAppItem(
-                app = "item2",
-                name = "item2",
-                icon = mock(Drawable::class.java),
-                isDrivingOptimized = false
-            )
-        `when`(context.resources).thenReturn(resources)
-        `when`(context.packageManager).thenReturn(packageManager)
-        `when`(resources.getStringArray(anyInt()))
-            .thenReturn(
-                arrayOf(item1.component.flattenToString(), item2.component.flattenToString())
-            )
-        `when`(packageManager.getApplicationIcon(item1.component.packageName))
-            .thenReturn(item1.icon)
-        `when`(packageManager.getApplicationIcon(item2.component.packageName))
-            .thenReturn(item2.icon)
-        val activityInfo1 = mock(ActivityInfo::class.java)
-        activityInfo1.name = item1.name
-        `when`(packageManager.getActivityInfo(item1.component, 0)).thenReturn(activityInfo1)
-        val activityInfo2 = mock(ActivityInfo::class.java)
-        activityInfo2.name = item2.name
-        `when`(packageManager.getActivityInfo(item2.component, 0)).thenReturn(activityInfo2)
-        `when`(
-                carPackageManager.isActivityDistractionOptimized(
-                    item1.component.packageName,
-                    item1.component.className
-                )
-            )
-            .thenReturn(item1.isDistractionOptimized)
-        `when`(
-                carPackageManager.isActivityDistractionOptimized(
-                    item2.component.packageName,
-                    item2.component.className
-                )
-            )
-            .thenReturn(item2.isDistractionOptimized)
-
-        val defaultApps = DockHelper(context, carPackageManager).defaultApps
-
-        assertThat(defaultApps.size).isEqualTo(2)
-        assertThat(defaultApps[0]).isEqualTo(item1)
-        assertThat(defaultApps[1]).isEqualTo(item2)
-    }
-
-    @Test
-    fun toDockAppItem_fetchCorrectAppName() {
-        val item1 = TestUtils.createAppItem(app = "app", name = "name")
-        `when`(context.packageManager).thenReturn(packageManager)
-        `when`(packageManager.getApplicationIcon(any(String::class.java)))
-                .thenReturn(mock(Drawable::class.java))
-        val activityInfo1 = mock(ActivityInfo::class.java)
-        activityInfo1.name = item1.name
-        `when`(packageManager.getActivityInfo(item1.component, 0)).thenReturn(activityInfo1)
-
-        val dockAppItem = DockHelper(context, carPackageManager).toDockAppItem(item1.component)
-
-        assertThat(dockAppItem.name).isEqualTo(item1.name)
-    }
-
-    @Test
-    fun toDockAppItem_fetchCorrectAppIcon() {
-        val item1 = TestUtils.createAppItem(app = "app", icon = mock(Drawable::class.java))
-        `when`(context.packageManager).thenReturn(packageManager)
-        val activityInfo = mock(ActivityInfo::class.java)
-        activityInfo.name = ""
-        `when`(packageManager.getActivityInfo(any(), eq(0))).thenReturn(activityInfo)
-        `when`(packageManager.getApplicationIcon(item1.component.packageName))
-                .thenReturn(item1.icon)
-
-        val dockAppItem = DockHelper(context, carPackageManager).toDockAppItem(item1.component)
-
-        assertThat(dockAppItem.icon).isEqualTo(item1.icon)
-    }
-
-    @Test
-    fun toDockAppItem_fetchCorrectAppDO() {
-        val item1 = TestUtils.createAppItem(app = "app", isDrivingOptimized = true)
-        `when`(context.packageManager).thenReturn(packageManager)
-        val activityInfo = mock(ActivityInfo::class.java)
-        activityInfo.name = ""
-        `when`(packageManager.getActivityInfo(any(), eq(0))).thenReturn(activityInfo)
-        `when`(packageManager.getApplicationIcon(any(String::class.java)))
-                .thenReturn(mock(Drawable::class.java))
-        `when`(
-                carPackageManager.isActivityDistractionOptimized(
-                    item1.component.packageName,
-                    item1.component.className
-                )
-            )
-            .thenReturn(item1.isDistractionOptimized)
-
-        val dockAppItem = DockHelper(context, carPackageManager).toDockAppItem(item1.component)
-
-        assertThat(dockAppItem.isDistractionOptimized).isEqualTo(item1.isDistractionOptimized)
-    }
-
-    @Test
-    fun toDockAppItem_fetchCorrectAppInfo() {
-        val item1 =
-            TestUtils.createAppItem(
-                app = "item1",
-                name = "item1",
-                icon = mock(Drawable::class.java),
-                isDrivingOptimized = true
-            )
-        `when`(context.packageManager).thenReturn(packageManager)
-        `when`(packageManager.getApplicationIcon(item1.component.packageName))
-            .thenReturn(item1.icon)
-        val activityInfo1 = mock(ActivityInfo::class.java)
-        activityInfo1.name = item1.name
-        `when`(packageManager.getActivityInfo(item1.component, 0)).thenReturn(activityInfo1)
-        `when`(
-                carPackageManager.isActivityDistractionOptimized(
-                    item1.component.packageName,
-                    item1.component.className
-                )
-            )
-            .thenReturn(item1.isDistractionOptimized)
-
-        val dockAppItem = DockHelper(context, carPackageManager).toDockAppItem(item1.component)
-
-        assertThat(dockAppItem).isEqualTo(item1)
-    }
-}
diff --git a/docklib/tests/src/com/android/car/docklib/DockViewModelTest.kt b/docklib/tests/src/com/android/car/docklib/DockViewModelTest.kt
index d7a53d1..464b918 100644
--- a/docklib/tests/src/com/android/car/docklib/DockViewModelTest.kt
+++ b/docklib/tests/src/com/android/car/docklib/DockViewModelTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -16,203 +16,668 @@
 
 package com.android.car.docklib
 
+import android.app.ActivityManager
+import android.car.content.pm.CarPackageManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageInfo
+import android.content.pm.PackageManager
+import android.content.pm.ServiceInfo
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
 import androidx.arch.core.executor.testing.InstantTaskExecutorRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.car.docklib.data.DockAppItem
+import com.android.car.docklib.data.DockProtoDataController
+import com.android.launcher3.icons.IconFactory
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
+import org.junit.Assert.assertThrows
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class DockViewModelTest {
-    @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule()
+    private companion object {
+        private const val CURRENT_USER_ID = 10
+        private const val MAX_ITEMS = 4
+        private const val TOAST_STR = "TOAST_STR"
+    }
 
-    private lateinit var items: List<DockAppItem?>
+    @get:Rule
+    val instantTaskExecutorRule = InstantTaskExecutorRule()
+
+    private lateinit var items: List<DockAppItem>
     private lateinit var model: DockViewModel
-    private val apps =
-        listOf(
-            TestUtils.createAppItem(app = "a"),
-            TestUtils.createAppItem(app = "b"),
-            TestUtils.createAppItem(app = "c"),
-            TestUtils.createAppItem(app = "d"),
-        )
+    private val resourcesMock = mock<Resources> {}
+    private val contextMock = mock<Context> {
+        on { getString(eq(R.string.pin_failed_no_spots)) } doReturn TOAST_STR
+        on { resources } doReturn resourcesMock
+    }
+    private val packageManagerMock = mock<PackageManager> {
+        on {
+            queryIntentServices(any<Intent>(), any<PackageManager.ResolveInfoFlags>())
+        } doReturn listOf()
+    }
+    private var carPackageManagerMock = mock<CarPackageManager> {}
+    private var bitmapMock = mock<Bitmap> {}
+    private var iconFactoryMock = mock<IconFactory> {
+        on { createScaledBitmap(any<Drawable>(), anyInt()) } doReturn bitmapMock
+    }
+    private val dataController = mock<DockProtoDataController>()
 
     @Before
     fun setUp() {
-        model = DockViewModel(4) { items = it }
+        // explicitly use default items unless saved items are set
+        whenever(dataController.loadFromFile()).thenReturn(null)
+        model = createSpyDockViewModel()
+        doNothing().whenever(model).showToast(any())
     }
 
     @Test
-    fun setDefaultApps_listSetInOrder() {
-        model.updateDefaultApps(apps)
+    fun init_defaultPinnedItems_addedToDock() {
+        val defaultPinnedItems =
+                createTestComponentList(pkgPrefix = "DEFAULT_PKG", classPrefix = "DEFAULT_CLASS")
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(apps[0])
-        assertThat(items[1]).isEqualTo(apps[1])
-        assertThat(items[2]).isEqualTo(apps[2])
-        assertThat(items[3]).isEqualTo(apps[3])
+        model = createSpyDockViewModel(defaultPinnedItems = defaultPinnedItems)
+
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items[0].component).isEqualTo(defaultPinnedItems[0])
+        assertThat(items[1].component).isEqualTo(defaultPinnedItems[1])
+        assertThat(items[2].component).isEqualTo(defaultPinnedItems[2])
+        assertThat(items[3].component).isEqualTo(defaultPinnedItems[3])
     }
 
     @Test
-    fun addDynamicItem_beforeDefaultApps_index0Updated() {
-        val dynamicItem = TestUtils.createAppItem(app = "da")
+    fun init_savedPinnedItems_noItemAddedToDock() {
+        val defaultPinnedItems =
+            createTestComponentList(pkgPrefix = "DEFAULT_PKG", classPrefix = "DEFAULT_CLASS")
+        val savedPinnedItems = mapOf<Int, ComponentName>()
+        whenever(dataController.loadFromFile()).thenReturn(savedPinnedItems)
 
-        model.addDynamicItem(dynamicItem)
+        model = createSpyDockViewModel(defaultPinnedItems = defaultPinnedItems)
 
-        assertThat(items[0]).isEqualTo(dynamicItem)
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items[0].type).isEqualTo(DockAppItem.Type.DYNAMIC)
+        assertThat(items[1].type).isEqualTo(DockAppItem.Type.DYNAMIC)
+        assertThat(items[2].type).isEqualTo(DockAppItem.Type.DYNAMIC)
+        assertThat(items[3].type).isEqualTo(DockAppItem.Type.DYNAMIC)
     }
 
     @Test
-    fun setDefaultApps_afterDynamicItem_fillRemainingPositions() {
-        val dynamicItem = TestUtils.createAppItem(app = "da")
-        model.addDynamicItem(dynamicItem)
+    fun init_savedPinnedItems_onlySavedItemsAddedToDock() {
+        val defaultPinnedItems =
+            createTestComponentList(pkgPrefix = "DEFAULT_PKG", classPrefix = "DEFAULT_CLASS")
+        val savedPinnedItems =
+            mapOf(
+                0 to createNewComponent(pkg = "SAVED_PKG_0", clazz = "SAVED_CLASS_0", false),
+                2 to createNewComponent(pkg = "SAVED_PKG_2", clazz = "SAVED_CLASS_2", false),
+            )
+        whenever(dataController.loadFromFile()).thenReturn(savedPinnedItems)
 
-        model.updateDefaultApps(apps)
+        model = createSpyDockViewModel(defaultPinnedItems = defaultPinnedItems)
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem)
-        assertThat(items[1]).isEqualTo(apps[1])
-        assertThat(items[2]).isEqualTo(apps[2])
-        assertThat(items[3]).isEqualTo(apps[3])
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items[0].component).isEqualTo(savedPinnedItems[0])
+        assertThat(items[1].type).isEqualTo(DockAppItem.Type.DYNAMIC)
+        assertThat(items[2].component).isEqualTo(savedPinnedItems[2])
+        assertThat(items[3].type).isEqualTo(DockAppItem.Type.DYNAMIC)
     }
 
     @Test
-    fun addDynamicItem_allItemsDefault_index0Updated() {
-        model.updateDefaultApps(apps)
+    fun addDynamicItem_emptyDockList_index0Updated() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
 
-        val dynamicItem = TestUtils.createAppItem(app = "da")
-        model.addDynamicItem(dynamicItem)
+        model.internalItems.clear()
+        model.addDynamicItem(newComponent)
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem)
-        assertThat(items[1]).isEqualTo(apps[1])
-        assertThat(items[2]).isEqualTo(apps[2])
-        assertThat(items[3]).isEqualTo(apps[3])
-    }
-
-    @Test
-    fun addDynamicItem_someItemsDefault_index1Updated() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem1)
-        assertThat(items[1]).isEqualTo(dynamicItem2)
-        assertThat(items[2]).isEqualTo(apps[2])
-        assertThat(items[3]).isEqualTo(apps[3])
-    }
-
-    @Test
-    fun addDynamicItem_someItemsDefault_index2Updated() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-
-        val dynamicItem3 = TestUtils.createAppItem(app = "da3")
-        model.addDynamicItem(dynamicItem3)
-
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem1)
-        assertThat(items[1]).isEqualTo(dynamicItem2)
-        assertThat(items[2]).isEqualTo(dynamicItem3)
-        assertThat(items[3]).isEqualTo(apps[3])
-    }
-
-    @Test
-    fun addDynamicItem_oneItemDefault_index3Updated() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-        val dynamicItem3 = TestUtils.createAppItem(app = "da3")
-        model.addDynamicItem(dynamicItem3)
-
-        val dynamicItem4 = TestUtils.createAppItem(app = "da4")
-        model.addDynamicItem(dynamicItem4)
-
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem1)
-        assertThat(items[1]).isEqualTo(dynamicItem2)
-        assertThat(items[2]).isEqualTo(dynamicItem3)
-        assertThat(items[3]).isEqualTo(dynamicItem4)
+        assertThat(items[0].component).isEqualTo(newComponent)
     }
 
     @Test
     fun addDynamicItem_allItemsDynamic_leastRecentItemUpdated() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-        val dynamicItem3 = TestUtils.createAppItem(app = "da3")
-        model.addDynamicItem(dynamicItem3)
-        val dynamicItem4 = TestUtils.createAppItem(app = "da4")
-        model.addDynamicItem(dynamicItem4)
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        model.internalItems.clear()
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC)
+        }
 
-        val dynamicItem5 = TestUtils.createAppItem(app = "da5")
-        model.addDynamicItem(dynamicItem5)
+        model.addDynamicItem(newComponent)
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem5)
-        assertThat(items[1]).isEqualTo(dynamicItem2)
-        assertThat(items[2]).isEqualTo(dynamicItem3)
-        assertThat(items[3]).isEqualTo(dynamicItem4)
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items[0].component).isEqualTo(newComponent)
     }
 
     @Test
     fun addDynamicItem_appInDock_itemsNotChanged() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-        val dynamicItem3 = TestUtils.createAppItem(app = "da3")
-        model.addDynamicItem(dynamicItem3)
-        val dynamicItem4 = TestUtils.createAppItem(app = "da4")
-        model.addDynamicItem(dynamicItem4)
+        val existingComponent = createNewComponent(pkg = "da0", clazz = "da0")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
 
-        val dynamicItem1B = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1B)
+        model.addDynamicItem(existingComponent)
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem1)
-        assertThat(items[1]).isEqualTo(dynamicItem2)
-        assertThat(items[2]).isEqualTo(dynamicItem3)
-        assertThat(items[3]).isEqualTo(dynamicItem4)
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items.filter { it.component == existingComponent }.size).isEqualTo(1)
     }
 
     @Test
     fun addDynamicItem_appInDock_recencyRefreshed() {
-        model.updateDefaultApps(apps)
-        val dynamicItem1 = TestUtils.createAppItem(app = "da1")
-        model.addDynamicItem(dynamicItem1)
-        val dynamicItem2 = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2)
-        val dynamicItem3 = TestUtils.createAppItem(app = "da3")
-        model.addDynamicItem(dynamicItem3)
-        val dynamicItem4 = TestUtils.createAppItem(app = "da4")
-        model.addDynamicItem(dynamicItem4)
+        val existingComponent = createNewComponent(pkg = "da0", clazz = "da0")
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
 
-        val dynamicItem2B = TestUtils.createAppItem(app = "da2")
-        model.addDynamicItem(dynamicItem2B)
-        val dynamicItem5 = TestUtils.createAppItem(app = "da5")
-        model.addDynamicItem(dynamicItem5)
-        val dynamicItem6 = TestUtils.createAppItem(app = "da6")
-        model.addDynamicItem(dynamicItem6)
+        model.addDynamicItem(existingComponent)
+        model.addDynamicItem(newComponent)
 
-        assertThat(items.size).isEqualTo(4)
-        assertThat(items[0]).isEqualTo(dynamicItem5)
-        assertThat(items[1]).isEqualTo(dynamicItem2B)
-        assertThat(items[2]).isEqualTo(dynamicItem6)
-        assertThat(items[3]).isEqualTo(dynamicItem4)
+        assertThat(items.size).isEqualTo(MAX_ITEMS)
+        assertThat(items[1].component).isEqualTo(newComponent)
+    }
+
+    @Test
+    fun pinItem_itemWithIdNotInDock_itemNotPinned() {
+        val idNotInDock = UUID.nameUUIDFromBytes("idNotInDock".toByteArray())
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    id = UUID.nameUUIDFromBytes("id$i".toByteArray()),
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
+
+        model.pinItem(idNotInDock)
+
+        items.forEach { assertThat(it.type).isEqualTo(DockAppItem.Type.DYNAMIC) }
+    }
+
+    @Test
+    fun pinItem_itemWithIdInDock_itemTypeStatic_itemIdUnchanged() {
+        val idInDock = UUID.nameUUIDFromBytes("id0".toByteArray())
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    id = UUID.nameUUIDFromBytes("id$i".toByteArray()),
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
+
+        model.pinItem(idInDock)
+
+        val dockItem = items.firstOrNull { it.id == idInDock }
+        assertThat(dockItem).isNotNull()
+        assertThat(dockItem?.type).isEqualTo(DockAppItem.Type.STATIC)
+    }
+
+    @Test
+    fun pinItem_indexLessThanZero_itemNotPinned() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
+
+        model.pinItem(newComponent, indexToPin = -1)
+
+        items.forEach {
+            assertThat(it.component).isNotEqualTo(newComponent)
+        }
+    }
+
+    @Test
+    fun pinItem_indexGreaterThanMaxItems_itemNotPinned() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
+
+        model.pinItem(newComponent, indexToPin = MAX_ITEMS + 1)
+
+        items.forEach {
+            assertThat(it.component).isNotEqualTo(newComponent)
+        }
+    }
+
+    @Test
+    fun pinItem_indexProvided_itemPinnedToIndex() {
+        val indexToPin = 2
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.DYNAMIC
+            )
+        }
+
+        model.pinItem(newComponent, indexToPin)
+
+        assertThat(items[
+                indexToPin].component).isEqualTo(newComponent)
+        assertThat(items[indexToPin].type).isEqualTo(DockAppItem.Type.STATIC)
+    }
+
+    @Test
+    fun pinItem_indexNotProvided_noDynamicItemOrEmptyIndex_itemNotPinned() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.STATIC
+            )
+        }
+
+        model.pinItem(newComponent, indexToPin = null)
+
+        items.forEach { assertThat(it.component).isNotEqualTo(newComponent) }
+    }
+
+    @Test
+    fun pinItem_indexNotProvided_noDynamicItemOrEmptyIndex_toastShown() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    app = "da$i",
+                    type = DockAppItem.Type.STATIC
+            )
+        }
+
+        model.pinItem(newComponent, indexToPin = null)
+
+        verify(model).showToast(eq(TOAST_STR))
+    }
+
+    @Test
+    fun pinItem_indexNotProvided_dynamicItemsPresent_itemPinnedToFirstDynamicItemIndex() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        model.internalItems.compute(0) { _, item -> item?.copy(type = DockAppItem.Type.STATIC) }
+        model.internalItems.compute(1) { _, item -> item?.copy(type = DockAppItem.Type.DYNAMIC) }
+        model.internalItems.compute(2) { _, item -> item?.copy(type = DockAppItem.Type.DYNAMIC) }
+        model.internalItems.compute(3) { _, item -> item?.copy(type = DockAppItem.Type.STATIC) }
+
+        model.pinItem(newComponent, indexToPin = null)
+
+        val index = items.indexOfFirst { it.component == newComponent }
+        assertThat(index).isEqualTo(1)
+        assertThat(items[index].type).isEqualTo(DockAppItem.Type.STATIC)
+    }
+
+    @Test
+    fun pinItem_indexNotProvided_emptyIndexesPresent_itemPinnedToFirstEmptyIndex() {
+        val newComponent = createNewComponent(pkg = "newpkg", clazz = "newclass")
+        model.internalItems.replaceAll { _, item -> item.copy(type = DockAppItem.Type.STATIC) }
+        model.internalItems.remove(1)
+
+        model.pinItem(newComponent, indexToPin = null)
+
+        val index = items.indexOfFirst { it.component == newComponent }
+        assertThat(index).isEqualTo(1)
+        assertThat(items[index].type).isEqualTo(DockAppItem.Type.STATIC)
+    }
+
+    @Test
+    fun removeItem_itemWithIdNotInDock_itemNotRemoved() {
+        val idNotInDock = UUID.nameUUIDFromBytes("idNotInDock".toByteArray())
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    id = UUID.nameUUIDFromBytes("id$i".toByteArray()),
+                    app = "da$i",
+                    type = DockAppItem.Type.STATIC
+            )
+        }
+        val listBeforeRemove = model.internalItems.values.toList()
+
+        model.removeItem(idNotInDock)
+
+        listBeforeRemove.forEachIndexed { key, item -> assertThat(items[key]).isEqualTo(item) }
+    }
+
+    @Test
+    fun removeItem_itemWithIdInDock_itemRemoved() {
+        val idInDock = UUID.nameUUIDFromBytes("id2".toByteArray())
+        for (i in 0..<MAX_ITEMS) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    id = UUID.nameUUIDFromBytes("id$i".toByteArray()),
+                    app = "da$i",
+                    type = DockAppItem.Type.STATIC
+            )
+        }
+
+        model.removeItem(idInDock)
+
+        items.forEach { item -> assertThat(item.id).isNotEqualTo(idInDock) }
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_noRecentTasks_noLauncherApp_errorThrown() {
+        model = createSpyDockViewModel(launcherActivities = mutableSetOf())
+        doReturn(List<ActivityManager.RunningTaskInfo>(0) { return })
+                .whenever(model).getRunningTasks()
+        model.internalItems.remove(2)
+
+        assertThrows(IllegalStateException::class.java) { model.createDockList() }
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_noRecentTasks_launcherActivitiesExcluded_errorThrown() {
+        val cmpList = createTestComponentList(pkgPrefix = "testPkg", classPrefix = "testClass")
+        model = createSpyDockViewModel(
+                launcherActivities = cmpList.toMutableSet(),
+                excludedComponents = cmpList.toSet(),
+        )
+        doReturn(List<ActivityManager.RunningTaskInfo>(0) { return })
+                .whenever(model).getRunningTasks()
+        model.internalItems.remove(2)
+
+        assertThrows(IllegalStateException::class.java) { model.createDockList() }
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_noRecentTasks_launcherActivitiesAlreadyInDock_errorThrown() {
+        val launcherAppComponent = createNewComponent(pkg = "testPkg", clazz = "testClass")
+        model = createSpyDockViewModel(launcherActivities = mutableSetOf(launcherAppComponent))
+        doReturn(List<ActivityManager.RunningTaskInfo>(0) { return })
+                .whenever(model).getRunningTasks()
+        model.internalItems[2] = TestUtils.createAppItem(component = launcherAppComponent)
+        model.internalItems.remove(3)
+
+        assertThrows(IllegalStateException::class.java) { model.createDockList() }
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_noRecentTasks_randomLauncherAppAdded() {
+        val launcherActivities = createTestComponentList(
+                pkgPrefix = "testPkg",
+                classPrefix = "testClass"
+        )
+        model = createSpyDockViewModel(launcherActivities = launcherActivities.toMutableSet())
+        doReturn(List<ActivityManager.RunningTaskInfo>(0) { return })
+                .whenever(model).getRunningTasks()
+        model.internalItems.remove(2)
+
+        val dockList = model.createDockList()
+
+        assertThat(launcherActivities.contains(dockList[2].component)).isTrue()
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_noRecentTasksForCurrentUser_randomLauncherAppAdded() {
+        val launcherActivities = createTestComponentList(
+                pkgPrefix = "testPkg",
+                classPrefix = "testClass"
+        )
+        model = createSpyDockViewModel(launcherActivities = launcherActivities.toMutableSet())
+        doReturn(createTestRunningTaskInfoList(userId = CURRENT_USER_ID + 1))
+                .whenever(model)
+                .getRunningTasks()
+        model.internalItems.remove(2)
+
+        val dockList = model.createDockList()
+
+        assertThat(launcherActivities.contains(dockList[2].component)).isTrue()
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_recentTasksExcluded_randomLauncherAppAdded() {
+        val launcherActivities = createTestComponentList(
+                pkgPrefix = "testPkg",
+                classPrefix = "testClass"
+        )
+        val excludedComponent = createNewComponent(pkg = "excludedPkg", clazz = "excludedClass")
+        model = createSpyDockViewModel(
+                launcherActivities = launcherActivities.toMutableSet(),
+                excludedComponents = setOf(excludedComponent),
+        )
+        doReturn(createTestRunningTaskInfoList(component = excludedComponent))
+                .whenever(model)
+                .getRunningTasks()
+        model.internalItems.remove(2)
+
+        val dockList = model.createDockList()
+
+        assertThat(launcherActivities.contains(dockList[2].component)).isTrue()
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_recentTasksAlreadyInDock_randomLauncherAppAdded() {
+        val launcherActivities = createTestComponentList(
+                pkgPrefix = "testPkg",
+                classPrefix = "testClass"
+        )
+        val recentTaskComponent =
+                createNewComponent(pkg = "recentTaskPkg", clazz = "recentTaskClass")
+        model = createSpyDockViewModel(launcherActivities = launcherActivities.toMutableSet())
+        doReturn(createTestRunningTaskInfoList(component = recentTaskComponent))
+                .whenever(model)
+                .getRunningTasks()
+        model.internalItems[2] = TestUtils.createAppItem(component = recentTaskComponent)
+        model.internalItems.remove(3)
+
+        val dockList = model.createDockList()
+
+        assertThat(launcherActivities.contains(dockList[3].component)).isTrue()
+    }
+
+    @Test
+    fun createDockList_indexNotFilled_recentTasksAdded() {
+        val recentTaskComponent =
+                createNewComponent(pkg = "recentTaskPkg", clazz = "recentTaskClass")
+        val recentTaskList = createTestRunningTaskInfoList(component = recentTaskComponent)
+        doReturn(recentTaskList).whenever(model).getRunningTasks()
+        model.internalItems.remove(2)
+
+        val dockList = model.createDockList()
+
+        assertThat(dockList[2].component).isEqualTo(recentTaskComponent)
+    }
+
+    @Test
+    fun removeItems_itemsWithPackageNameInDock_itemsRemoved() {
+        val pkgToBeRemoved = "pkgToBeRemoved"
+        val pkgOther = "pkgOther"
+        model.internalItems[0] =
+                TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC, pkg = pkgToBeRemoved)
+        model.internalItems[1] =
+                TestUtils.createAppItem(type = DockAppItem.Type.STATIC, pkg = pkgOther)
+        model.internalItems[2] =
+                TestUtils.createAppItem(type = DockAppItem.Type.STATIC, pkg = pkgToBeRemoved)
+        model.internalItems[3] =
+                TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC, pkg = pkgOther)
+
+        model.removeItems(pkgToBeRemoved)
+
+        items.forEach { assertThat(it.component.packageName).isNotEqualTo(pkgToBeRemoved) }
+    }
+
+    @Test
+    fun removeItems_itemsWithPackageNameNotInDock_itemsNotRemoved() {
+        val pkgToBeRemoved = "pkgToBeRemoved"
+        val pkgOther = "pkgOther"
+        model.internalItems[0] =
+                TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC, pkg = pkgOther)
+        model.internalItems[1] =
+                TestUtils.createAppItem(type = DockAppItem.Type.STATIC, pkg = pkgOther)
+        model.internalItems[2] =
+                TestUtils.createAppItem(type = DockAppItem.Type.STATIC, pkg = pkgOther)
+        model.internalItems[3] =
+                TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC, pkg = pkgOther)
+
+        model.removeItems(pkgToBeRemoved)
+
+        items.forEach { assertThat(it.component.packageName).isNotEqualTo(pkgToBeRemoved) }
+    }
+
+    @Test
+    fun setCarPackageManager_distractionValuesUpdated() {
+        model = createSpyDockViewModel(shouldSetCarPackageManager = false)
+        for (i in 0..<4) {
+            model.internalItems[i] = TestUtils.createAppItem(
+                    pkg = "pkg$i",
+                    clazz = "class$i",
+                    isDrivingOptimized = false
+            )
+        }
+        whenever(carPackageManagerMock.isActivityDistractionOptimized(
+                eq("pkg0"),
+                eq("class0")
+        )).thenReturn(true)
+        whenever(carPackageManagerMock.isActivityDistractionOptimized(
+                eq("pkg1"),
+                eq("class1")
+        )).thenReturn(false)
+        whenever(carPackageManagerMock.isActivityDistractionOptimized(
+                eq("pkg2"),
+                eq("class2")
+        )).thenReturn(true)
+        whenever(carPackageManagerMock.isActivityDistractionOptimized(
+                eq("pkg3"),
+                eq("class3")
+        )).thenReturn(false)
+
+        model.setCarPackageManager(carPackageManagerMock)
+
+        items.forEach {
+            when (it.component.packageName) {
+                "pkg0" -> assertThat(it.isDistractionOptimized).isEqualTo(true)
+                "pkg1" -> assertThat(it.isDistractionOptimized).isEqualTo(false)
+                "pkg2" -> assertThat(it.isDistractionOptimized).isEqualTo(true)
+                "pkg3" -> assertThat(it.isDistractionOptimized).isEqualTo(false)
+            }
+        }
+    }
+
+    private fun createSpyDockViewModel(
+            shouldSetCarPackageManager: Boolean = true,
+            launcherActivities: MutableSet<ComponentName> = createTestComponentList(
+                    pkgPrefix = "LAUNCHER_PKG",
+                    classPrefix = "LAUNCHER_CLASS"
+            ).toMutableSet(),
+            defaultPinnedItems: List<ComponentName> = createTestComponentList(
+                    pkgPrefix = "DEFAULT_PKG",
+                    classPrefix = "DEFAULT_CLASS"
+            ),
+            excludedComponents: Set<ComponentName> = setOf(),
+    ): DockViewModel {
+        return spy(DockViewModel(
+                maxItemsInDock = MAX_ITEMS,
+                context = contextMock,
+                packageManager = packageManagerMock,
+                if (shouldSetCarPackageManager) carPackageManagerMock else null,
+                userId = CURRENT_USER_ID,
+                launcherActivities,
+                defaultPinnedItems,
+                isPackageExcluded = { false },
+                isComponentExcluded = { excludedComponents.contains(it) },
+                iconFactory = iconFactoryMock,
+                dockProtoDataController = dataController,
+                observer = { items = it },
+        ))
+    }
+
+    private fun createTestRunningTaskInfoList(
+            pkgPrefix: String = "testRunningTaskInfo_PKG",
+            classPrefix: String = "testRunningTaskInfo_CLASS",
+            component: ComponentName? = null,
+            userId: Int = CURRENT_USER_ID
+    ): List<ActivityManager.RunningTaskInfo> {
+        val recentTasks = mutableListOf<ActivityManager.RunningTaskInfo>()
+        for (i in 1..10) {
+            val t = mock<ActivityManager.RunningTaskInfo> {}
+            t.userId = userId
+            val cmp = component ?: createNewComponent(pkg = "$pkgPrefix$i", "$classPrefix$i")
+            t.baseActivity = cmp
+            val intent = mock<Intent> {}
+            intent.component = cmp
+            t.baseIntent = intent
+            recentTasks.add(t)
+        }
+        return recentTasks
+    }
+
+    private fun createTestComponentList(
+            pkgPrefix: String,
+            classPrefix: String,
+            isService: Boolean = false,
+    ): List<ComponentName> {
+        val launcherActivities = mutableListOf<ComponentName>()
+        for (i in 1..10) {
+            launcherActivities.add(
+                createNewComponent(pkg = "$pkgPrefix$i", "$classPrefix$i", isService)
+            )
+        }
+        return launcherActivities
+    }
+
+    private fun createNewComponent(
+        pkg: String,
+        clazz: String,
+        isService: Boolean = false
+    ): ComponentName {
+        val component = ComponentName(pkg, clazz)
+        val pi = createMockPackageInfo(component, isService)
+        whenever(packageManagerMock.getPackageInfo(eq(pkg), any<PackageManager.PackageInfoFlags>()))
+            .thenReturn(pi)
+        return component
+    }
+
+    private fun createMockPackageInfo(
+        component: ComponentName,
+        isService: Boolean = false
+    ): PackageInfo {
+        val pi = mock<PackageInfo> {}
+        if (isService) {
+            pi.services = arrayOf(createMockServiceInfo(component))
+        } else {
+            pi.activities = arrayOf(createMockActivityInfo(component))
+        }
+        return pi
+    }
+
+    private fun createMockActivityInfo(component: ComponentName): ActivityInfo {
+        val icon = mock<Drawable> {}
+        val ai = mock<ActivityInfo> {
+            on { loadIcon(any()) } doReturn icon
+            on { loadLabel(any()) } doReturn "${component.packageName}-${component.className}"
+            on { componentName } doReturn component
+        }
+        ai.name = "${component.packageName}-${component.className}"
+        return ai
+    }
+
+    private fun createMockServiceInfo(component: ComponentName): ServiceInfo {
+        val icon = mock<Drawable> {}
+        val si = mock<ServiceInfo> {
+            on { loadIcon(any()) } doReturn icon
+            on { componentName } doReturn component
+        }
+        si.name = "${component.packageName}-${component.className}"
+        return si
     }
 }
diff --git a/docklib/tests/src/com/android/car/docklib/TestUtils.kt b/docklib/tests/src/com/android/car/docklib/TestUtils.kt
index c362590..0e940d0 100644
--- a/docklib/tests/src/com/android/car/docklib/TestUtils.kt
+++ b/docklib/tests/src/com/android/car/docklib/TestUtils.kt
@@ -1,22 +1,52 @@
 package com.android.car.docklib
 
 import android.content.ComponentName
+import android.graphics.Color
 import android.graphics.drawable.Drawable
+import androidx.annotation.ColorInt
 import com.android.car.docklib.data.DockAppItem
-import org.mockito.Mockito.mock
+import com.android.car.docklib.data.DockItemId
+import java.util.UUID
+import org.mockito.kotlin.mock
 
 object TestUtils {
-
-    private val icon = mock(Drawable::class.java)
-
     /** Create a hardcoded dock item with optional fields */
     fun createAppItem(
-        type: DockAppItem.Type = DockAppItem.Type.DYNAMIC,
-        app: String = "app",
-        name: String = "app",
-        icon: Drawable = this.icon,
-        isDrivingOptimized: Boolean = true
+            id: @DockItemId UUID = UUID.randomUUID(),
+            type: DockAppItem.Type = DockAppItem.Type.DYNAMIC,
+            app: String = "app",
+            pkg: String = app,
+            clazz: String = app,
+            name: String = "app",
+            component: ComponentName = ComponentName(pkg, clazz),
+            icon: Drawable = mock<Drawable>(),
+            @ColorInt iconColor: Int = Color.WHITE,
+            @ColorInt iconColorScrim: Int? = null,
+            isDrivingOptimized: Boolean = true,
+            isMediaApp: Boolean = false,
     ): DockAppItem {
-        return DockAppItem(type, ComponentName(app, app), name, icon, isDrivingOptimized)
+        if (iconColorScrim != null) {
+            return DockAppItem(
+                    id = id,
+                    type = type,
+                    component = component,
+                    name = name,
+                    icon = icon,
+                    iconColor = iconColor,
+                    iconColorScrim = iconColorScrim,
+                    isDistractionOptimized = isDrivingOptimized,
+                    isMediaApp = isMediaApp,
+            )
+        }
+        return DockAppItem(
+                id = id,
+                type = type,
+                component = component,
+                name = name,
+                icon = icon,
+                iconColor = iconColor,
+                isDistractionOptimized = isDrivingOptimized,
+                isMediaApp = isMediaApp,
+        )
     }
 }
diff --git a/docklib/tests/src/com/android/car/docklib/data/DockAppItemTest.kt b/docklib/tests/src/com/android/car/docklib/data/DockAppItemTest.kt
index 2da0f3e..56aa35f 100644
--- a/docklib/tests/src/com/android/car/docklib/data/DockAppItemTest.kt
+++ b/docklib/tests/src/com/android/car/docklib/data/DockAppItemTest.kt
@@ -16,10 +16,12 @@
 
 package com.android.car.docklib.data
 
+import android.graphics.Color
 import android.graphics.drawable.Drawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.car.docklib.TestUtils
 import com.google.common.truth.Truth.assertThat
+import java.util.UUID
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -29,32 +31,36 @@
 class DockAppItemTest {
     @Test
     fun compareAppItems_equal() {
-        val item1: DockAppItem = TestUtils.createAppItem()
-        val item2: DockAppItem = TestUtils.createAppItem()
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id)
 
         assertThat(item1).isEqualTo(item2)
     }
 
     @Test
     fun compareAppItems_notEqual_differentApps() {
-        val item1: DockAppItem = TestUtils.createAppItem(app = "1")
-        val item2: DockAppItem = TestUtils.createAppItem(app = "2")
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, app = "1")
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, app = "2")
 
         assertThat(item1).isNotEqualTo(item2)
     }
 
     @Test
     fun compareAppItems_notEqual_differentNames() {
-        val item1: DockAppItem = TestUtils.createAppItem(name = "1")
-        val item2: DockAppItem = TestUtils.createAppItem(name = "2")
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, name = "1")
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, name = "2")
 
         assertThat(item1).isNotEqualTo(item2)
     }
 
     @Test
     fun compareAppItems_notEqual_differentStates() {
-        val item1: DockAppItem = TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC)
-        val item2: DockAppItem = TestUtils.createAppItem(type = DockAppItem.Type.STATIC)
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, type = DockAppItem.Type.DYNAMIC)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, type = DockAppItem.Type.STATIC)
 
         assertThat(item1).isNotEqualTo(item2)
     }
@@ -65,17 +71,63 @@
         whenever(icon1.constantState).thenReturn(null)
         val icon2 = mock<Drawable>()
         whenever(icon2.constantState).thenReturn(mock<Drawable.ConstantState>())
-
-        val item1: DockAppItem = TestUtils.createAppItem(icon = icon1)
-        val item2: DockAppItem = TestUtils.createAppItem(icon = icon2)
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, icon = icon1)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, icon = icon2)
 
         assertThat(item1).isNotEqualTo(item2)
     }
 
     @Test
     fun compareAppItems_notEqual_differentDrivingOptimized() {
-        val item1: DockAppItem = TestUtils.createAppItem(isDrivingOptimized = true)
-        val item2: DockAppItem = TestUtils.createAppItem(isDrivingOptimized = false)
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, isDrivingOptimized = true)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, isDrivingOptimized = false)
+
+        assertThat(item1).isNotEqualTo(item2)
+    }
+
+    @Test
+    fun compareAppItems_notEqual_differentMediaType() {
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, isMediaApp = true)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, isMediaApp = false)
+
+        assertThat(item1).isNotEqualTo(item2)
+    }
+
+    @Test
+    fun compareAppItems_notEqual_differentIconColor() {
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(id = id, iconColor = Color.WHITE)
+        val item2: DockAppItem = TestUtils.createAppItem(id = id, iconColor = Color.BLACK)
+
+        assertThat(item1).isNotEqualTo(item2)
+    }
+
+    @Test
+    fun compareAppItems_notEqual_differentIconColorScrim() {
+        val id = UUID.randomUUID()
+        val item1: DockAppItem = TestUtils.createAppItem(
+                id = id,
+                iconColor = Color.WHITE,
+                iconColorScrim = Color.argb(
+                        100, // alpha
+                        255, // red
+                        0, // green
+                        0 // blue
+                )
+        )
+        val item2: DockAppItem = TestUtils.createAppItem(
+                id = id,
+                iconColor = Color.WHITE,
+                iconColorScrim = Color.argb(
+                        150, // alpha
+                        0, // red
+                        255, // green
+                        0 // blue
+                )
+        )
 
         assertThat(item1).isNotEqualTo(item2)
     }
diff --git a/docklib/tests/src/com/android/car/docklib/data/DockProtoDataControllerTest.kt b/docklib/tests/src/com/android/car/docklib/data/DockProtoDataControllerTest.kt
new file mode 100644
index 0000000..1f1e756
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/data/DockProtoDataControllerTest.kt
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 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.docklib.data
+
+import android.content.ComponentName
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.car.docklib.DockItemProto.DockAppItemListMessage
+import com.android.car.docklib.DockItemProto.DockAppItemMessage
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class DockProtoDataControllerTest {
+
+    private lateinit var dataController: DockProtoDataController
+    private val dataSource = mock<DockProtoDataSource>()
+
+    private val pinnedItemA = ComponentName("packageA", "classA")
+    private val pinnedItemC = ComponentName("packageC", "classC")
+
+    @Before
+    fun setup() {
+        dataController = DockProtoDataController(mock<File>())
+        val field = DockProtoDataController::class.java.getDeclaredField("dataSource")
+        field.isAccessible = true
+        field.set(dataController, dataSource)
+    }
+
+    @Test
+    fun testSaveToFile_OneItem_DataCorrect() {
+        val dockData = mapOf(1 to pinnedItemA)
+        val captor = argumentCaptor<DockAppItemListMessage>()
+
+        dataController.savePinnedItemsToFile(dockData)
+
+        verify(dataSource).writeToFile(captor.capture())
+        assertThat(captor.allValues.size).isEqualTo(1)
+        val dockProtoData = captor.allValues[0].dockAppItemMessageList
+        assertThat(dockProtoData.size).isEqualTo(dockData.size)
+        assertThat(dockProtoData[0].packageName).isEqualTo(pinnedItemA.packageName)
+        assertThat(dockProtoData[0].className).isEqualTo(pinnedItemA.className)
+        assertThat(dockProtoData[0].relativePosition).isEqualTo(1)
+    }
+
+    @Test
+    fun testSaveToFile_MultipleItems_DataCorrect() {
+        val dockData = mapOf(
+            0 to pinnedItemA,
+            // Intentionally skip index 1
+            2 to pinnedItemC,
+        )
+        val captor = argumentCaptor<DockAppItemListMessage>()
+
+        dataController.savePinnedItemsToFile(dockData)
+
+        verify(dataSource).writeToFile(captor.capture())
+        assertThat(captor.allValues.size).isEqualTo(1)
+        val dockProtoData = captor.allValues[0].dockAppItemMessageList
+        assertThat(dockProtoData.size).isEqualTo(dockData.size)
+        assertThat(dockProtoData[0].packageName).isEqualTo(pinnedItemA.packageName)
+        assertThat(dockProtoData[0].className).isEqualTo(pinnedItemA.className)
+        assertThat(dockProtoData[0].relativePosition).isEqualTo(0)
+        assertThat(dockProtoData[1].packageName).isEqualTo(pinnedItemC.packageName)
+        assertThat(dockProtoData[1].className).isEqualTo(pinnedItemC.className)
+        assertThat(dockProtoData[1].relativePosition).isEqualTo(2)
+    }
+
+    @Test
+    fun testLoadFromFile_OneItem_DataCorrect() {
+        val dockProtoData = DockAppItemListMessage.newBuilder()
+            .addDockAppItemMessage(convertDockItemToProto(pinnedItemA, 1))
+            .build()
+        whenever(dataSource.readFromFile()).thenReturn(dockProtoData)
+
+        val loadedDockData = dataController.loadFromFile()
+
+        assertThat(loadedDockData?.size).isEqualTo(1)
+        assertThat(loadedDockData?.get(1)).isEqualTo(pinnedItemA)
+    }
+
+    @Test
+    fun testLoadFromFile_MultipleItems_DataCorrect() {
+        val dockProtoData = DockAppItemListMessage.newBuilder()
+            .addDockAppItemMessage(convertDockItemToProto(pinnedItemA, 0))
+            .addDockAppItemMessage(convertDockItemToProto(pinnedItemC, 2))
+            .build()
+        whenever(dataSource.readFromFile()).thenReturn(dockProtoData)
+
+        val loadedDockData = dataController.loadFromFile()
+
+        assertThat(loadedDockData?.size).isEqualTo(2)
+        assertThat(loadedDockData?.get(0)).isEqualTo(pinnedItemA)
+        assertThat(loadedDockData?.get(2)).isEqualTo(pinnedItemC)
+    }
+
+    private fun convertDockItemToProto(componentName: ComponentName, position: Int) =
+        DockAppItemMessage.newBuilder()
+            .setPackageName(componentName.packageName)
+            .setClassName(componentName.className)
+            .setRelativePosition(position)
+            .build()
+}
diff --git a/docklib/tests/src/com/android/car/docklib/events/DockPackageChangeReceiverTest.kt b/docklib/tests/src/com/android/car/docklib/events/DockPackageChangeReceiverTest.kt
new file mode 100644
index 0000000..3c20c56
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/events/DockPackageChangeReceiverTest.kt
@@ -0,0 +1,152 @@
+package com.android.car.docklib.events
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER
+import android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.car.docklib.DockInterface
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class DockPackageChangeReceiverTest {
+    private companion object {
+        private const val TEST_UID = 10
+        private const val TEST_PKG_NAME = "TEST_PKG_NAME"
+    }
+
+    private val dockInterfaceMock = mock<DockInterface> {}
+    private val packageManagerMock = mock<PackageManager> {}
+    private val contextMock = mock<Context> {
+        on { packageManager } doReturn packageManagerMock
+    }
+    private val uriMock = mock<Uri> {}
+    private val intentMock = mock<Intent> {
+        on { data } doReturn uriMock
+    }
+    private val dockPackageChangeReceiver = DockPackageChangeReceiver(dockInterfaceMock)
+
+    @Test
+    fun onReceive_uidNotSent_noop() {
+        // since getIntExtra cannot return null value, return the default value passed in as arg
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt()))
+            .then { it.getArgument<Int>(1) }
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onReceive_pkgNotSend_noop() {
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(null)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onReceive_actionPackageRemoved_replacing_noop() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_REMOVED)
+        whenever(intentMock.getBooleanExtra(eq(Intent.EXTRA_REPLACING), anyBoolean()))
+            .thenReturn(true)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onReceive_actionPackageRemoved_notReplacing_packageRemovedCalled() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_REMOVED)
+        whenever(intentMock.getBooleanExtra(eq(Intent.EXTRA_REPLACING), anyBoolean()))
+            .thenReturn(false)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verify(dockInterfaceMock).packageRemoved(eq(TEST_PKG_NAME))
+    }
+
+    @Test
+    fun onReceive_actionPackageAdded_replacing_noop() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_ADDED)
+        whenever(intentMock.getBooleanExtra(eq(Intent.EXTRA_REPLACING), anyBoolean()))
+            .thenReturn(true)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onReceive_actionPackageAdded_notReplacing_packageAddedCalled() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_ADDED)
+        whenever(intentMock.getBooleanExtra(eq(Intent.EXTRA_REPLACING), anyBoolean()))
+            .thenReturn(false)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verify(dockInterfaceMock).packageAdded(eq(TEST_PKG_NAME))
+    }
+
+    @Test
+    fun onReceive_actionPackageChanged_isEnabled_noop() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_CHANGED)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+        whenever(packageManagerMock.getApplicationEnabledSetting(eq(TEST_PKG_NAME)))
+            .thenReturn(COMPONENT_ENABLED_STATE_ENABLED)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onReceive_actionPackageChanged_isDisabled_packageRemovedCalled() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_CHANGED)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+        whenever(packageManagerMock.getApplicationEnabledSetting(eq(TEST_PKG_NAME)))
+            .thenReturn(COMPONENT_ENABLED_STATE_DISABLED)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verify(dockInterfaceMock).packageRemoved(eq(TEST_PKG_NAME))
+    }
+
+    @Test
+    fun onReceive_actionPackageChanged_isDisabledForUser_packageRemovedCalled() {
+        whenever(intentMock.action).thenReturn(Intent.ACTION_PACKAGE_CHANGED)
+        whenever(intentMock.getIntExtra(eq(Intent.EXTRA_UID), anyInt())).thenReturn(TEST_UID)
+        whenever(uriMock.schemeSpecificPart).thenReturn(TEST_PKG_NAME)
+        whenever(packageManagerMock.getApplicationEnabledSetting(eq(TEST_PKG_NAME)))
+            .thenReturn(COMPONENT_ENABLED_STATE_DISABLED_USER)
+
+        dockPackageChangeReceiver.onReceive(contextMock, intentMock)
+
+        verify(dockInterfaceMock).packageRemoved(eq(TEST_PKG_NAME))
+    }
+}
diff --git a/docklib/tests/src/com/android/car/docklib/media/MediaUtilsTest.kt b/docklib/tests/src/com/android/car/docklib/media/MediaUtilsTest.kt
new file mode 100644
index 0000000..07cb25b
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/media/MediaUtilsTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 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.docklib.media
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.car.docklib.media.MediaUtils.Companion.CAR_MEDIA_DATA_SCHEME
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class MediaUtilsTest {
+    private val uriMock = mock<Uri> {}
+    private val intentMock = mock<Intent> {}
+    private val runningTaskInfoMock = mock<RunningTaskInfo> {}
+    private val packageManagerMock = mock<PackageManager> {}
+    private val intentCaptor = argumentCaptor<Intent>()
+
+    @Before
+    fun setup() {
+        runningTaskInfoMock.baseIntent = intentMock
+    }
+
+    @Test
+    fun getMediaComponentName_noData_returnsNull() {
+        whenever(intentMock.data).doReturn(null)
+
+        val ret = MediaUtils.getMediaComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isNull()
+    }
+
+    @Test
+    fun getMediaComponentName_incorrectDataScheme_returnsNull() {
+        whenever(intentMock.data).doReturn(uriMock)
+        whenever(uriMock.scheme).doReturn("MediaUtilsTestScheme")
+
+        val ret = MediaUtils.getMediaComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isNull()
+    }
+
+    @Test
+    fun getMediaComponentName_returnsCorrectComponent() {
+        val componentName = ComponentName("testPkg", "testClass")
+        whenever(intentMock.data).doReturn(uriMock)
+        whenever(uriMock.scheme).doReturn(CAR_MEDIA_DATA_SCHEME)
+        whenever(uriMock.schemeSpecificPart).doReturn("/" + componentName.flattenToString())
+
+        val ret = MediaUtils.getMediaComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isEqualTo(componentName)
+    }
+
+    @Test
+    fun fetchMediaServiceComponents_packageNotNull_queriesOnlyForThatPackage() {
+        val pkg = "testPkg"
+
+        MediaUtils.fetchMediaServiceComponents(packageManagerMock, pkg)
+
+        verify(packageManagerMock).queryIntentServices(intentCaptor.capture(), anyInt())
+        assertThat(intentCaptor.firstValue.getPackage()).isEqualTo(pkg)
+    }
+}
diff --git a/docklib/tests/src/com/android/car/docklib/task/DockTaskStackChangeListenerTest.kt b/docklib/tests/src/com/android/car/docklib/task/DockTaskStackChangeListenerTest.kt
new file mode 100644
index 0000000..0a480ad
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/task/DockTaskStackChangeListenerTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 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.docklib.task
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.ComponentName
+import android.content.Intent
+import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.car.docklib.DockInterface
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class DockTaskStackChangeListenerTest {
+    private val dockInterfaceMock = mock<DockInterface> {}
+    private val runningTaskInfoMock = mock<RunningTaskInfo> {}
+    private val intentMock = mock<Intent> {}
+
+    @Test
+    fun onTaskMovedToFront_taskNotForCurrentUser_appLaunchedNotCalled() {
+        val currentUserId = 10
+        val taskUserId = 11
+        val taskComponentName = ComponentName("testPkgName", "testClassName")
+        runningTaskInfoMock.userId = taskUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        runningTaskInfoMock.baseActivity = taskComponentName
+
+        val dockTaskStackChangeListener =
+                DockTaskStackChangeListener(currentUserId, dockInterfaceMock)
+        dockTaskStackChangeListener.onTaskMovedToFront(runningTaskInfoMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onTaskMovedToFront_taskNotForDefaultDisplay_appLaunchedNotCalled() {
+        val currentUserId = 10
+        val nonDefaultDisplay = Display.DEFAULT_DISPLAY + 2
+        val taskComponentName = ComponentName("testPkgName", "testClassName")
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = nonDefaultDisplay
+        runningTaskInfoMock.baseActivity = taskComponentName
+
+        val dockTaskStackChangeListener =
+                DockTaskStackChangeListener(currentUserId, dockInterfaceMock)
+        dockTaskStackChangeListener.onTaskMovedToFront(runningTaskInfoMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onTaskMovedToFront_taskMissingBaseActivityAndBaseIntentComponent_appLaunchedNotCalled() {
+        val currentUserId = 10
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        runningTaskInfoMock.baseActivity = null
+        whenever(intentMock.component).thenReturn(null)
+        runningTaskInfoMock.baseIntent = intentMock
+
+        val dockTaskStackChangeListener =
+                DockTaskStackChangeListener(currentUserId, dockInterfaceMock)
+        dockTaskStackChangeListener.onTaskMovedToFront(runningTaskInfoMock)
+
+        verifyNoMoreInteractions(dockInterfaceMock)
+    }
+
+    @Test
+    fun onTaskMovedToFront_appLaunchedCalled() {
+        val currentUserId = 10
+        val taskComponentName = ComponentName("testPkgName", "testClassName")
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        runningTaskInfoMock.baseActivity = taskComponentName
+
+        val dockTaskStackChangeListener =
+                DockTaskStackChangeListener(currentUserId, dockInterfaceMock)
+        dockTaskStackChangeListener.onTaskMovedToFront(runningTaskInfoMock)
+
+        verify(dockInterfaceMock).appLaunched(eq(taskComponentName))
+    }
+}
diff --git a/docklib/tests/src/com/android/car/docklib/task/TaskUtilsTest.kt b/docklib/tests/src/com/android/car/docklib/task/TaskUtilsTest.kt
new file mode 100644
index 0000000..f90e0a3
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/task/TaskUtilsTest.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 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.docklib.task
+
+import android.app.ActivityManager
+import android.content.ComponentName
+import android.content.Intent
+import android.net.Uri
+import android.view.Display
+import com.android.car.docklib.media.MediaUtils
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+class TaskUtilsTest {
+    private val runningTaskInfoMock = mock<ActivityManager.RunningTaskInfo> {}
+    private val intentMock = mock<Intent> {}
+    private val uriMock = mock<Uri> {}
+
+    @Test
+    fun getComponentName_taskMissingBaseActivityAndBaseIntentComponent_returnsNull() {
+        val currentUserId = 10
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        runningTaskInfoMock.baseActivity = null
+        whenever(intentMock.component).thenReturn(null)
+        runningTaskInfoMock.baseIntent = intentMock
+
+        val ret = TaskUtils.getComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isNull()
+    }
+
+    @Test
+    fun getComponentName_returnsComponent() {
+        val currentUserId = 10
+        val taskComponentName = ComponentName("testPkgName", "testClassName")
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        runningTaskInfoMock.baseActivity = taskComponentName
+
+        val ret = TaskUtils.getComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isEqualTo(taskComponentName)
+    }
+
+    @Test
+    fun getComponentName_mediaComponent_returnsMediaComponent() {
+        val mediaComponentName = ComponentName("testPkg", "testClass")
+        val currentUserId = 10
+        runningTaskInfoMock.userId = currentUserId
+        runningTaskInfoMock.displayId = Display.DEFAULT_DISPLAY
+        whenever(intentMock.component).doReturn(MediaUtils.CAR_MEDIA_ACTIVITY)
+        whenever(intentMock.data).doReturn(uriMock)
+        whenever(uriMock.scheme).doReturn(MediaUtils.CAR_MEDIA_DATA_SCHEME)
+        whenever(uriMock.schemeSpecificPart).doReturn("/" + mediaComponentName.flattenToString())
+        runningTaskInfoMock.baseIntent = intentMock
+
+        val ret = TaskUtils.getComponentName(runningTaskInfoMock)
+
+        assertThat(ret).isEqualTo(mediaComponentName)
+    }
+}
diff --git a/docklib/tests/src/com/android/car/docklib/view/DockAdapterTest.kt b/docklib/tests/src/com/android/car/docklib/view/DockAdapterTest.kt
index d39ed0f..19541ba 100644
--- a/docklib/tests/src/com/android/car/docklib/view/DockAdapterTest.kt
+++ b/docklib/tests/src/com/android/car/docklib/view/DockAdapterTest.kt
@@ -1,122 +1,138 @@
 package com.android.car.docklib.view
 
+import android.content.ComponentName
 import android.content.Context
-import android.content.Intent
 import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.car.docklib.TestUtils
+import com.android.car.docklib.DockInterface
 import com.android.car.docklib.data.DockAppItem
-import com.google.common.truth.Truth.assertThat
-import java.util.function.Consumer
+import org.junit.After
+import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.spy
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
+import org.mockito.kotlin.verifyNoMoreInteractions
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class DockAdapterTest {
-    private val contextMock = mock<Context> {}
-    private val intentConsumerMock = mock<Consumer<Intent>> {}
+
+    private companion object {
+        private const val PACKAGE = "package"
+    }
+
+    private val dockInterfaceMock = mock<DockInterface> {}
     private val dockItemViewHolderMock = mock<DockItemViewHolder> {}
+    private val componentName = mock<ComponentName> {
+        on { packageName } doReturn PACKAGE
+    }
+    private val runnableMock = mock<Runnable> {}
+    private val contextMock = mock<Context> {}
+    private val dockAdapter: DockAdapter = spy(DockAdapter(dockInterfaceMock, contextMock))
+    private val dockItemList: MutableList<DockAppItem> = mutableListOf()
 
-    @Test
-    fun setItems_dockSizeEqualToListSize_adapterHasDockSize() {
-        val defaultApps =
-            listOf(TestUtils.createAppItem(app = "a"), TestUtils.createAppItem(app = "b"))
-        val adapter = spy(DockAdapter(defaultApps.size, intentConsumerMock, contextMock))
-
-        adapter.setItems(defaultApps)
-
-        assertThat(adapter.itemCount).isEqualTo(defaultApps.size)
-        verify(adapter).notifyItemChanged(0)
-        verify(adapter).notifyItemChanged(1)
+    @Before
+    fun setup() {
+        for (i in 0..5) {
+            dockItemList.add(mock<DockAppItem> {})
+        }
+        dockAdapter.submitList(dockItemList)
+        doReturn(componentName).whenever(dockItemList[1]).component
     }
 
-    @Test
-    fun setItems_dockSizeLessThanListSize_adapterHasDockSize() {
-        val defaultApps =
-            listOf(
-                TestUtils.createAppItem(app = "a"),
-                TestUtils.createAppItem(app = "b"),
-                TestUtils.createAppItem(app = "c")
-            )
-        val dockSize = 2
-        val adapter = spy(DockAdapter(dockSize, intentConsumerMock, contextMock))
-
-        adapter.setItems(defaultApps)
-
-        assertThat(adapter.itemCount).isEqualTo(dockSize)
-        verify(adapter).notifyItemChanged(0)
-        verify(adapter).notifyItemChanged(1)
-    }
-
-    @Test
-    fun setItems_dockSizeGreaterThanListSize_adapterHasDockSize() {
-        val defaultApps =
-            listOf(TestUtils.createAppItem(app = "a"), TestUtils.createAppItem(app = "b"))
-        val dockSize = 3
-        val adapter = spy(DockAdapter(dockSize, intentConsumerMock, contextMock))
-
-        adapter.setItems(defaultApps)
-
-        assertThat(adapter.itemCount).isEqualTo(dockSize)
-        verify(adapter).notifyItemChanged(0)
-        verify(adapter).notifyItemChanged(1)
-        verify(adapter, times(0)).notifyItemChanged(2)
+    @After
+    fun tearDown() {
+        dockItemList.clear()
     }
 
     @Test
     fun onBindViewHolder_emptyPayload_onBindViewHolderWithoutPayloadCalled() {
-        val adapter = spy(DockAdapter(3, intentConsumerMock, contextMock))
+        dockAdapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(0) {})
 
-        adapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(0) {})
-
-        verify(adapter).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verify(dockAdapter).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
     }
 
     @Test
-    fun onBindViewHolder_nullPayload_onBindViewHolderWithoutPayloadCalled() {
-        val adapter = spy(DockAdapter(3, intentConsumerMock, contextMock))
+    fun onBindViewHolder_nullPayload_noop() {
+        dockAdapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(1) {})
 
-        adapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(1) {})
-
-        verify(adapter).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verifyNoMoreInteractions(dockItemViewHolderMock)
     }
 
     @Test
-    fun onBindViewHolder_payloadOfIncorrectType_onBindViewHolderWithoutPayloadCalled() {
+    fun onBindViewHolder_payloadOfIncorrectType_noop() {
         class DummyPayload
-        val adapter = spy(DockAdapter(3, intentConsumerMock, contextMock))
 
-        adapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(1) {
-            DummyPayload()
-        })
+        dockAdapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(1) { DummyPayload() })
 
-        verify(adapter).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verifyNoMoreInteractions(dockItemViewHolderMock)
     }
 
     @Test
-    fun onBindViewHolder_payload_CHANGE_SAME_ITEM_TYPE_itemTypeChangedCalled() {
-        val dockAppItem0 = mock<DockAppItem> {}
-        val dockAppItem1 = mock<DockAppItem> {}
-        val dockAppItem2 = mock<DockAppItem> {}
-        val adapter = spy(
-            DockAdapter(
-                numItems = 3,
-                intentConsumerMock,
-                contextMock,
-                items = arrayOf(dockAppItem0, dockAppItem1, dockAppItem2)
-            )
+    fun onBindViewHolder_payloadChangeItemType_itemTypeChangedCalled() {
+        dockAdapter.onBindViewHolder(
+                dockItemViewHolderMock,
+                1,
+                MutableList(1) { DockAdapter.PayloadType.CHANGE_ITEM_TYPE }
         )
 
-        adapter.onBindViewHolder(dockItemViewHolderMock, 1, MutableList(1) {
-            DockAdapter.PayloadType.CHANGE_SAME_ITEM_TYPE
-        })
+        verify(dockAdapter, never()).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verify(dockItemViewHolderMock).itemTypeChanged(eq(dockItemList[1]))
+    }
 
-        verify(adapter, never()).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
-        verify(dockItemViewHolderMock).itemTypeChanged(eq(dockAppItem1))
+    @Test
+    fun onBindViewHolder_payloadMediaItemType_hasMediaSession_hasActiveMediaSessionCalled() {
+        dockAdapter.onMediaSessionChange(listOf(PACKAGE))
+        dockAdapter.onBindViewHolder(
+            dockItemViewHolderMock,
+            1,
+            MutableList(1) { DockAdapter.PayloadType.CHANGE_ACTIVE_MEDIA_SESSION }
+        )
+
+        verify(dockAdapter, never()).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verify(dockItemViewHolderMock).setHasActiveMediaSession(true)
+    }
+
+    @Test
+    fun onBindViewHolder_payloadMediaItemType_noMediaSession_hasActiveMediaSessionCalled() {
+        dockAdapter.onBindViewHolder(
+            dockItemViewHolderMock,
+            1,
+            MutableList(1) { DockAdapter.PayloadType.CHANGE_ACTIVE_MEDIA_SESSION }
+        )
+
+        verify(dockAdapter, never()).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verify(dockItemViewHolderMock).setHasActiveMediaSession(false)
+    }
+
+    @Test
+    fun onBindViewHolder_multiplePayloadChangeItemType_itemTypeChangedCalledMultipleTimes() {
+        dockAdapter.onBindViewHolder(
+                dockItemViewHolderMock,
+                1,
+                MutableList(3) { DockAdapter.PayloadType.CHANGE_ITEM_TYPE }
+        )
+
+        verify(dockAdapter, never()).onBindViewHolder(eq(dockItemViewHolderMock), eq(1))
+        verify(dockItemViewHolderMock, times(3)).itemTypeChanged(eq(dockItemList[1]))
+    }
+
+    @Test
+    fun onBindViewHolder_setCallback_bindWithRunnable() {
+        dockAdapter.setCallback(1, runnableMock)
+        dockAdapter.onBindViewHolder(dockItemViewHolderMock, 1)
+
+        verify(dockItemViewHolderMock).bind(
+                eq(dockItemList[1]),
+                eq(false),
+                eq(runnableMock),
+                any()
+        )
     }
 }
diff --git a/docklib/tests/src/com/android/car/docklib/view/DockDragListenerTest.kt b/docklib/tests/src/com/android/car/docklib/view/DockDragListenerTest.kt
index 371f87e..cb3b05a 100644
--- a/docklib/tests/src/com/android/car/docklib/view/DockDragListenerTest.kt
+++ b/docklib/tests/src/com/android/car/docklib/view/DockDragListenerTest.kt
@@ -3,7 +3,6 @@
 import android.content.ClipData
 import android.content.ClipDescription
 import android.content.ComponentName
-import android.content.Context
 import android.content.res.Resources
 import android.graphics.Point
 import android.view.DragEvent
@@ -14,36 +13,29 @@
 import android.view.SurfaceControl
 import android.view.View
 import androidx.core.animation.ValueAnimator
-import androidx.recyclerview.widget.RecyclerView
-import androidx.recyclerview.widget.RecyclerView.ViewHolder
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.car.docklib.R
 import com.android.car.docklib.view.DockDragListener.Companion.APP_ITEM_DRAG_TAG
 import com.google.common.truth.Truth.assertThat
+import java.util.function.Consumer
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.isNull
 import org.mockito.kotlin.any
 import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
-import org.mockito.kotlin.spy
 import org.mockito.kotlin.verify
 import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 class DockDragListenerTest {
     private val resourcesMock = mock<Resources> {
-        on { getInteger(eq(R.integer.drag_drop_animate_in_duration)) } doReturn 0
+        on { getInteger(eq(R.integer.drop_animation_scale_down_duration_ms)) } doReturn 0
+        on { getInteger(eq(R.integer.drop_animation_scale_up_duration_ms)) } doReturn 0
     }
-    private val contextMock = mock<Context> {
-        on { resources } doReturn resourcesMock
-    }
-    private val itemViewMock = mock<View> {
-        on { context } doReturn contextMock
-    }
-    private val viewHolderSpy = spy(object : ViewHolder(itemViewMock) {})
     private val viewMock = mock<View> {}
     private val dragEventMock = mock<DragEvent> {}
     private val clipDescriptionMock = mock<ClipDescription> {}
@@ -51,18 +43,32 @@
     private val clipDataItemMock = mock<ClipData.Item> {}
     private val surfaceControlMock = mock<SurfaceControl> {}
     private val surfaceControlTransactionMock = mock<SurfaceControl.Transaction> {}
-    private val surfaceControlTransactionMock2 = mock<SurfaceControl.Transaction> {}
     private val callbackMock = mock<DockDragListener.Callback> {}
     private val valueAnimatorMock = mock<ValueAnimator> {}
+    private val booleanConsumerMock = mock<Consumer<Boolean>> {}
     private val componentNameCaptor = argumentCaptor<ComponentName>()
-    private var dockDragListener = DockDragListener(viewHolderSpy, callbackMock)
+    private var dockDragListener = object : DockDragListener(resourcesMock, callbackMock) {
+        override fun getAnimator(
+            surfaceControl: SurfaceControl,
+            fromX: Float,
+            fromY: Float,
+            toX: Float,
+            toY: Float,
+            fromScaleX: Float,
+            fromScaleY: Float,
+            toScaleX: Float,
+            toScaleY: Float,
+            animationDuration: Long
+        ): ValueAnimator {
+            return valueAnimatorMock
+        }
+    }
 
     companion object {
         private val VALID_COMPONENT_NAME = ComponentName(
             "com.android.car.docklib.view",
             DockDragListenerTest::javaClass.name
         ).flattenToString()
-        private const val VALID_ADAPTER_POSITION = 0
     }
 
     @Test
@@ -108,29 +114,8 @@
     }
 
     @Test
-    fun onDrag_ACTION_DROP_invalidPosition_returnFalse() {
-        whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn RecyclerView.NO_POSITION
-
-        val ret = dockDragListener.onDrag(viewMock, dragEventMock)
-
-        assertThat(ret).isFalse()
-    }
-
-    @Test
-    fun onDrag_ACTION_DROP_invalidPosition_resetViewCallbackTriggered() {
-        whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn RecyclerView.NO_POSITION
-
-        dockDragListener.onDrag(viewMock, dragEventMock)
-
-        verify(callbackMock).resetView()
-    }
-
-    @Test
     fun onDrag_ACTION_DROP_noClipData_returnFalse() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(dragEventMock.clipData) doReturn clipDataMock
         whenever(clipDataMock.getItemAt(any())) doReturn null
 
@@ -142,7 +127,6 @@
     @Test
     fun onDrag_ACTION_DROP_noClipData_resetViewCallbackTriggered() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(dragEventMock.clipData) doReturn clipDataMock
         whenever(clipDataMock.getItemAt(any())) doReturn null
 
@@ -154,7 +138,6 @@
     @Test
     fun onDrag_ACTION_DROP_clipDataWithNoText_returnFalse() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn null
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -167,7 +150,6 @@
     @Test
     fun onDrag_ACTION_DROP_clipDataWithNoText_resetViewCallbackTriggered() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn null
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -181,7 +163,6 @@
     fun onDrag_ACTION_DROP_invalidComponentName_returnFalse() {
         val invalidComponentName = "invalidComponentName"
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn invalidComponentName
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -195,7 +176,6 @@
     fun onDrag_ACTION_DROP_invalidComponentName_resetViewCallbackTriggered() {
         val invalidComponentName = "invalidComponentName"
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn invalidComponentName
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -208,7 +188,6 @@
     @Test
     fun onDrag_ACTION_DROP_noDragSurface_returnFalse() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn VALID_COMPONENT_NAME
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -222,7 +201,6 @@
     @Test
     fun onDrag_ACTION_DROP_noDragSurface_dragAcceptedCallbackTriggered() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn VALID_COMPONENT_NAME
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -230,7 +208,7 @@
 
         dockDragListener.onDrag(viewMock, dragEventMock)
 
-        verify(callbackMock).dragAccepted(componentNameCaptor.capture())
+        verify(callbackMock).dropSuccessful(componentNameCaptor.capture(), isNull())
         assertThat(componentNameCaptor.firstValue).isNotNull()
         assertThat(componentNameCaptor.firstValue.flattenToString()).isEqualTo(VALID_COMPONENT_NAME)
     }
@@ -238,7 +216,6 @@
     @Test
     fun onDrag_ACTION_DROP_validPositionComponentNameDragSurface_returnTrue() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn VALID_COMPONENT_NAME
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -247,19 +224,6 @@
         whenever(callbackMock.getDropLocation()).thenReturn(Point(0, 0))
         whenever(callbackMock.getDropHeight()).thenReturn(10f)
         whenever(callbackMock.getDropWidth()).thenReturn(10f)
-        dockDragListener = object : DockDragListener(viewHolderSpy, callbackMock) {
-            override fun getAnimator(
-                surfaceControl: SurfaceControl,
-                fromX: Float,
-                fromY: Float,
-                toX: Float,
-                toY: Float,
-                toScaleX: Float,
-                toScaleY: Float
-            ): ValueAnimator {
-                return valueAnimatorMock
-            }
-        }
 
         val ret = dockDragListener.onDrag(viewMock, dragEventMock)
 
@@ -269,7 +233,6 @@
     @Test
     fun onDrag_ACTION_DROP_validPositionComponentNameDragSurface_animationStarted() {
         whenever(dragEventMock.action) doReturn ACTION_DROP
-        whenever(viewHolderSpy.bindingAdapterPosition) doReturn VALID_ADAPTER_POSITION
         whenever(clipDataItemMock.text) doReturn VALID_COMPONENT_NAME
         whenever(clipDataMock.getItemAt(eq(0))) doReturn clipDataItemMock
         whenever(dragEventMock.clipData) doReturn clipDataMock
@@ -278,19 +241,6 @@
         whenever(callbackMock.getDropLocation()).thenReturn(Point(0, 0))
         whenever(callbackMock.getDropHeight()).thenReturn(10f)
         whenever(callbackMock.getDropWidth()).thenReturn(10f)
-        dockDragListener = object : DockDragListener(viewHolderSpy, callbackMock) {
-            override fun getAnimator(
-                surfaceControl: SurfaceControl,
-                fromX: Float,
-                fromY: Float,
-                toX: Float,
-                toY: Float,
-                toScaleX: Float,
-                toScaleY: Float
-            ): ValueAnimator {
-                return valueAnimatorMock
-            }
-        }
 
         dockDragListener.onDrag(viewMock, dragEventMock)
 
@@ -370,108 +320,39 @@
     }
 
     @Test
-    fun getAnimatorListener_onAnimationEnd_transactionsClosed() {
+    fun getAnimatorListener_onAnimationEnd_callbackTriggered() {
         whenever(surfaceControlMock.isValid) doReturn true
 
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
+        val listener = dockDragListener.getAnimatorListener(booleanConsumerMock)
         listener.onAnimationEnd(valueAnimatorMock)
 
-        verify(surfaceControlTransactionMock).close()
-        verify(surfaceControlTransactionMock2).close()
+        verify(booleanConsumerMock).accept(
+            eq(false) // isCancelled
+        )
     }
 
     @Test
-    fun getAnimatorListener_onAnimationCancel_transactionsClosed() {
+    fun getAnimatorListener_onAnimationCancel_callbackTriggered() {
         whenever(surfaceControlMock.isValid) doReturn true
 
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
+        val listener = dockDragListener.getAnimatorListener(booleanConsumerMock)
         listener.onAnimationCancel(valueAnimatorMock)
 
-        verify(surfaceControlTransactionMock).close()
-        verify(surfaceControlTransactionMock2).close()
+        verify(booleanConsumerMock).accept(
+            eq(true) // isCancelled
+        )
     }
 
     @Test
-    fun getAnimatorListener_onAnimationCancelAndonAnimationEnd_transactionsClosedOnce() {
+    fun getAnimatorListener_onAnimationCancelAndonAnimationEnd_callbackTriggeredOnce() {
         whenever(surfaceControlMock.isValid) doReturn true
 
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
+        val listener = dockDragListener.getAnimatorListener(booleanConsumerMock)
         listener.onAnimationCancel(valueAnimatorMock)
         listener.onAnimationEnd(valueAnimatorMock)
 
-        verify(surfaceControlTransactionMock).close()
-        verify(surfaceControlTransactionMock2).close()
-    }
-
-    @Test
-    fun getAnimatorListener_onAnimationEnd_surfaceControlHidden() {
-        whenever(surfaceControlMock.isValid) doReturn true
-
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
+        verify(booleanConsumerMock).accept(
+            eq(true) // isCancelled
         )
-        listener.onAnimationEnd(valueAnimatorMock)
-
-        verify(surfaceControlTransactionMock2).hide(eq(surfaceControlMock))
-        verify(surfaceControlTransactionMock2).apply()
-    }
-
-    @Test
-    fun getAnimatorListener_onAnimationCancel_surfaceControlHidden() {
-        whenever(surfaceControlMock.isValid) doReturn true
-
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
-        listener.onAnimationCancel(valueAnimatorMock)
-
-        verify(surfaceControlTransactionMock2).hide(eq(surfaceControlMock))
-        verify(surfaceControlTransactionMock2).apply()
-    }
-
-    @Test
-    fun getAnimatorListener_onAnimationEnd_surfaceControlRemoved() {
-        whenever(surfaceControlMock.isValid) doReturn true
-
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
-        listener.onAnimationEnd(valueAnimatorMock)
-
-        verify(surfaceControlTransactionMock2).remove(eq(surfaceControlMock))
-        verify(surfaceControlTransactionMock2).apply()
-    }
-
-    @Test
-    fun getAnimatorListener_onAnimationCancel_surfaceControlRemoved() {
-        whenever(surfaceControlMock.isValid) doReturn true
-
-        val listener = dockDragListener.getAnimatorListener(
-            surfaceControlMock,
-            surfaceControlTransactionMock,
-            surfaceControlTransactionMock2
-        )
-        listener.onAnimationCancel(valueAnimatorMock)
-
-        verify(surfaceControlTransactionMock2).remove(eq(surfaceControlMock))
-        verify(surfaceControlTransactionMock2).apply()
     }
 }
diff --git a/docklib/tests/src/com/android/car/docklib/view/DockItemLongClickListenerTest.kt b/docklib/tests/src/com/android/car/docklib/view/DockItemLongClickListenerTest.kt
index d8ba54b..b2fbee5 100644
--- a/docklib/tests/src/com/android/car/docklib/view/DockItemLongClickListenerTest.kt
+++ b/docklib/tests/src/com/android/car/docklib/view/DockItemLongClickListenerTest.kt
@@ -1,7 +1,10 @@
 package com.android.car.docklib.view
 
+import android.car.media.CarMediaManager
+import android.content.ComponentName
 import android.content.Context
 import android.content.res.Resources
+import android.os.UserHandle
 import android.view.View
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.car.docklib.TestUtils
@@ -19,17 +22,22 @@
 
 @RunWith(AndroidJUnit4::class)
 class DockItemLongClickListenerTest {
-    private val dockAppItemMock = mock<DockAppItem>()
     private val resourcesMock = mock<Resources>()
-    private val contextMock = mock<Context> { on { resources } doReturn resourcesMock }
+    private val userHandleMock = mock<UserHandle>()
+    private val contextMock = mock<Context> {
+        on { resources } doReturn resourcesMock
+        on { user } doReturn userHandleMock
+    }
     private val viewMock = mock<View> { on { context } doReturn contextMock }
     private val runnableMock1 = mock<Runnable>()
     private val runnableMock2 = mock<Runnable>()
     private val carUiShortcutsPopupMock = mock<CarUiShortcutsPopup>()
-    private val carUiShortcutsPopupBuilderMock = mock<CarUiShortcutsPopup.Builder>() {
+    private val carUiShortcutsPopupBuilderMock = mock<CarUiShortcutsPopup.Builder> {
         on { addShortcut(any<CarUiShortcutsPopup.ShortcutItem>()) } doReturn it
         on { build(any<Context>(), any<View>()) } doReturn carUiShortcutsPopupMock
     }
+    private val carMediaManagerMock = mock<CarMediaManager>()
+
     private lateinit var dockItemLongClickListener: DockItemLongClickListener
 
     @Before
@@ -46,41 +54,48 @@
 
     @Test
     fun onLongClick_typeStatic_pinShortcutItem_parameterIsItemPinnedIsTrue() {
-        dockItemLongClickListener =
-            createDockItemLongClickListener(TestUtils.createAppItem(DockAppItem.Type.STATIC))
+        dockItemLongClickListener = createDockItemLongClickListener(
+                TestUtils.createAppItem(type = DockAppItem.Type.STATIC)
+        )
 
         dockItemLongClickListener.onLongClick(viewMock)
 
         verify(dockItemLongClickListener).createPinShortcutItem(
-            any<Resources>(),
-            eq(true),
-            any<Runnable>(),
-            any<Runnable>()
+                any<Resources>(),
+                eq(true),
+                any<Runnable>(),
+                any<Runnable>()
         )
     }
 
     @Test
     fun onLongClick_typeDynamic_pinShortcutItem_parameterIsItemPinnedIsFalse() {
-        dockItemLongClickListener =
-            createDockItemLongClickListener(TestUtils.createAppItem(DockAppItem.Type.DYNAMIC))
+        dockItemLongClickListener = createDockItemLongClickListener(
+                TestUtils.createAppItem(type = DockAppItem.Type.DYNAMIC)
+        )
 
         dockItemLongClickListener.onLongClick(viewMock)
 
         verify(dockItemLongClickListener).createPinShortcutItem(
-            any<Resources>(),
-            eq(false),
-            any<Runnable>(),
-            any<Runnable>()
+                any<Resources>(),
+                eq(false),
+                any<Runnable>(),
+                any<Runnable>()
         )
     }
 
     private fun createDockItemLongClickListener(
-        dockAppItem: DockAppItem = dockAppItemMock
+        dockAppItem: DockAppItem = TestUtils.createAppItem()
     ): DockItemLongClickListener {
         return spy(object : DockItemLongClickListener(
             dockAppItem,
             runnableMock1,
-            runnableMock2
+            runnableMock2,
+            ComponentName("testPkg", "testClass"),
+            contextMock,
+            carMediaManagerMock,
+            setOf()
+
         ) {
             override fun createCarUiShortcutsPopupBuilder(): CarUiShortcutsPopup.Builder =
                 carUiShortcutsPopupBuilderMock
diff --git a/docklib/tests/src/com/android/car/docklib/view/animation/ExcitementAnimationHelperTest.kt b/docklib/tests/src/com/android/car/docklib/view/animation/ExcitementAnimationHelperTest.kt
new file mode 100644
index 0000000..ac12e3c
--- /dev/null
+++ b/docklib/tests/src/com/android/car/docklib/view/animation/ExcitementAnimationHelperTest.kt
@@ -0,0 +1,59 @@
+package com.android.car.docklib.view.animation
+
+import androidx.core.animation.Animator
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+
+@RunWith(AndroidJUnit4::class)
+class ExcitementAnimationHelperTest {
+    private val animatorMock = mock<Animator>()
+    private val successCallbackMock = mock<Runnable>()
+    private val failureCallbackMock = mock<Runnable>()
+
+    @Test
+    fun getAnimatorListener_onAnimationEnd_onlySuccessCallbackCalled() {
+        val animatorListener =
+            ExcitementAnimationHelper.getAnimatorListener(
+                successCallbackMock,
+                failureCallbackMock
+            )
+
+        animatorListener.onAnimationEnd(animatorMock)
+
+        verify(successCallbackMock).run()
+        verify(failureCallbackMock, never()).run()
+    }
+
+    @Test
+    fun getAnimatorListener_onAnimationCancel_onlyFailureCallbackCalled() {
+        val animatorListener =
+            ExcitementAnimationHelper.getAnimatorListener(
+                successCallbackMock,
+                failureCallbackMock
+            )
+
+        animatorListener.onAnimationCancel(animatorMock)
+
+        verify(failureCallbackMock).run()
+        verify(successCallbackMock, never()).run()
+    }
+
+    @Test
+    fun getAnimatorListener_onAnimationCancelAndonAnimationEnd_onlyFailureCallbackCalled() {
+        val animatorListener =
+            ExcitementAnimationHelper.getAnimatorListener(
+                successCallbackMock,
+                failureCallbackMock
+            )
+
+        animatorListener.onAnimationCancel(animatorMock)
+        animatorListener.onAnimationEnd(animatorMock)
+
+        verify(failureCallbackMock).run()
+        verify(successCallbackMock, never()).run()
+    }
+}
diff --git a/gradle.properties b/gradle.properties
index b5987b6..235e0e2 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,4 +21,4 @@
 # Enables namespacing of each library's R class so that its R class includes only the
 # resources declared in the library itself and none from the library's dependencies,
 # thereby reducing the size of the R class for that library
-android.nonTransitiveRClass=true
+android.nonTransitiveRClass=false
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 58944a6..a81b7b0 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Sep 28 21:17:18 PDT 2023
+#Wed Jan 31 17:34:35 PST 2024
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java b/libs/aconfig-platform-compat/build.gradle.kts
similarity index 68%
copy from app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
copy to libs/aconfig-platform-compat/build.gradle.kts
index e30ffe5..f150500 100644
--- a/app/src/com/android/car/carlauncher/LaunchRootCarTaskViewCallbacks.java
+++ b/libs/aconfig-platform-compat/build.gradle.kts
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,10 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher;
+plugins {
+    java
+}
 
-/**
- * A callbacks interface for {@link LaunchRootCarTaskView}.
- */
-public interface LaunchRootCarTaskViewCallbacks extends
-        CarTaskViewCallbacks {}
+sourceSets {
+    main {
+        java.setSrcDirs(listOf("java"))
+    }
+}
diff --git a/libs/aconfig-platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java b/libs/aconfig-platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java
new file mode 120000
index 0000000..5dd4e31
--- /dev/null
+++ b/libs/aconfig-platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java
@@ -0,0 +1 @@
+../../../../../../../../../../tools/platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java
\ No newline at end of file
diff --git a/libs/aconfig-platform-compat/java/com/android/aconfig/annotations b/libs/aconfig-platform-compat/java/com/android/aconfig/annotations
new file mode 120000
index 0000000..591d7e9
--- /dev/null
+++ b/libs/aconfig-platform-compat/java/com/android/aconfig/annotations
@@ -0,0 +1 @@
+../../../../../../../../../../frameworks/libs/modules-utils/java/com/android/aconfig/annotations/
\ No newline at end of file
diff --git a/libs/appgrid/Android.bp b/libs/appgrid/Android.bp
index 06dd2c9..22b05f9 100644
--- a/libs/appgrid/Android.bp
+++ b/libs/appgrid/Android.bp
@@ -25,33 +25,46 @@
     },
     sdk_version: "module_current",
     min_sdk_version: "31",
-    srcs: ["lib/src/com/android/car/carlauncher/proto/launcher_item.proto"]
+    srcs: ["lib/src/com/android/car/carlauncher/proto/launcher_item.proto"],
 }
 
 android_library {
     name: "CarAppGrid-lib",
     platform_apis: true,
 
-    srcs: ["lib/src/**/*.java",
-    "lib/src/**/*.kt",
-    "lib/hidden_apis_enabled/**/*.java"],
+    srcs: [
+        "lib/src/**/*.java",
+        "lib/src/**/*.kt",
+        ":hidden_api_enabled_srcs",
+    ],
 
     resource_dirs: ["lib/res"],
 
     static_libs: [
+        "CarLauncherCommon",
         "androidx-constraintlayout_constraintlayout-solver",
         "androidx-constraintlayout_constraintlayout",
         "androidx.lifecycle_lifecycle-extensions",
         "car-media-common",
         "guava",
         "car-ui-lib",
+        "car_launcher_flags_java_lib",
         "launcher_item",
         "CarDockUtilLib",
+        "androidx.lifecycle_lifecycle-runtime-ktx",
+        "androidx.lifecycle_lifecycle-viewmodel-ktx",
+        "androidx.activity_activity-ktx",
+        "androidx.fragment_fragment-ktx",
+        "androidx.core_core-ktx",
+        "kotlinx-coroutines-core",
+        "kotlinx-coroutines-android",
     ],
 
     libs: ["android.car"],
 
     manifest: "lib/AndroidManifest.xml",
+    // TODO(b/319708040): re-enable use_resource_processor
+    use_resource_processor: false,
 }
 
 // Uses test key instead of the regular "platform" key
@@ -64,8 +77,10 @@
 
     resource_dirs: ["app/src/main/res"],
 
-     srcs: ["app/src/**/*.java",
-        "app/src/**/*.kt",],
+    srcs: [
+        "app/src/**/*.java",
+        "app/src/**/*.kt",
+    ],
 
     platform_apis: true,
 
diff --git a/libs/appgrid/lib/AndroidManifest.xml b/libs/appgrid/lib/AndroidManifest.xml
index 3d3d8ab..ccae000 100644
--- a/libs/appgrid/lib/AndroidManifest.xml
+++ b/libs/appgrid/lib/AndroidManifest.xml
@@ -39,6 +39,7 @@
         <activity
             android:name="com.android.car.carlauncher.AppGridActivity"
             android:launchMode="singleInstance"
+            android:windowSoftInputMode="adjustPan"
             android:exported="true"
             android:theme="@style/Theme.Launcher.AppGridActivity"
             android:excludeFromRecents="true">
diff --git a/libs/appgrid/lib/build.gradle b/libs/appgrid/lib/build.gradle
index 2cd55c6..b082fa0 100644
--- a/libs/appgrid/lib/build.gradle
+++ b/libs/appgrid/lib/build.gradle
@@ -18,6 +18,14 @@
     id 'com.android.library'
     id "com.google.protobuf"
     id 'kotlin-android'
+    id 'aconfig'
+}
+
+aconfig {
+    aconfigDeclaration {
+        packageName.set("com.android.car.carlauncher")
+        srcFile.setFrom(files("../../../app/car_launcher_flags.aconfig"))
+    }
 }
 
 android {
@@ -39,7 +47,6 @@
             res.srcDirs = ['res']
             java.srcDirs = ['src']
             java {
-                srcDirs += 'hidden_apis_disabled/src/com/android/car/carlauncher/hidden'
                 srcDirs += 'build/generated/source/proto/java'
             }
             kotlin {
@@ -49,6 +56,19 @@
                 srcDir 'src/com/android/car/carlauncher/proto' // default value
             }
         }
+
+        test {
+            java.srcDirs = ['robotests/src']
+            res.srcDirs   = ['robotests/res']
+        }
+
+    }
+
+    testOptions {
+        unitTests {
+            includeAndroidResources = true
+            returnDefaultValues = true
+        }
     }
 
     compileOptions {
@@ -68,26 +88,43 @@
 dependencies {
     compileOnly files(gradle.ext.lib_car_system_stubs)
     compileOnly files(gradle.ext.lib_car_ui_lib_oem_apis)
+    implementation project(":libs:hidden-apis-compat:hidden-apis-disabled")
+    implementation project(":libs:car-launcher-common")
     implementation project(":libs:car-apps-common")
     implementation project(":libs:car-media-common")
     implementation project(":libs:car-ui-lib")
+    implementation project(":docklib-util")
     implementation 'androidx.core:core-ktx:1.12.0'
-    api 'androidx.annotation:annotation:1.7.0'
+    api 'androidx.annotation:annotation:1.7.1'
     implementation 'androidx.appcompat:appcompat:1.6.1'
-    implementation 'com.google.android.material:material:1.10.0'
-    implementation 'com.android.car.ui:car-ui-lib:2.5.1'
+    implementation 'com.google.android.material:material:1.11.0'
     implementation 'com.google.guava:guava:31.0.1-jre'
-    testImplementation 'junit:junit:4.13.2'
-    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
-    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
-    implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation  "androidx.datastore:datastore:1.0.0"
-    implementation  "com.google.protobuf:protobuf-javalite:3.20.1"
+    implementation 'androidx.preference:preference-ktx:1.2.1'
+    implementation 'androidx.recyclerview:recyclerview:1.3.2'
+    implementation('com.google.protobuf:protobuf-javalite:3.25.1')
+    implementation 'androidx.media:media:1.7.0'
+    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    //Test dependencies
+    testImplementation files(gradle.ext.lib_car_system_stubs)
+    testImplementation files(gradle.ext.lib_car_test_api)
+    testImplementation 'junit:junit:4.13.2'
+    testImplementation 'androidx.test:core-ktx:1.5.0'
+
+    testImplementation 'org.robolectric:robolectric:4.11.1'
+
+    testImplementation "org.mockito:mockito-core:5.8.0"
+    testImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0"
+
+    testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3"
+
 }
 
 protobuf {
     protoc {
-        artifact = "com.google.protobuf:protoc:3.23.4"
+        artifact = "com.google.protobuf:protoc:3.25.1"
     }
 
     // Generates the java Protobuf-lite code for the Protobufs in this project. See
diff --git a/libs/appgrid/lib/res/layout/app_grid_activity.xml b/libs/appgrid/lib/res/layout/app_grid_activity.xml
deleted file mode 100644
index 2554924..0000000
--- a/libs/appgrid/lib/res/layout/app_grid_activity.xml
+++ /dev/null
@@ -1,53 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2019 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.
--->
-<com.android.car.ui.FocusArea
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:id="@+id/focus_area"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:gravity="center">
-    <LinearLayout
-        android:id="@+id/apps_grid_background"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:gravity="center">
-        <com.android.car.carlauncher.Banner
-            android:id="@+id/tos_banner"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:visibility="gone"
-            app:first_button_text="@string/banner_review_button_text"
-            app:second_button_text="@string/banner_dismiss_button_text"
-            app:title_text="@string/banner_title_text" />
-        <com.android.car.carlauncher.AppGridRecyclerView
-            android:id="@+id/apps_grid"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"/>
-        <FrameLayout
-            android:id="@+id/page_indicator_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-            <com.android.car.carlauncher.PageIndicator
-                android:id="@+id/page_indicator"
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/page_indicator_height"/>
-        </FrameLayout>
-    </LinearLayout>
-</com.android.car.ui.FocusArea>
diff --git a/libs/appgrid/lib/res/layout/app_grid_container_activity.xml b/libs/appgrid/lib/res/layout/app_grid_container_activity.xml
new file mode 100644
index 0000000..aed1ce5
--- /dev/null
+++ b/libs/appgrid/lib/res/layout/app_grid_container_activity.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.fragment.app.FragmentContainerView
+        android:id="@+id/fragmentContainer"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/libs/appgrid/lib/res/layout/app_grid_fragment.xml b/libs/appgrid/lib/res/layout/app_grid_fragment.xml
new file mode 100644
index 0000000..96bd0ea
--- /dev/null
+++ b/libs/appgrid/lib/res/layout/app_grid_fragment.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+    Copyright (C) 2019 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.
+-->
+<com.android.car.ui.FocusArea
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/focus_area"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:gravity="center">
+    <FrameLayout
+        android:id="@+id/apps_grid_background_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+        <com.android.car.carlauncher.Banner
+            app:first_button_text="@string/banner_review_button_text"
+            app:second_button_text="@string/banner_dismiss_button_text"
+            app:title_text="@string/banner_title_text"
+            android:id="@+id/tos_banner"
+            android:alpha="0.0"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:elevation="10dp"
+            android:visibility="gone" />
+        <LinearLayout
+            android:id="@+id/apps_grid_background"
+            android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:gravity="center">
+            <com.android.car.carlauncher.AppGridRecyclerView
+                android:id="@+id/apps_grid"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"/>
+            <FrameLayout
+                android:id="@+id/page_indicator_container"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+                <com.android.car.carlauncher.PageIndicator
+                    android:id="@+id/page_indicator"
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/page_indicator_height"/>
+            </FrameLayout>
+        </LinearLayout>
+    </FrameLayout>
+</com.android.car.ui.FocusArea>
diff --git a/libs/appgrid/lib/res/layout/banner.xml b/libs/appgrid/lib/res/layout/banner.xml
index fca31c1..147d2f8 100644
--- a/libs/appgrid/lib/res/layout/banner.xml
+++ b/libs/appgrid/lib/res/layout/banner.xml
@@ -22,7 +22,7 @@
     android:layout_height="wrap_content"
     android:background="@color/banner_background_color">
     <TextView
-        android:id="@+id/second_button"
+        android:id="@+id/banner_second_button"
         android:layout_marginEnd="56dp"
         app:layout_constraintEnd_toEndOf="parent"
         app:layout_constraintWidth_max="@dimen/banner_button_maximum_width"
@@ -30,9 +30,9 @@
         style="@style/BannerButton"/>
     <TextView
         android:background="@android:color/transparent"
-        android:id="@+id/first_button"
+        android:id="@+id/banner_first_button"
         android:layout_marginEnd="72dp"
-        app:layout_constraintEnd_toStartOf="@id/second_button"
+        app:layout_constraintEnd_toStartOf="@id/banner_second_button"
         app:layout_constraintWidth_max="@dimen/banner_button_maximum_width"
         app:layout_constraintTop_toTopOf="parent"
         style="@style/BannerButton"/>
@@ -45,12 +45,12 @@
         android:layout_marginTop="@dimen/banner_content_margin"
         android:layout_width="0dp"
         android:textColor="@color/banner_title_text_color"
-        app:layout_constraintEnd_toStartOf="@id/first_button"
-        app:layout_constraintStart_toEndOf="@+id/icon"
+        app:layout_constraintEnd_toStartOf="@id/banner_first_button"
+        app:layout_constraintStart_toEndOf="@+id/banner_icon"
         app:layout_constraintTop_toTopOf="parent"/>
 
     <ImageView
-        android:id="@+id/icon"
+        android:id="@+id/banner_icon"
         android:layout_height="@dimen/banner_image_view_size"
         android:layout_marginStart="35dp"
         android:layout_marginTop="@dimen/banner_content_margin"
diff --git a/libs/appgrid/lib/res/values-af/strings.xml b/libs/appgrid/lib/res/values-af/strings.xml
index a9e994c..e4a4ed3 100644
--- a/libs/appgrid/lib/res/values-af/strings.xml
+++ b/libs/appgrid/lib/res/values-af/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Hierdie funksie sal alle gepasmaakte rangskikking verwyder. Wil jy voortgaan?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alle apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media-apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stop app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Appinligting"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gestop."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Stop app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"As jy ’n app dwing om te stop, kan dit sleg optree."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"App kan nie gestop word nie."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan nie gebruik word terwyl jy bestuur nie."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Versteek ontfoutingapps"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Wys ontfoutingapps"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"voorneme:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-am/strings.xml b/libs/appgrid/lib/res/values-am/strings.xml
index e489dd5..694a108 100644
--- a/libs/appgrid/lib/res/values-am/strings.xml
+++ b/libs/appgrid/lib/res/values-am/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ይህ ተግባር ሁሉንም ብጁ ቅደም ተከተል ማስያዞች ያስወግዳል። መቀጠል ይፈልጋሉ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ሁሉም መተግበሪያዎች"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"የሚዲያ መተግበሪያዎች"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"መተግበሪያን አቁም"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"የመተግበሪያ መረጃ"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> እንዲቆም ተደርጓል።"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"መተግበሪያ ይቁም?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"አንድ መተግበሪያን በኃይል እንዲቆም ካደረጉት በትክክል ላይሠራ ይችላል።"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"መተግበሪያው ሊቆም አይችልም።"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> በመንዳት ላይ ሳለ ጥቅም ላይ መዋል አይችልም።"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"የስህተት ማረሚያ መተግበሪያዎችን ደብቅ"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"የስህተት ማረሚያ መተግበሪያዎችን አሳይ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ar/strings.xml b/libs/appgrid/lib/res/values-ar/strings.xml
index b810128..a93f352 100644
--- a/libs/appgrid/lib/res/values-ar/strings.xml
+++ b/libs/appgrid/lib/res/values-ar/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ستزيل هذه الدالة أي ترتيب مخصّص. هل تريد المتابعة؟"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"جميع التطبيقات"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"تطبيقات الوسائط"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"إيقاف التطبيق"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"معلومات التطبيقات"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"تم إيقاف \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"هل تريد إيقاف التطبيق؟"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"في حال فرض إيقاف التطبيق، قد لا يعمل بشكل صحيح."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"يتعذّر إيقاف التطبيق."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"لا يمكن استخدام \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" أثناء القيادة."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"إخفاء تطبيقات تصحيح الأخطاء"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"إظهار تطبيقات تصحيح الأخطاء"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-as/strings.xml b/libs/appgrid/lib/res/values-as/strings.xml
index 8003b6b..a51fe0f 100644
--- a/libs/appgrid/lib/res/values-as/strings.xml
+++ b/libs/appgrid/lib/res/values-as/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"এই ফাংশ্বনটোৱে আটাইবোৰ কাষ্টম অৰ্ডাৰ আঁতৰাব। আপুনি অব্যাহত ৰাখিব বিচাৰেনে?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"আটাইবোৰ এপ্‌"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"মিডিয়া এপ্‌"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"এপ্ বন্ধ কৰক"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"এপৰ তথ্য"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ কৰা হৈছে।"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"এপ্ বন্ধ কৰিবনে?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"আপুনি কোনো এপ্‌ বলেৰে বন্ধ কৰিবলৈ চেষ্টা কৰিলে, ই অস্বাভাৱিক আচৰণ কৰিব পাৰে।"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"এপ্‌ বন্ধ কৰিব নোৱাৰি।"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"গাড়ী চলাই থকাৰ সময়ত <xliff:g id="APP_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিব নোৱাৰি।"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ডিবাগ এপ্‌সমূহ লুকুৱাওক"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ডিবাগ এপ্‌সমূহ দেখুৱাওক"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-az/strings.xml b/libs/appgrid/lib/res/values-az/strings.xml
index ff2b6a9..fc5918d 100644
--- a/libs/appgrid/lib/res/values-az/strings.xml
+++ b/libs/appgrid/lib/res/values-az/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Bu funksiya fərdi sıralamanı siləcək. Davam edilsin?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Bütün tətbiqlər"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media tətbiqləri"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Tətbiqi dayandırın"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Tətbiq haqqında"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> dayandırılıb."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Tətbiq dayandırılsın?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Tətbiqi məcburi dayandırsanız, səhv işləyə bilər."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Tətbiqi dayandırmaq olmur."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Avtomobil sürərkən <xliff:g id="APP_NAME">%1$s</xliff:g> istifadə edilə bilməz."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sazlama tətbiqlərini gizlədin"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Sazlama tətbiqlərini göstərin"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-b+sr+Latn/strings.xml b/libs/appgrid/lib/res/values-b+sr+Latn/strings.xml
index a7863c7..1a36a31 100644
--- a/libs/appgrid/lib/res/values-b+sr+Latn/strings.xml
+++ b/libs/appgrid/lib/res/values-b+sr+Latn/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ova funkcija će ukloniti celokupan prilagođen raspored. Želite da nastavite?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Sve aplikacije"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medijske aplikacije"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zaustavi aplikaciju"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informacije o aplikaciji"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Želite da zaustavite aplikaciju?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ako prinudno zaustavite aplikaciju, možda će se ponašati neočekivano."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikacija ne može da se zaustavi."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne može da se koristi tokom vožnje."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sakrij aplikacije za otklanjanje grešaka"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Prikaži aplikacije za otklanjanje grešaka"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-be/strings.xml b/libs/appgrid/lib/res/values-be/strings.xml
index 71381d0..7dfb707 100644
--- a/libs/appgrid/lib/res/values-be/strings.xml
+++ b/libs/appgrid/lib/res/values-be/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Гэта функцыя скасуе любы карыстальніцкі парадак сартавання. Працягнуць?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Усе праграмы"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Мультымедыйныя праграмы"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Спыніць праграму"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Звесткі пра праграму"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" спынена."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Спыніць праграму?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Прымусовае спыненне праграмы можа прывесці да збою."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Немагчыма спыніць праграму."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" нельга карыстацца, калі вы за рулём."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Схаваць праграмы адладкі"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Паказаць праграмы адладкі"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-bg/strings.xml b/libs/appgrid/lib/res/values-bg/strings.xml
index d07284f..9efac53 100644
--- a/libs/appgrid/lib/res/values-bg/strings.xml
+++ b/libs/appgrid/lib/res/values-bg/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Тази функция ще премахне персонализираното нареждане. Искате ли да продължите?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Всички приложения"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Медийни приложения"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Спиране на приложението"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Информация от приложенията"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Приложението <xliff:g id="APP_NAME">%1$s</xliff:g> е спряно."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Да се спре ли приложението?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ако принудително спрете приложение, то може да не функционира правилно."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Приложението не може да бъде спряно."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се използва при шофиране."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Скриване на приложенията за отстраняване на грешки"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Показване на приложенията за отстраняв. на грешки"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-bn/strings.xml b/libs/appgrid/lib/res/values-bn/strings.xml
index 45d4e60..a3fe8df 100644
--- a/libs/appgrid/lib/res/values-bn/strings.xml
+++ b/libs/appgrid/lib/res/values-bn/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"এই ফাংশন সবকটি কাস্টম অর্ডারিং সরিয়ে দেবে। আপনি কি এগিয়ে যেতে চান?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"সব অ্যাপ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"মিডিয়া অ্যাপ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"অ্যাপ বন্ধ করুন"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"অ্যাপের তথ্য"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"অ্যাপ বন্ধ করবেন?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"আপনি কোনও অ্যাপকে জোর করে বন্ধ করলে, তা সঠিক ভাবে কাজ নাও করতে পারে।"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"অ্যাপ বন্ধ করা যাচ্ছে না।"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ড্রাইভ করার সময় <xliff:g id="APP_NAME">%1$s</xliff:g> ব্যবহার করা যাবে না।"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ডিবাগ অ্যাপ লুকান"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ডিবাগ অ্যাপ দেখুন"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-bs/strings.xml b/libs/appgrid/lib/res/values-bs/strings.xml
index 1f931a9..09d2753 100644
--- a/libs/appgrid/lib/res/values-bs/strings.xml
+++ b/libs/appgrid/lib/res/values-bs/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ova funkcija će ukloniti sve prilagođene narudžbe. Želite li nastaviti?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Sve aplikacije"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medijske aplikacije"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zaustavi aplikaciju"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informacije o aplikaciji"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Zaustaviti aplikaciju?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ako prisilno zaustavite aplikaciju, moguće je da će se ponašati nepredviđeno."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Nije moguće zaustaviti aplikaciju."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Nije moguće koristiti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> tokom vožnje."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sakrij aplikacije za otklanjanje grešaka"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Prikaži aplikacije za otklanjanje grešaka"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ca/strings.xml b/libs/appgrid/lib/res/values-ca/strings.xml
index bad0936..53d48a0 100644
--- a/libs/appgrid/lib/res/values-ca/strings.xml
+++ b/libs/appgrid/lib/res/values-ca/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Aquesta funció suprimirà l\'ordre personalitzat. Vols continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Totes les aplicacions"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplicacions multimèdia"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Atura l\'aplicació"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informació de l\'aplicació"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'ha aturat."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Vols aturar l\'aplicació?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Si forces l\'aturada d\'una aplicació, és possible que no funcioni correctament."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"L\'aplicació no es pot aturar."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"No es pot utilitzar <xliff:g id="APP_NAME">%1$s</xliff:g> mentre es condueix."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Amaga les aplicacions de depuració"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostra les aplicacions de depuració"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-cs/strings.xml b/libs/appgrid/lib/res/values-cs/strings.xml
index 42c7b54..0c6d134 100644
--- a/libs/appgrid/lib/res/values-cs/strings.xml
+++ b/libs/appgrid/lib/res/values-cs/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Tato funkce zruší veškeré vlastní řazení. Chcete pokračovat?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Všechny aplikace"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Mediální aplikace"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zastavit aplikaci"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informace o aplikaci"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byla zastavena."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Zastavit aplikaci?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Vynucené zastavení může způsobit nepředvídatelné chování aplikace."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikaci nelze ukončit."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> nelze používat při řízení."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Skrýt ladicí aplikace"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Zobrazit ladicí aplikace"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-da/strings.xml b/libs/appgrid/lib/res/values-da/strings.xml
index 90f9b1e..39b61d7 100644
--- a/libs/appgrid/lib/res/values-da/strings.xml
+++ b/libs/appgrid/lib/res/values-da/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Denne funktion fjerner alle tilpassede rækkefølger. Vil du fortsætte?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alle apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medieapps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stands app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Appoplysninger"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> er blevet standset."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Vil du standse appen?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Hvis du tvinger en app til at standse, kan det medføre, at den ikke fungerer korrekt."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Appen kan ikke standses."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan ikke bruges under kørsel."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Skjul apps til fejlretning"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Vis apps til fejlretning"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-de/strings.xml b/libs/appgrid/lib/res/values-de/strings.xml
index 259b82b..7817c11 100644
--- a/libs/appgrid/lib/res/values-de/strings.xml
+++ b/libs/appgrid/lib/res/values-de/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Durch diese Funktion wird die gesamte benutzerdefinierte Sortierung entfernt. Möchtest du fortfahren?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alle Apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medien-Apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"App beenden"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"App-Info"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> wurde beendet."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"App beenden?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Das Beenden der App zu erzwingen kann zu unerwünschtem Verhalten führen."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Die App kann nicht beendet werden."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> kann während der Fahrt nicht genutzt werden."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Debug-Apps verbergen"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Debug-Apps anzeigen"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-el/strings.xml b/libs/appgrid/lib/res/values-el/strings.xml
index d5f12fa..4a98939 100644
--- a/libs/appgrid/lib/res/values-el/strings.xml
+++ b/libs/appgrid/lib/res/values-el/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Αυτή η λειτουργία θα καταργήσει όλες τις προσαρμοσμένες ταξινομήσεις. Θέλετε να συνεχίσετε;"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Όλες οι εφαρμογές"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Εφαρμογές μέσων"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Διακοπή εφαρμογής"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Πληροφορίες εφαρμογής"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> διακόπηκε."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Διακοπή εφαρμογής;"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Αν κάνετε αναγκαστική διακοπή μιας εφαρμογής, ενδέχεται να μην λειτουργεί σωστά."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Δεν είναι δυνατή η διακοπή της εφαρμογής."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Αδύνατη η χρήση της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> κατά την οδήγηση."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Απόκρυψη εφαρμογών εντοπισμού σφαλμάτων"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Εμφάνιση εφαρμογών εντοπισμού σφαλμάτων"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-en-rAU/strings.xml b/libs/appgrid/lib/res/values-en-rAU/strings.xml
index c7d3cc2..06e44c5 100644
--- a/libs/appgrid/lib/res/values-en-rAU/strings.xml
+++ b/libs/appgrid/lib/res/values-en-rAU/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"This function will remove all customised ordering. Do you want to continue?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"All apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stop app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"App info"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Stop app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"If you force stop an app, it may misbehave."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"App can\'t be stopped."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Hide debug apps"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Show debug apps"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-en-rCA/strings.xml b/libs/appgrid/lib/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..09c85fd
--- /dev/null
+++ b/libs/appgrid/lib/res/values-en-rCA/strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+    Copyright (C) 2018 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.
+  -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="reset_appgrid_title" msgid="6491348358859198288">"Reset app grid to A-Z order"</string>
+    <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"This function will remove all custom ordering. Do you want to continue?"</string>
+    <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"All apps"</string>
+    <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media apps"</string>
+    <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"App can’t be stopped."</string>
+    <string name="hide_debug_apps" msgid="7140064693464751647">"Hide debug apps"</string>
+    <string name="show_debug_apps" msgid="2748157232151197494">"Show debug apps"</string>
+    <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
+    <string name="banner_title_text" msgid="8827498256184464356">"To use user tos disabled apps, agree to User tos"</string>
+    <string name="banner_review_button_text" msgid="369410598918950148">"Review"</string>
+    <string name="banner_dismiss_button_text" msgid="5389352614429069562">"Not Now"</string>
+</resources>
diff --git a/libs/appgrid/lib/res/values-en-rGB/strings.xml b/libs/appgrid/lib/res/values-en-rGB/strings.xml
index c7d3cc2..06e44c5 100644
--- a/libs/appgrid/lib/res/values-en-rGB/strings.xml
+++ b/libs/appgrid/lib/res/values-en-rGB/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"This function will remove all customised ordering. Do you want to continue?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"All apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stop app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"App info"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Stop app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"If you force stop an app, it may misbehave."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"App can\'t be stopped."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Hide debug apps"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Show debug apps"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-en-rIN/strings.xml b/libs/appgrid/lib/res/values-en-rIN/strings.xml
index c7d3cc2..06e44c5 100644
--- a/libs/appgrid/lib/res/values-en-rIN/strings.xml
+++ b/libs/appgrid/lib/res/values-en-rIN/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"This function will remove all customised ordering. Do you want to continue?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"All apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stop app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"App info"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Stop app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"If you force stop an app, it may misbehave."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"App can\'t be stopped."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Hide debug apps"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Show debug apps"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-en-rXC/strings.xml b/libs/appgrid/lib/res/values-en-rXC/strings.xml
index a993053..0d9b4bb 100644
--- a/libs/appgrid/lib/res/values-en-rXC/strings.xml
+++ b/libs/appgrid/lib/res/values-en-rXC/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‎‎‏‏‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‏‎‎‎‎‎‎‎‎‎‏‎‏‏‎‎‎‏‎‎This function will remove all custom ordering. Do you want to continue?‎‏‎‎‏‎"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‏‎‎‏‏‎‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‎All apps‎‏‎‎‏‎"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‏‏‏‎‎‎‎‏‏‎‎‎‎‎‎‎‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‎‎‏‏‎‏‏‏‏‎‎Media apps‎‏‎‎‏‎"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‎‎‏‎‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‎‎‎‎‎‎‏‎‎‎‏‎‎Stop app‎‏‎‎‏‎"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‎‎‏‎‏‎‎‎App info‎‏‎‎‏‎"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has been stopped.‎‏‎‎‏‎"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‏‎‎Stop app?‎‏‎‎‏‎"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‎‎If you force stop an app, it may misbehave.‎‏‎‎‏‎"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‏‏‏‎‏‎‎App can’t be stopped.‎‏‎‎‏‎"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‏‏‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ can\'t be used while driving.‎‏‎‎‏‎"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‎‏‎‏‏‎‏‎‎‏‏‎‏‏‎‎‎‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‎‎‎‏‏‏‏‏‎Hide debug apps‎‏‎‎‏‎"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‏‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎Show debug apps‎‏‎‎‏‎"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‎‏‏‎intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end‎‏‎‎‏‎"</string>
diff --git a/libs/appgrid/lib/res/values-es-rUS/strings.xml b/libs/appgrid/lib/res/values-es-rUS/strings.xml
index 80e0ae2..bf1fdbc 100644
--- a/libs/appgrid/lib/res/values-es-rUS/strings.xml
+++ b/libs/appgrid/lib/res/values-es-rUS/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Esta función quitará todo el orden personalizado. ¿Quieres continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Todas las apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Apps de música"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Detener app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Información de la app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Se detuvo <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"¿Quieres detener la app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Si fuerzas la detención de una app, es posible que funcione incorrectamente."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"No se puede detener la app."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"No puedes usar <xliff:g id="APP_NAME">%1$s</xliff:g> mientras conduces."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ocultar apps de depuración"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostrar apps de depuración"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-es/strings.xml b/libs/appgrid/lib/res/values-es/strings.xml
index fc387e2..63cbadf 100644
--- a/libs/appgrid/lib/res/values-es/strings.xml
+++ b/libs/appgrid/lib/res/values-es/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Esta función eliminará todo el orden personalizado. ¿Quieres continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Todas las aplicaciones"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplicaciones multimedia"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Detener aplicación"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Información de la aplicación"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> se ha detenido."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"¿Detener la aplicación?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Si fuerzas la detención de una aplicación, puede que no funcione correctamente."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"La aplicación no se puede detener."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> no se puede usar mientras se conduce."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ocultar aplicaciones de depuración"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostrar aplicaciones de depuración"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-et/strings.xml b/libs/appgrid/lib/res/values-et/strings.xml
index a009afc..dfb991f 100644
--- a/libs/appgrid/lib/res/values-et/strings.xml
+++ b/libs/appgrid/lib/res/values-et/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"See funktsioon eemaldab kogu kohandatud järjestuse. Kas soovite jätkata?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Kõik rakendused"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Meediarakendused"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Peata rakendus"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Rakenduse teave"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> peatati."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Kas peatada rakendus?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Kui sundpeatate rakenduse, võib see valesti toimida."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Rakendust ei saa peatada."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa sõidu ajal kasutada."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Peida silumisrakendused"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Kuva silumisrakendused"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-eu/strings.xml b/libs/appgrid/lib/res/values-eu/strings.xml
index 9a2cbc1..8100fe5 100644
--- a/libs/appgrid/lib/res/values-eu/strings.xml
+++ b/libs/appgrid/lib/res/values-eu/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Funtzio horrek hurrenkera pertsonalizatua kenduko du. Aurrera egin nahi duzu?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Aplikazio guztiak"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Multimedia-aplikazioak"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Gelditu aplikazioa"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Aplikazioari buruzko informazioa"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Gelditu da <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Aplikazioa gelditu nahi duzu?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Aplikazioak gelditzera behartzen badituzu, baliteke behar bezala ez funtzionatzea."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Ezin da gelditu aplikazioa."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezin da erabili gidatu bitartean."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ezkutatu arazteko aplikazioak"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Erakutsi arazteko aplikazioak"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-fa/strings.xml b/libs/appgrid/lib/res/values-fa/strings.xml
index d179c4f..1750368 100644
--- a/libs/appgrid/lib/res/values-fa/strings.xml
+++ b/libs/appgrid/lib/res/values-fa/strings.xml
@@ -21,17 +21,11 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"این عملکرد همه ترتیب‌های سفارشی را حذف خواهد کرد. می‌خواهید ادامه دهید؟"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"همه برنامه‌ها"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"برنامه‌های رسانه"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"متوقف کردن برنامه"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"اطلاعات برنامه"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شده است."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"برنامه متوقف شود؟"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"توقف اجباری برنامه ممکن است باعث عملکرد نادرست آن شود."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"برنامه متوقف نمی‌شود."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"هنگام رانندگی نمی‌توان از <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده کرد."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"پنهان کردن برنامه‌های اشکال‌زدایی"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"نمایش برنامه‌های اشکال‌زدایی"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
     <string name="banner_title_text" msgid="8827498256184464356">"برای استفاده از برنامه‌هایی که به‌دلیل شرایط خدمات کاربر غیرفعال شده‌اند، با «شرایط خدمات کاربر» موافقت کنید"</string>
     <string name="banner_review_button_text" msgid="369410598918950148">"مرور کردن"</string>
-    <string name="banner_dismiss_button_text" msgid="5389352614429069562">"اکنون نه"</string>
+    <string name="banner_dismiss_button_text" msgid="5389352614429069562">"حالا نه"</string>
 </resources>
diff --git a/libs/appgrid/lib/res/values-fi/strings.xml b/libs/appgrid/lib/res/values-fi/strings.xml
index 6ad5ac6..e56ac63 100644
--- a/libs/appgrid/lib/res/values-fi/strings.xml
+++ b/libs/appgrid/lib/res/values-fi/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Toiminto poistaa kaikki omat järjestykset. Haluatko jatkaa?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Kaikki sovellukset"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Mediasovellukset"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Sulje sovellus"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Sovelluksen tiedot"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> on suljettu."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Suljetaanko sovellus?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Jos pakotat sovelluksen sulkeutumaan, se ei välttämättä toimi oikein."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Sovellusta ei voi keskeyttää."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei voi olla käytössä ajon aikana."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Piilota virheenkorjaussovellukset"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Näytä virheenkorjaussovellukset"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-fr-rCA/strings.xml b/libs/appgrid/lib/res/values-fr-rCA/strings.xml
index 2203ae8..49d83e5 100644
--- a/libs/appgrid/lib/res/values-fr-rCA/strings.xml
+++ b/libs/appgrid/lib/res/values-fr-rCA/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Cette fonction va retirer tous les classements personnalisés. Voulez-vous continuer?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Toutes les applications"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Applications multimédias"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Arrêter l\'application"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Détails de l\'application"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> a été arrêtée."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Voulez-vous arrêter l\'application?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Si vous forcez l\'arrêt d\'une application, son fonctionnement peut en être affecté."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Impossible d\'arrêter l\'application."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne peut pas être utilisée en conduisant."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Masquer les applications de débogage"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Afficher les applications de débogage"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-fr/strings.xml b/libs/appgrid/lib/res/values-fr/strings.xml
index 5a251ec..a24af5a 100644
--- a/libs/appgrid/lib/res/values-fr/strings.xml
+++ b/libs/appgrid/lib/res/values-fr/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Cette fonction supprimera tous les tris personnalisés. Voulez-vous continuer ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Toutes les applications"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Applications multimédias"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Arrêter l\'appli"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informations d\'applications"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> a été arrêtée."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Arrêter l\'appli ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"L\'arrêt forcé d\'une appli peut provoquer un fonctionnement instable."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Impossible d\'arrêter l\'appli."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Impossible d\'utiliser <xliff:g id="APP_NAME">%1$s</xliff:g> en conduisant."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Masquer les applis de débogage"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Afficher les applis de débogage"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-gl/strings.xml b/libs/appgrid/lib/res/values-gl/strings.xml
index 29fb3e9..a9a821a 100644
--- a/libs/appgrid/lib/res/values-gl/strings.xml
+++ b/libs/appgrid/lib/res/values-gl/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Esta función quitará toda a orde personalizada. Queres continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Todas as aplicacións"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplicacións multimedia"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Deter aplicación"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Información da aplicación"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Detívose a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Queres deter a aplicación?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Se forzas a parada dunha aplicación, é posible que non funcione correctamente."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Non se pode deter a aplicación."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Non se pode utilizar <xliff:g id="APP_NAME">%1$s</xliff:g> mentres se conduce."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ocultar aplicacións de depuración"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostrar aplicacións de depuración"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-gu/strings.xml b/libs/appgrid/lib/res/values-gu/strings.xml
index db2961a..893e20b 100644
--- a/libs/appgrid/lib/res/values-gu/strings.xml
+++ b/libs/appgrid/lib/res/values-gu/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"આ સુવિધા, સેટ કરવામાં આવેલા બધા કસ્ટમ ક્રમ કાઢી નાખશે. શું તમે ચાલુ રાખવા માગો છો?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"બધી ઍપ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"મીડિયા ઍપ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ઍપ બંધ કરો"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ઍપની માહિતી"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> બંધ કરવામાં આવી છે."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"શું ઍપ બંધ કરીએ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"જો તમે કોઈ ઍપને ફરજિયાત બંધ કરો, તો તે અયોગ્ય વર્તન કરી શકે છે."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ઍપ બંધ કરી શકાતી નથી."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ડ્રાઇવ કરતી વખતે <xliff:g id="APP_NAME">%1$s</xliff:g>નો ઉપયોગ કરી શકાતો નથી."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ડિબગ ઍપ છુપાવો"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ડિબગ ઍપ બતાવો"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-hi/strings.xml b/libs/appgrid/lib/res/values-hi/strings.xml
index 8c48d20..7cfe1dc 100644
--- a/libs/appgrid/lib/res/values-hi/strings.xml
+++ b/libs/appgrid/lib/res/values-hi/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"इस कार्रवाई से, पसंद के मुताबिक सेट किए सभी क्रम हटा दिए जाएंगे. क्या आपको यह कार्रवाई करनी है?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"सभी ऐप्लिकेशन"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"मीडिया ऐप्लिकेशन"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ऐप्लिकेशन को बंद करें"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ऐप्लिकेशन की जानकारी"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> को रोक दिया गया है."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"क्या आपको ऐप्लिकेशन रोकना है?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"अगर किसी ऐप्लिकेशन को ज़बरदस्ती रोका जाता है, तो हो सकता है कि वह ठीक तरह से काम न करें."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ऐप्लिकेशन को बंद नहीं किया जा सका."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"गाड़ी चलाते समय <xliff:g id="APP_NAME">%1$s</xliff:g> ऐप्लिकेशन इस्तेमाल नहीं किया जा सकता."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"डीबग किए गए ऐप्लिकेशन छिपाएं"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"डीबग किए गए ऐप्लिकेशन दिखाएं"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-hr/strings.xml b/libs/appgrid/lib/res/values-hr/strings.xml
index 248d751..d040c32 100644
--- a/libs/appgrid/lib/res/values-hr/strings.xml
+++ b/libs/appgrid/lib/res/values-hr/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ta će funkcija ukloniti sav prilagođeni poredak. Želite li nastaviti?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Sve aplikacije"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplikacije za medijske sadržaje"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zaustavi aplikaciju"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informacije o aplikaciji"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Želite li zaustaviti aplikaciju?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ako silom zaustavite aplikaciju, možda će se ponašati nepredviđeno."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikacija se ne može zaustaviti."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne može se koristiti tijekom vožnje."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sakrij aplikacije za otklanjanje pogrešaka"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Prikaži aplikacije za otklanjanje pogrešaka"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-hu/strings.xml b/libs/appgrid/lib/res/values-hu/strings.xml
index 2295436..e048db7 100644
--- a/libs/appgrid/lib/res/values-hu/strings.xml
+++ b/libs/appgrid/lib/res/values-hu/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"A funkció bekapcsolásával minden egyéni elrendezést eltávolít. Biztosan folytatja?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Összes alkalmazás"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Médiaalkalmazások"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Alkalmazás leállítása"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Alkalmazásadatok"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> leállítva."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Leállítja az alkalmazást?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ha egy alkalmazást leállásra kényszerít, lehetséges, hogy hibásan fog működni."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Az alkalmazás nem állítható le."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> nem használható vezetés közben."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Hibakereső alkalmazások elrejtése"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Hibakereső alkalmazások megjelenítése"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-hy/strings.xml b/libs/appgrid/lib/res/values-hy/strings.xml
index 2f1ed69..d7b1fbf 100644
--- a/libs/appgrid/lib/res/values-hy/strings.xml
+++ b/libs/appgrid/lib/res/values-hy/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Այս գործառույթը կհեռացնի հավելվածների հատուկ դասավորությունը։ Շարունակե՞լ։"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Բոլոր հավելվածները"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Մեդիա հավելվածներ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Կանգնեցնել հավելվածը"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Հավելվածի մասին"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը կանգնեցվեց։"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Կանգնեցնե՞լ հավելվածը"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Հավելվածի ստիպողական կանգնեցումը կարող է ազդել դրա աշխատանքի վրա։"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Չհաջողվեց կանգնեցնել հավելվածի աշխատանքը։"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը հնարավոր չէ օգտագործել վարելու ժամանակ։"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Թաքցնել վրիպազերծման հավելվածները"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Ցույց տալ վրիպազերծման հավելվածները"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-in/strings.xml b/libs/appgrid/lib/res/values-in/strings.xml
index 885f891..0c75392 100644
--- a/libs/appgrid/lib/res/values-in/strings.xml
+++ b/libs/appgrid/lib/res/values-in/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Fungsi ini akan menghapus semua pengurutan kustom. Ingin melanjutkan?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Semua aplikasi"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplikasi media"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Hentikan aplikasi"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Info aplikasi"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah dihentikan."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Hentikan aplikasi?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Jika aplikasi dihentikan paksa, fungsinya mungkin akan terganggu."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikasi tidak dapat dihentikan."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat digunakan saat mengemudi."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sembunyikan aplikasi debug"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Tampilkan aplikasi debug"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-is/strings.xml b/libs/appgrid/lib/res/values-is/strings.xml
index 21819f9..037666a 100644
--- a/libs/appgrid/lib/res/values-is/strings.xml
+++ b/libs/appgrid/lib/res/values-is/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Þessi eiginleiki fjarlægir alla sérsniðna röðun. Viltu halda áfram?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Öll forrit"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Margmiðlunarforrit"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stöðva forrit"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Upplýsingar um forrit"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> var stöðvað."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Stöðva forritið?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ef þú þvingar lokun forrits gæti það látið illa."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Ekki er hægt að stöðva forrit."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Ekki er hægt að nota <xliff:g id="APP_NAME">%1$s</xliff:g> við akstur."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Fela villuleitarforrit"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Sýna villuleitarforrit"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-it/strings.xml b/libs/appgrid/lib/res/values-it/strings.xml
index 2ab37fe..f7be1fa 100644
--- a/libs/appgrid/lib/res/values-it/strings.xml
+++ b/libs/appgrid/lib/res/values-it/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Questa funzione rimuove tutti gli ordini personalizzati. Vuoi continuare?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Tutte le app"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"App multimediali"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Interrompi l\'app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informazioni app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è stata interrotta."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Interrompere l\'app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Se forzi l\'interruzione di un\'app, questa potrebbe funzionare in modo anomalo."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Impossibile interrompere l\'app."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Non è possibile usare l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> durante la guida."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Nascondi app di debug"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostra app di debug"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-iw/strings.xml b/libs/appgrid/lib/res/values-iw/strings.xml
index 4513b26..ecd1805 100644
--- a/libs/appgrid/lib/res/values-iw/strings.xml
+++ b/libs/appgrid/lib/res/values-iw/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"הפונקציה הזו תסיר את כל סידורי התצוגה שהותאמו אישית. רוצה להמשיך?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"כל האפליקציות"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"אפליקציות מדיה"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"עצירת האפליקציה"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"פרטי האפליקציה"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"עצרת את האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"לעצור את האפליקציה?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"אם סוגרים אפליקציה באופן ידני, יכול להיות שהיא לא תפעל כמו שצריך."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"לא ניתן לעצור את פעולת האפליקציה."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"לא ניתן להשתמש באפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> במהלך הנהיגה."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"הסתרת אפליקציות לניפוי באגים"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"הצגת אפליקציות לניפוי באגים"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ja/strings.xml b/libs/appgrid/lib/res/values-ja/strings.xml
index fefa214..c34b301 100644
--- a/libs/appgrid/lib/res/values-ja/strings.xml
+++ b/libs/appgrid/lib/res/values-ja/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"この機能を実行すると、カスタムの並べ替えがすべて削除されます。続行しますか?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"すべてのアプリ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"メディアアプリ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"アプリを停止"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"アプリ情報"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> を停止しました。"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"アプリを停止しますか?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"アプリを強制停止すると、アプリが正常に機能しないことがあります。"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"アプリを停止できません。"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"運転中は <xliff:g id="APP_NAME">%1$s</xliff:g> を使用できません。"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"デバッグアプリを表示しない"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"デバッグアプリを表示する"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ka/strings.xml b/libs/appgrid/lib/res/values-ka/strings.xml
index 4f7d5e7..6114bb8 100644
--- a/libs/appgrid/lib/res/values-ka/strings.xml
+++ b/libs/appgrid/lib/res/values-ka/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ეს ფუნქცია წაშლის ყველა მორგებულ შეკვეთას. გსურთ გაგრძელება?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ყველა აპი"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"მედია აპები"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"აპის შეჩერება"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"აპის ინფორმაცია"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> შეჩერებულია."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"გსურთ აპის შეჩერება?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"თუ აპს შეწყვეტას აიძულებთ, შესაძლოა, მან ცუდად განაგრძოს მუშაობა."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"აპის შეჩერება შეუძლებელია."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"მანქანის მართვისას <xliff:g id="APP_NAME">%1$s</xliff:g>-ს ვერ გამოიყენებთ."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"გამართვის აპების დამალვა"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"გამართვის აპების ჩვენება"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-kk/strings.xml b/libs/appgrid/lib/res/values-kk/strings.xml
index 24ff876..35616a0 100644
--- a/libs/appgrid/lib/res/values-kk/strings.xml
+++ b/libs/appgrid/lib/res/values-kk/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Бұл функция арнаулы ретті толығымен өшіреді. Жалғастырғыңыз келе ме?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Барлық қолданба"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Мультимедиа қолданбалары"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Қолданба жұмысын тоқтату"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Қолданба туралы ақпарат"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> жұмысы тоқтатылды."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Қолданба жұмысы тоқтатылсын ба?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Қолданбаны қолмен тоқтату оның жұмысына кері әсерін тигізуі мүмкін."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Қолданба жұмысын тоқтату мүмкін емес."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Көлік жүргізу кезінде <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын пайдалануға болмайды."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Түзету қолданбаларын жасыру"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Түзету қолданбаларын көрсету"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-km/strings.xml b/libs/appgrid/lib/res/values-km/strings.xml
index 731e396..6599142 100644
--- a/libs/appgrid/lib/res/values-km/strings.xml
+++ b/libs/appgrid/lib/res/values-km/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"មុខងារនេះនឹងដកការបញ្ជាទិញផ្ទាល់ខ្លួនទាំងអស់ចេញ។ តើ​អ្នក​ចង់​បន្ត​ដែរ​ឬ​ទេ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"កម្មវិធី​ទាំងអស់"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"កម្មវិធីមេឌៀ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"បញ្ឈប់កម្មវិធី"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ព័ត៌មានកម្មវិធី"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ត្រូវបានបញ្ឈប់។"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"បញ្ឈប់កម្មវិធីឬ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ប្រសិនបើអ្នក​បង្ខំឱ្យបញ្ឈប់​កម្មវិធី​ កម្មវិធីអាចនឹង​ដំណើរការ​មិនត្រឹមត្រូវ។"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"មិនអាច​បញ្ឈប់កម្មវិធី​បានទេ។"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"មិនអាចប្រើ​ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេលបើកបរបានទេ។"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"លាក់កម្មវិធីជួសជុល"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"បង្ហាញកម្មវិធីជួសជុល"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-kn/strings.xml b/libs/appgrid/lib/res/values-kn/strings.xml
index ecd5344..5b0c65f 100644
--- a/libs/appgrid/lib/res/values-kn/strings.xml
+++ b/libs/appgrid/lib/res/values-kn/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ಈ ಕಾರ್ಯವು ಎಲ್ಲಾ ಕಸ್ಟಮ್ ಆರ್ಡರ್ ಮಾಡುವಿಕೆಯನ್ನು ತೆಗೆದುಹಾಕುತ್ತದೆ. ನೀವು ಮುಂದುವರಿಸಲು ಬಯಸುತ್ತೀರಾ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ಎಲ್ಲಾ ಆ್ಯಪ್‌ಗಳು"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"ಮಾಧ್ಯಮ ಆ್ಯಪ್‌ಗಳು"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ಆ್ಯಪ್ ಅನ್ನು ನಿಲ್ಲಿಸಿ"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ಆ್ಯಪ್ ಅನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಬಲವಂತವಾಗಿ ನಿಲ್ಲಿಸಿದರೆ, ಅದು ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರಬಹುದು."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ಆ್ಯಪ್ ನಿಲ್ಲಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ಡ್ರೈವ್ ಮಾಡುವಾಗ <xliff:g id="APP_NAME">%1$s</xliff:g> ಬಳಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ಡೀಬಗ್ ಆ್ಯಪ್‌ಗಳನ್ನು ಮರೆಮಾಡಿ"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ಡೀಬಗ್ ಆ್ಯಪ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ko/strings.xml b/libs/appgrid/lib/res/values-ko/strings.xml
index 3f417fe..846e8f2 100644
--- a/libs/appgrid/lib/res/values-ko/strings.xml
+++ b/libs/appgrid/lib/res/values-ko/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"이 기능은 모든 맞춤설정 정렬을 삭제합니다. 계속하시겠습니까?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"모든 앱"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"미디어 앱"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"앱 종료"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"앱 정보"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 종료되었습니다."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"앱을 종료하시겠습니까?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"강제로 앱을 종료하면 예기치 않은 오류가 발생할 수 있습니다."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"앱을 닫을 수 없습니다."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"운전 중에는 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"디버그 앱 숨기기"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"디버그 앱 표시"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;end"</string>
diff --git a/libs/appgrid/lib/res/values-ky/strings.xml b/libs/appgrid/lib/res/values-ky/strings.xml
index a506c7c..37b02b0 100644
--- a/libs/appgrid/lib/res/values-ky/strings.xml
+++ b/libs/appgrid/lib/res/values-ky/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Бул функция бардык ыңгайлаштырылган иреттерди өчүрөт. Улантасызбы?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Бардык колдонмолор"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Мультимедиа колдонмолору"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Колдонмону токтотуу"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Колдонмо тууралуу"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> токтотулду."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Колдонмону токтотосузбу?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Колдонмону мажбурлап токтотсоңуз, ал туура эмес иштеп калышы мүмкүн."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Колдонмону токтотууга болбойт."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Айдап баратканда <xliff:g id="APP_NAME">%1$s</xliff:g> колдонулбайт."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Мүчүлүштүктөрдү оңдоочу колдонмолорду жашыруу"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Мүчүлүштүктөрдү оңдоочу колдонмолорду көрсөтүү"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-lo/strings.xml b/libs/appgrid/lib/res/values-lo/strings.xml
index 738a165..fe98eae 100644
--- a/libs/appgrid/lib/res/values-lo/strings.xml
+++ b/libs/appgrid/lib/res/values-lo/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ຟັງຊັນນີ້ຈະລຶບການຈັດລຳດັບທີ່ກຳນົດເອງອອກທັງໝົດ. ທ່ານຕ້ອງການສືບຕໍ່ບໍ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ແອັບທັງໝົດ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"ແອັບມີເດຍ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ຢຸດແອັບ"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ຂໍ້ມູນແອັບ"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"ຢຸດ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ຢຸດແອັບໄວ້ບໍ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ຫານທ່ານບັງຄັບປິດແອັບໃດໜຶ່ງ, ມັນອາດເຮັດວຽກຜິດປົກກະຕິໄດ້."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ບໍ່ສາມາດຢຸດແອັບໄດ້."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ບໍ່ສາມາດໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ໃນຂະນະທີ່ຂັບລົດໄດ້."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ເຊື່ອງແອັບດີບັກ"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ສະແດງແອັບດີບັກ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-lt/strings.xml b/libs/appgrid/lib/res/values-lt/strings.xml
index ca94d0e..12ddee8 100644
--- a/libs/appgrid/lib/res/values-lt/strings.xml
+++ b/libs/appgrid/lib/res/values-lt/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ši funkcija pašalins visą tinkintą tvarką. Ar norite tęsti?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Visos programos"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medijos programos"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Sustabdyti programą"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Programos informacija"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ sustabdyta."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Sustabdyti programą?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Jei priverstinai sustabdysite programą, ji gali neveikti tinkamai."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Programos negalima sustabdyti."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Vairuojant negalima naudoti „<xliff:g id="APP_NAME">%1$s</xliff:g>“."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Slėpti derinimo programas"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Rodyti derinimo programas"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-lv/strings.xml b/libs/appgrid/lib/res/values-lv/strings.xml
index 0ba342d..93fdbda 100644
--- a/libs/appgrid/lib/res/values-lv/strings.xml
+++ b/libs/appgrid/lib/res/values-lv/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ar šo funkciju tiks mainīta pielāgotā lietotņu secība. Vai vēlaties turpināt?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Visas lietotnes"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Multivides lietotnes"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Apturēt lietotnes darbību"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informācija par lietotni"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> darbība ir apturēta."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Vai apturēt lietotnes darbību?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Piespiedu kārtā apturot lietotnes darbību, var rasties šīs lietotnes darbības traucējumi."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Lietotnes darbību nevar apturēt."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevar izmantot braukšanas laikā."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Slēpt atkļūdošanas lietotnes"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Rādīt atkļūdošanas lietotnes"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-mk/strings.xml b/libs/appgrid/lib/res/values-mk/strings.xml
index 1f204c1..dee31cb 100644
--- a/libs/appgrid/lib/res/values-mk/strings.xml
+++ b/libs/appgrid/lib/res/values-mk/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Оваа функција ќе ги отстрани сите приспособени подредувања. Дали сакате да продолжите?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Сите апликации"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Апликации за аудиовизуелни содржини"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Сопри ја апликацијата"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Информации за апликацијата"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> е сопрена."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Да се сопре апликацијата?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ако сопрете апликација присилно, таа може да не се однесува правилно."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Апликацијата не може да се сопре."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се користи при возење."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Скриј апликации за отстранување грешки"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Прикажи апликации за отстранување грешки"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ml/strings.xml b/libs/appgrid/lib/res/values-ml/strings.xml
index 868d31c..e1bfd41 100644
--- a/libs/appgrid/lib/res/values-ml/strings.xml
+++ b/libs/appgrid/lib/res/values-ml/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ഈ ഫംഗ്ഷൻ, എല്ലാ ഇഷ്‌ടാനുസൃത ക്രമപ്പെടുത്തലുകളും നീക്കം ചെയ്യും. നിങ്ങൾക്ക് തുടരണമെന്നുണ്ടോ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"എല്ലാ ആപ്പുകളും"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"മീഡിയ ആപ്പുകൾ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ആപ്പിന്റെ പ്രവർത്തനം നിർത്തുക"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ആപ്പ് വിവരങ്ങൾ"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിന്റെ പ്രവർത്തനം നിർത്തി."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ആപ്പിന്റെ പ്രവർത്തനം നിർത്തണോ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ഒരു ആപ്പിന്റെ പ്രവർത്തനം നിർബന്ധിതമായി നിർത്തിയാൽ, അത് ശരിയായി പ്രവർത്തിക്കാനിടയില്ല."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ആപ്പ് നിർത്താനാകില്ല."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ഡ്രൈവിംഗിനിടെ <xliff:g id="APP_NAME">%1$s</xliff:g> ഉപയോഗിക്കാനാകില്ല."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ഡീബഗ് ആപ്പുകൾ മറയ്ക്കുക"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ഡീബഗ് ആപ്പുകൾ കാണിക്കുക"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-mn/strings.xml b/libs/appgrid/lib/res/values-mn/strings.xml
index 476d009..6fe8f51 100644
--- a/libs/appgrid/lib/res/values-mn/strings.xml
+++ b/libs/appgrid/lib/res/values-mn/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Энэ функц нь бүх захиалгат дарааллыг хасна. Та үргэлжлүүлэхийг хүсэж байна уу?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Бүх апп"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Медиа аппууд"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Аппыг зогсоох"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Аппын мэдээлэл"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г зогсоосон."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Аппыг зогсоох уу?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Хэрэв та аппыг хүчээр зогсоовол энэ нь буруу ажиллаж магадгүй."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Аппыг зогсоох боломжгүй."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г жолоо барьж байх үед ашиглах боломжгүй."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Дебаг хийх аппуудыг нуух"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Дебаг хийх аппуудыг харуулах"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-mr/strings.xml b/libs/appgrid/lib/res/values-mr/strings.xml
index c4b830f..f28ff673 100644
--- a/libs/appgrid/lib/res/values-mr/strings.xml
+++ b/libs/appgrid/lib/res/values-mr/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"हे फंक्शन सर्व कस्टम ऑर्डरिंग काढून टाकेल. तुम्हाला पुढे सुरू ठेवायचे आहे का?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"सर्व अ‍ॅप्स"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"मीडिया अ‍ॅप्स"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"अ‍ॅप थांबवा"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ॲप माहिती"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> थांबवले आहे."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"अ‍ॅप थांबवायचे आहे का?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"तुम्ही अ‍ॅप सक्तीने थांबवल्यास, ते गैरवर्तन करू शकते."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"अ‍ॅप थांबवू शकत नाही."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ड्राइव्ह करताना <xliff:g id="APP_NAME">%1$s</xliff:g> वापरता येऊ शकत नाही."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"डीबग केलेली ॲप्स लपवा"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"डीबग केलेली ॲप्स दाखवा"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ms/strings.xml b/libs/appgrid/lib/res/values-ms/strings.xml
index 161c68d..606187f 100644
--- a/libs/appgrid/lib/res/values-ms/strings.xml
+++ b/libs/appgrid/lib/res/values-ms/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Fungsi ini akan mengalih keluar semua susunan tersuai. Adakah anda mahu meneruskan tindakan?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Semua apl"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Apl media"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Hentikan apl"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Maklumat apl"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah dihentikan."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Hentikan apl?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Jika anda menghentikan apl secara paksa, fungsi apl mungkin terganggu."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Apl tidak dapat dihentikan."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak boleh digunakan semasa memandu."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Sembunyikan apl nyahpepijat"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Tunjukkan apl nyahpepijat"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;end"</string>
diff --git a/libs/appgrid/lib/res/values-my/strings.xml b/libs/appgrid/lib/res/values-my/strings.xml
index 3bdcac7..9868099 100644
--- a/libs/appgrid/lib/res/values-my/strings.xml
+++ b/libs/appgrid/lib/res/values-my/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ဤလုပ်ဆောင်ချက်သည် စိတ်ကြိုက်စီစဉ်မှုအားလုံးကို ဖယ်ရှားမည်။ ရှေ့ဆက်လိုပါသလား။"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"အက်ပ်အားလုံး"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"မီဒီယာ အက်ပ်များ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"အက်ပ် ရပ်ရန်"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"အက်ပ်အချက်အလက်"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ရပ်သွားသည်။"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"အက်ပ်ကို ရပ်မလား။"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"အက်ပ်ကို မဖြစ်မနေ ရပ်ခိုင်းလျှင် အမှားဖြစ်နိုင်သည်။"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"အက်ပ်ကို ရပ်၍မရပါ။"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ကားမောင်းနေစဉ် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို သုံး၍မရပါ။"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"အမှားရှာပြင်သည့်အက်ပ်များ ဖျောက်ထားရန်"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"အမှားရှာပြင်သည့်အက်ပ်များ ပြပါ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-nb/strings.xml b/libs/appgrid/lib/res/values-nb/strings.xml
index 65159b6..ce3165e 100644
--- a/libs/appgrid/lib/res/values-nb/strings.xml
+++ b/libs/appgrid/lib/res/values-nb/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Denne funksjonen fjerner alle tilpassede rekkefølger. Vil du fortsette?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alle apper"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medieapper"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Stopp appen"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Appinformasjon"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> er stoppet."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Vil du stoppe appen?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Hvis du tvinger en app til å stoppe, kan det oppstå problemer."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Appen kan ikke stoppes."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Du kan ikke bruke <xliff:g id="APP_NAME">%1$s</xliff:g> mens du kjører."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Skjul feilsøkingsapper"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Vis feilsøkingsapper"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ne/strings.xml b/libs/appgrid/lib/res/values-ne/strings.xml
index 7aae9b0..0d2ffb6 100644
--- a/libs/appgrid/lib/res/values-ne/strings.xml
+++ b/libs/appgrid/lib/res/values-ne/strings.xml
@@ -21,15 +21,9 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"यो फङ्सनले रोजाइअनुसार सेट गरिएका सबै क्रम हटाउने छ। तपाईं जारी राख्न चाहनुहुन्छ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"सबै एप"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"मिडिया एपहरू"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"एप बन्द गर्नुहोस्"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"एपसम्बन्धी जानकारी"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> बन्द गरिएको छ।"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"एप बन्द गर्ने हो?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"तपाईंले कुनै एपलाई जबरजस्ती रोक्नुभयो भने त्यसले सही तरिकाले काम नगर्न सक्छ।"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"एप बन्द गर्न सकिँदैन।"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ड्राइभ गर्दा <xliff:g id="APP_NAME">%1$s</xliff:g> प्रयोग गर्न सकिँदैन।"</string>
-    <string name="hide_debug_apps" msgid="7140064693464751647">"डिबग एपहरू लुकाइयोस्"</string>
-    <string name="show_debug_apps" msgid="2748157232151197494">"डिबग एपहरू देखाइयोस्"</string>
+    <string name="hide_debug_apps" msgid="7140064693464751647">"डिबग एपहरू लुकाउनुहोस्"</string>
+    <string name="show_debug_apps" msgid="2748157232151197494">"डिबग एपहरू देखाउनुहोस्"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
     <string name="banner_title_text" msgid="8827498256184464356">"तपाईं प्रयोगकर्ताले सहमति जनाउनु पर्ने सेवाका सर्तहरूका कारण निष्क्रिय पारिएका एपहरू प्रयोग गर्न चाहनुहुन्छ भने प्रयोगकर्ताले सहमति जनाउनु पर्ने सेवाका सर्तहरूमा सहमति जनाउनुहोस्"</string>
     <string name="banner_review_button_text" msgid="369410598918950148">"समीक्षा गर्नुहोस्"</string>
diff --git a/libs/appgrid/lib/res/values-nl/strings.xml b/libs/appgrid/lib/res/values-nl/strings.xml
index 3a2af95..f249441 100644
--- a/libs/appgrid/lib/res/values-nl/strings.xml
+++ b/libs/appgrid/lib/res/values-nl/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Met deze functie wordt de aangepaste volgorde helemaal verwijderd. Wil je doorgaan?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alle apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media-apps"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"App stoppen"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"App-informatie"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gestopt."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"App stoppen?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Als je een app gedwongen stopt, kan deze onverwacht gedrag vertonen."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"De app kan niet worden gestopt."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Je kunt <xliff:g id="APP_NAME">%1$s</xliff:g> niet gebruiken tijdens het rijden"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Foutopsporingsapps verbergen"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Foutopsporingsapps tonen"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-or/strings.xml b/libs/appgrid/lib/res/values-or/strings.xml
index eda5a9a..326df07 100644
--- a/libs/appgrid/lib/res/values-or/strings.xml
+++ b/libs/appgrid/lib/res/values-or/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ଏହି ଫଙ୍କସନ ସମସ୍ତ କଷ୍ଟମ ଅର୍ଡରିଂକୁ କାଢ଼ି ଦେବ। ଆପଣ ଜାରି ରଖିବାକୁ ଚାହାଁନ୍ତି?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ସମସ୍ତ ଆପ୍ସ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"ମିଡିଆ ଆପ୍ସ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ଆପକୁ ବନ୍ଦ କରନ୍ତୁ"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ଆପ ସୂଚନା"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ଆପକୁ ବନ୍ଦ କରିବେ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ଯଦି ଆପଣ ଏକ ଆପକୁ ବାଧ୍ୟତାର ସହ ବନ୍ଦ କରନ୍ତି, ତେବେ ଏହା ଠିକ୍ ଭାବେ କାମ ନକରିପାରେ।"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ଆପ ବନ୍ଦ କରାଯାଇପାରିବ ନାହିଁ।"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ଡ୍ରାଇଭ୍ କରିବା ସମୟରେ <xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ।"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ଡିବଗ ଆପ୍ସ ଲୁଚାନ୍ତୁ"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ଡିବଗ ଆପ୍ସ ଦେଖାନ୍ତୁ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-pa/strings.xml b/libs/appgrid/lib/res/values-pa/strings.xml
index b78886a..5eded97 100644
--- a/libs/appgrid/lib/res/values-pa/strings.xml
+++ b/libs/appgrid/lib/res/values-pa/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ਇਸ ਫੰਕਸ਼ਨ ਨਾਲ ਸਾਰਾ ਵਿਉਂਤਿਆ ਕ੍ਰਮ ਹਟ ਜਾਵੇਗਾ। ਕੀ ਤੁਸੀਂ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"ਸਾਰੀਆਂ ਐਪਾਂ"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"ਮੀਡੀਆ ਐਪਾਂ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ਐਪ ਬੰਦ ਕਰੋ"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ਐਪ ਜਾਣਕਾਰੀ"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਗਿਆ ਹੈ।"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ਕੀ ਐਪ ਨੂੰ ਬੰਦ ਕਰਨਾ ਹੈ?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ਜੇ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਕਰਦੇ ਹੋ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਇਹ ਠੀਕ ਤਰ੍ਹਾਂ ਕੰਮ ਨਾ ਕਰੇ।"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ਐਪ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ਗੱਡੀ ਚਲਾਉਣ ਵੇਲੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਵਰਤਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ਡੀਬੱਗ ਐਪਾਂ ਲੁਕਾਓ"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ਡੀਬੱਗ ਐਪਾਂ ਦਿਖਾਓ"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-pl/strings.xml b/libs/appgrid/lib/res/values-pl/strings.xml
index 1e8e257..db03284 100644
--- a/libs/appgrid/lib/res/values-pl/strings.xml
+++ b/libs/appgrid/lib/res/values-pl/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ta funkcja usuwa wszystkie niestandardowe ustawienia kolejności. Czy chcesz kontynuować?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Wszystkie aplikacje"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplikacje multimedialne"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zatrzymaj aplikację"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informacje o aplikacji"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> została zatrzymana."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Zatrzymać aplikację?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Jeśli wymusisz zatrzymanie aplikacji, może ona zadziałać nieprawidłowo."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikacji nie można zatrzymać."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Podczas jazdy nie można korzystać z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ukryj aplikacje do debugowania"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Pokaż aplikacje do debugowania"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-pt-rPT/strings.xml b/libs/appgrid/lib/res/values-pt-rPT/strings.xml
index f310ce9..db6dadc 100644
--- a/libs/appgrid/lib/res/values-pt-rPT/strings.xml
+++ b/libs/appgrid/lib/res/values-pt-rPT/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Esta função vai remover toda a ordenação personalizada. Quer continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Todas as apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Apps de multimédia"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Parar app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informações da app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi parada."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Parar a app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Se forçar a paragem de uma app, esta pode apresentar um comportamento anormal."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Não é possível parar a app."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Não é possível usar a app <xliff:g id="APP_NAME">%1$s</xliff:g> durante a condução."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ocultar apps de depuração"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostrar apps de depuração"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-pt/strings.xml b/libs/appgrid/lib/res/values-pt/strings.xml
index 052dc2e..5cd5927 100644
--- a/libs/appgrid/lib/res/values-pt/strings.xml
+++ b/libs/appgrid/lib/res/values-pt/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Esta função vai remover a ordem personalizada. Você quer continuar?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Todos os apps"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Apps de música"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Parar o app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informações do app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> foi parado."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Parar o app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Se você forçar o fechamento de um app, ele pode apresentar mau funcionamento."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Não é possível interromper o app."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não pode ser usado ao dirigir."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ocultar apps de depuração"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Mostrar apps de depuração"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ro/strings.xml b/libs/appgrid/lib/res/values-ro/strings.xml
index 0d9aa1b..c3f2e44 100644
--- a/libs/appgrid/lib/res/values-ro/strings.xml
+++ b/libs/appgrid/lib/res/values-ro/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Această funcție va elimina orice ordonare personalizată. Continui?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Toate aplicațiile"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplicații media"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Oprește aplicația"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informații despre aplicație"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost oprită."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Oprești aplicația?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Dacă forțezi oprirea unei aplicații, aceasta se poate comporta necorespunzător."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplicația nu poate fi oprită."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Nu poți folosi <xliff:g id="APP_NAME">%1$s</xliff:g> în timp ce conduci."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ascunde aplicațiile de remediere a erorilor"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Afișează aplicațiile de remediere a erorilor"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ru/strings.xml b/libs/appgrid/lib/res/values-ru/strings.xml
index fdcee77..18282f5 100644
--- a/libs/appgrid/lib/res/values-ru/strings.xml
+++ b/libs/appgrid/lib/res/values-ru/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Текущий порядок приложений будет изменен на алфавитный. Продолжить?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Все приложения"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Мультимедийные приложения"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Остановить"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Сведения о приложении"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" остановлено."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Остановить приложение?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Принудительное закрытие приложения может отразиться на его функциональности."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Невозможно остановить приложение."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" нельзя использовать при вождении."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Скрыть приложения для отладки"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Показать приложения для отладки"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-si/strings.xml b/libs/appgrid/lib/res/values-si/strings.xml
index 20051df..bc281c7 100644
--- a/libs/appgrid/lib/res/values-si/strings.xml
+++ b/libs/appgrid/lib/res/values-si/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"මෙම කාර්යය සියලු අභිරුචි අනුපිළිවෙලක් ඉවත් කරනු ඇත. ඔබට ඉදිරියට යාමට අවශ්‍යද?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"සියලු යෙදුම්"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"මාධ්‍ය යෙදුම්"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"යෙදුම නවත්වන්න"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"යෙදුම් තතු"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> නතර කර ඇත."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"යෙදුම නවත්වන්න ද?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ඔබ යෙදුමක් බලෙන් නැවත වුවහොත්, එය වැරදි ලෙස ක්‍රියා කරනු ඇත."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"යෙදුම නැවැත්විය නොහැක."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> හට රිය පදවන අතරේ යතුරු පුවරුව භාවිතා කළ නොහැක."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"නිදොස් කිරීමේ යෙදුම් සඟවන්න"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"නිදොස් කිරීමේ යෙදුම් පෙන්වන්න"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sk/strings.xml b/libs/appgrid/lib/res/values-sk/strings.xml
index 6438114..4c03800 100644
--- a/libs/appgrid/lib/res/values-sk/strings.xml
+++ b/libs/appgrid/lib/res/values-sk/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Táto funkcia zruší všetko vlastné poradie. Chcete pokračovať?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Všetky aplikácie"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Prehrávače"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zastaviť aplikáciu"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informácie o aplikácii"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> bola zastavená."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Chcete zastaviť aplikáciu?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ak vynútite zastavenie aplikácie, môže sa správať zvláštne."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikáciu nie je možné ukončiť."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Pri šoférovaní nie je možné používať aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Skryť aplikácie na ladenie"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Zobraziť aplikácie na ladenie"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sl/strings.xml b/libs/appgrid/lib/res/values-sl/strings.xml
index ea80a02..2ecf767 100644
--- a/libs/appgrid/lib/res/values-sl/strings.xml
+++ b/libs/appgrid/lib/res/values-sl/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ta funkcija bo odstranila vse razvrščanje po meri. Ali želite nadaljevati?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Vse aplikacije"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplikacije za predstavnost"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Ustavi aplikacijo"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Podatki o aplikacijah"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je ustavljena."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Želite ustaviti aplikacijo?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Če boste vsilili zaustavitev aplikacije, morda ne bo pravilno delovala."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikacije ni mogoče ustaviti."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Med vožnjo ni mogoče uporabljati aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Skrij aplikacije za odpravljanje napak"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Prikaži aplikacije za odpravljanje napak"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sq/strings.xml b/libs/appgrid/lib/res/values-sq/strings.xml
index 263be17..0cf2bec 100644
--- a/libs/appgrid/lib/res/values-sq/strings.xml
+++ b/libs/appgrid/lib/res/values-sq/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ky funksion do të heqë të gjitha renditjet e personalizuara. Dëshiron të vazhdosh?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Të gjitha aplikacionet"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Aplikacionet e medias"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Ndalo aplikacionin"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Informacione mbi aplikacionin"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> është ndaluar."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Të ndalohet aplikacioni?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Nëse e ndalon me forcë një aplikacion, ai mund të ketë çrregullime në funksionim."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Aplikacioni nuk mund të ndalohet."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"\"<xliff:g id="APP_NAME">%1$s</xliff:g>\" nuk mund të përdoret gjatë drejtimit të makinës."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Fshih aplikacionet e korrigjimit të defekteve"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Shfaq aplikacionet e korrigjimit të defekteve"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sr/strings.xml b/libs/appgrid/lib/res/values-sr/strings.xml
index 0f692f2..3d51943 100644
--- a/libs/appgrid/lib/res/values-sr/strings.xml
+++ b/libs/appgrid/lib/res/values-sr/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Ова функција ће уклонити целокупан прилагођен распоред. Желите да наставите?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Све апликације"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Медијске апликације"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Заустави апликацију"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Информације о апликацији"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је заустављена."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Желите да зауставите апликацију?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ако принудно зауставите апликацију, можда ће се понашати неочекивано."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Апликација не може да се заустави."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се користи током вожње."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Сакриј апликације за отклањање грешака"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Прикажи апликације за отклањање грешака"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sv/strings.xml b/libs/appgrid/lib/res/values-sv/strings.xml
index 0ef43bc..016f3ce 100644
--- a/libs/appgrid/lib/res/values-sv/strings.xml
+++ b/libs/appgrid/lib/res/values-sv/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Funktionen tar bort alla anpassade ordningar. Vill du fortsätta?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Alla appar"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medieappar"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Avsluta appen"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Appinformation"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> har avslutats."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Vill du avsluta appen?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Om du tvingar appen att avsluta kanske den inte fungerar som den ska."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Det går inte att avsluta appen."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g> går inte att använda under körning."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Dölj felsökningsappar"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Visa felsökningsappar"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-sw/strings.xml b/libs/appgrid/lib/res/values-sw/strings.xml
index 66fb8d3..9650d1d 100644
--- a/libs/appgrid/lib/res/values-sw/strings.xml
+++ b/libs/appgrid/lib/res/values-sw/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Utendaji huu utaondoa mipangilio yote maalum ya upangaji. Je, ungependa kuendelea?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Programu zote"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Programu za muziki"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Zima programu"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Maelezo ya programu"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Umezima programu ya <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Je, ungependa kuzima programu?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Huenda programu isifanye kazi ipasavyo, iwapo utalazimisha kuizima."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Programu haiwezi kuzimwa."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Huwezi kutumia <xliff:g id="APP_NAME">%1$s</xliff:g> wakati unaendesha gari."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ficha programu za kutatua hitilafu"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Onyesha programu za kutatua hitilafu"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ta/strings.xml b/libs/appgrid/lib/res/values-ta/strings.xml
index e7a6698..8374a00 100644
--- a/libs/appgrid/lib/res/values-ta/strings.xml
+++ b/libs/appgrid/lib/res/values-ta/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"இது பிரத்தியேக வரிசைகள் அனைத்தையும் அகற்றும். தொடர விரும்புகிறீர்களா?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"அனைத்து ஆப்ஸும்"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"மீடியா ஆப்ஸ்"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ஆப்ஸை நிறுத்து"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ஆப்ஸ் தகவல்கள்"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் நிறுத்தப்பட்டது."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ஆப்ஸை நிறுத்தவா?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"ஆப்ஸை உடனே நிறுத்தினால் அது சரியாகச் செயல்படாமல் போகக்கூடும்."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ஆப்ஸை நிறுத்த முடியாது."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"காரை ஓட்டும்போது <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸைப் பயன்படுத்த முடியாது."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"பிழைதிருத்தும் ஆப்ஸை மறை"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"பிழைதிருத்தும் ஆப்ஸைக் காட்டு"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-te/strings.xml b/libs/appgrid/lib/res/values-te/strings.xml
index a7f46d2..346ca68 100644
--- a/libs/appgrid/lib/res/values-te/strings.xml
+++ b/libs/appgrid/lib/res/values-te/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ఈ ఫంక్షన్ అన్ని అనుకూల ఆర్డరింగ్‌ను తీసివేస్తుంది. మీరు కొనసాగాలనుకుంటున్నారా?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"అన్ని యాప్‌లు"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"మీడియా యాప్‌లు"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"యాప్‌ను ఆపివేయండి"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"యాప్ సమాచారం"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఆపివేయబడింది."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"యాప్‌ను ఆపివేయాలా?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"మీరు యాప్‌ను ఫోర్స్ ఆపివేస్తే, అది సరిగ్గా పని చేయకపోవచ్చు."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"యాప్‌ను ఆపడం సాధ్యం కాదు."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"డ్రైవింగ్ చేస్తున్నపుడు <xliff:g id="APP_NAME">%1$s</xliff:g>ను ఉపయోగించలేరు."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"డీబగ్ యాప్‌లను దాచండి"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"డీబగ్ యాప్‌లను చూపించండి"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-th/strings.xml b/libs/appgrid/lib/res/values-th/strings.xml
index 5a30b02..98dcdd9 100644
--- a/libs/appgrid/lib/res/values-th/strings.xml
+++ b/libs/appgrid/lib/res/values-th/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"ฟังก์ชันนี้จะนำการจัดลำดับที่กำหนดเองออกทั้งหมด ต้องการทำต่อไหม"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"แอปทั้งหมด"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"แอปสื่อ"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"หยุดแอป"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ข้อมูลแอป"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"หยุด <xliff:g id="APP_NAME">%1$s</xliff:g> แล้ว"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"หยุดแอปไหม"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"แอปอาจทำงานผิดพลาดหากบังคับให้หยุด"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"หยุดแอปไม่ได้"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ขณะขับรถไม่ได้"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ซ่อนแอปแก้ไขข้อบกพร่อง"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"แสดงแอปแก้ไขข้อบกพร่อง"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-tl/strings.xml b/libs/appgrid/lib/res/values-tl/strings.xml
index c23a923..623a521 100644
--- a/libs/appgrid/lib/res/values-tl/strings.xml
+++ b/libs/appgrid/lib/res/values-tl/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Aalisin ng function na ito ang lahat ng custom na pagkakasunod-sunod. Gusto mo bang magpatuloy?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Lahat ng app"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Mga media app"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Ihinto ang app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Impormasyon ng app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Inihinto ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Ihinto ang app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Kung sapilitan mong ihihinto ang isang app, posible itong gumana nang maayos."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Hindi puwedeng ihinto ang app."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Hindi magagamit ang <xliff:g id="APP_NAME">%1$s</xliff:g> habang nagmamaneho."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"I-hide ang mga debug app"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Ipakita ang mga debug app"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-tr/strings.xml b/libs/appgrid/lib/res/values-tr/strings.xml
index a8eae95..8a96385 100644
--- a/libs/appgrid/lib/res/values-tr/strings.xml
+++ b/libs/appgrid/lib/res/values-tr/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Bu işlev tüm özel sıralamayı kaldıracaktır. Devam etmek istiyor musunuz?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Tüm uygulamalar"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Medya uygulamaları"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Uygulamayı durdur"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Uygulama bilgisi"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> durduruldu."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Uygulama durdurulsun mu?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Uygulamayı zorla durdurursanız hatalı davranabilir."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Uygulama durdurulamıyor."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"<xliff:g id="APP_NAME">%1$s</xliff:g>, sürüş sırasında kullanılamaz."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Hata ayıklama uygulamalarını gizle"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Hata ayıklama uygulamalarını göster"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-uk/strings.xml b/libs/appgrid/lib/res/values-uk/strings.xml
index e35e55d..dec8afb 100644
--- a/libs/appgrid/lib/res/values-uk/strings.xml
+++ b/libs/appgrid/lib/res/values-uk/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Якщо застосувати цю функцію, визначений користувачем порядок розташування додатків зміниться. Продовжити?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Усі додатки"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Мультимедійні додатки"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Припинити роботу додатка"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Інформація про додаток"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"Роботу додатка <xliff:g id="APP_NAME">%1$s</xliff:g> припинено."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Припинити роботу додатка?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Примусове вимкнення додатка може призвести до збою в його роботі."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Не вдалося припинити роботу додатка."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Додатком <xliff:g id="APP_NAME">%1$s</xliff:g> не можна користуватися під час руху."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Сховати додатки для налагодження"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Показати додатки для налагодження"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-ur/strings.xml b/libs/appgrid/lib/res/values-ur/strings.xml
index 34191e2..3b65bc7 100644
--- a/libs/appgrid/lib/res/values-ur/strings.xml
+++ b/libs/appgrid/lib/res/values-ur/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"یہ فنکشن تمام حسب ضرورت ترتیب کو ہٹا دے گا۔ کیا آپ جاری رکھنا چاہتے ہیں؟"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"سبھی ایپس"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"میڈیا ایپس"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"ایپ بند کریں"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"ایپ کی معلومات"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو بند کر دیا گیا ہے۔"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"ایپ بند کریں؟"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"اگر آپ کسی ایپ کو زبردستی بند کر دیتے ہیں تو یہ غلط برتاؤ کر سکتی ہے۔"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"ایپ کو بند نہیں کیا جا سکتا۔"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"ڈرائیو کرتے وقت <xliff:g id="APP_NAME">%1$s</xliff:g> کا استعمال نہیں کیا جا سکتا۔"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"ڈیبگ ایپس چھپائیں"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"ڈیبگ ایپس دکھائیں"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-uz/strings.xml b/libs/appgrid/lib/res/values-uz/strings.xml
index 6441fce..26cbf5c 100644
--- a/libs/appgrid/lib/res/values-uz/strings.xml
+++ b/libs/appgrid/lib/res/values-uz/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Bu funksiya barcha maxsus buyurtmalarni bekor qiladi. Davom etasizmi?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Barcha ilovalar"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Media ilovalari"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Ilovani toʻxtatish"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Ilova haqida"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi toʻxtatildi."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Ilova toʻxtatilsinmi?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Ilovani majburan toʻxtatish uning ishlashiga taʼsir koʻrsatishi mumkin."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Ilovani toʻxtatish imkonsiz."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Avtomobil rejimida <xliff:g id="APP_NAME">%1$s</xliff:g> ishlamaydi."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Nosozliklarni aniqlash ilovalarini berkitish"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Nosozliklarni aniqlash ilovalarini chiqarish"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-vi/strings.xml b/libs/appgrid/lib/res/values-vi/strings.xml
index fee8689..0c0f70f 100644
--- a/libs/appgrid/lib/res/values-vi/strings.xml
+++ b/libs/appgrid/lib/res/values-vi/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Chức năng này sẽ xoá mọi thứ tự tuỳ chỉnh. Bạn có muốn tiếp tục không?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Tất cả ứng dụng"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Ứng dụng đa phương tiện"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Dừng ứng dụng"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Thông tin ứng dụng"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã bị dừng."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Dừng ứng dụng?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Nếu bạn buộc một ứng dụng dừng lại, ứng dụng đó có thể hoạt động không đúng cách."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"Không dừng được ứng dụng."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"Không thể dùng <xliff:g id="APP_NAME">%1$s</xliff:g> trong khi lái xe."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Ẩn các ứng dụng gỡ lỗi"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Hiện các ứng dụng gỡ lỗi"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-zh-rCN/strings.xml b/libs/appgrid/lib/res/values-zh-rCN/strings.xml
index 6755a3b..5b40485 100644
--- a/libs/appgrid/lib/res/values-zh-rCN/strings.xml
+++ b/libs/appgrid/lib/res/values-zh-rCN/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"此功能会撤消所有自定义排序。要继续吗?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"所有应用"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"媒体应用"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"停止应用"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"应用信息"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"已停止“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"要停止应用吗?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"强行停止某个应用可能会导致其出现异常。"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"无法停止应用。"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"驾车时无法使用“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"隐藏调试应用"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"显示调试应用"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-zh-rHK/strings.xml b/libs/appgrid/lib/res/values-zh-rHK/strings.xml
index 2583635..2b632b9 100644
--- a/libs/appgrid/lib/res/values-zh-rHK/strings.xml
+++ b/libs/appgrid/lib/res/values-zh-rHK/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"此功能會移除所有自訂順序。你要繼續嗎?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"所有應用程式"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"媒體應用程式"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"停止應用程式"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"應用程式資料"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"已停止「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"要停止應用程式嗎?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"強制停止應用程式,可能會導致操作不正常。"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"無法停止應用程式。"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"駕駛時無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"隱藏偵錯應用程式"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"顯示偵錯應用程式"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-zh-rTW/strings.xml b/libs/appgrid/lib/res/values-zh-rTW/strings.xml
index baeb589..2eb6003 100644
--- a/libs/appgrid/lib/res/values-zh-rTW/strings.xml
+++ b/libs/appgrid/lib/res/values-zh-rTW/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"這項功能會移除所有自訂排序,要繼續嗎?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"所有應用程式"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"媒體應用程式"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"停止應用程式"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"應用程式資訊"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"已停止「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"要停止應用程式嗎?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"如果你強制停止應用程式,應用程式可能會出現異常行為。"</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"無法停止應用程式。"</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"開車時無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"隱藏偵錯應用程式"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"顯示偵錯應用程式"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values-zu/strings.xml b/libs/appgrid/lib/res/values-zu/strings.xml
index 89cc9e9..f861098 100644
--- a/libs/appgrid/lib/res/values-zu/strings.xml
+++ b/libs/appgrid/lib/res/values-zu/strings.xml
@@ -21,13 +21,7 @@
     <string name="reset_appgrid_dialogue_message" msgid="2278301828239327586">"Lo msebenzi uzosusa konke uku-oda ngokwezifiso. Ingabe ufuna ukuqhubeka?"</string>
     <string name="app_launcher_title_all_apps" msgid="3522783138519460233">"Wonke ama-app"</string>
     <string name="app_launcher_title_media_only" msgid="7194631822174015710">"Ama-app emidiya"</string>
-    <string name="app_launcher_stop_app_action" msgid="4488562150865461282">"Misa i-app"</string>
-    <string name="app_launcher_app_info_action" msgid="475788297140730900">"Ulwazi lwe-app"</string>
-    <string name="app_launcher_stop_app_success_toast_text" msgid="1504575154930117738">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> imisiwe."</string>
-    <string name="app_launcher_stop_app_dialog_title" msgid="339411511647776450">"Misa i-app?"</string>
-    <string name="app_launcher_stop_app_dialog_text" msgid="5113650734637193870">"Uma uphoqelela ukumisa i-app, kungenzeka ukuthi ingasebenzi kahle."</string>
     <string name="app_launcher_stop_app_cant_stop_text" msgid="6513703446595313338">"I-App ayikwazi ukumiswa."</string>
-    <string name="driving_toast_text" msgid="397905281933065053">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazi ukusetshenziswa ngenkathi ushayela."</string>
     <string name="hide_debug_apps" msgid="7140064693464751647">"Fihla ama-app okususa iphutha"</string>
     <string name="show_debug_apps" msgid="2748157232151197494">"Bonisa ama-app okususa iphutha"</string>
     <string name="user_tos_activity_intent" msgid="5323981034042569291">"intent:#Intent;action=com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=false;end"</string>
diff --git a/libs/appgrid/lib/res/values/colors.xml b/libs/appgrid/lib/res/values/colors.xml
index 580f625..a62fd85 100644
--- a/libs/appgrid/lib/res/values/colors.xml
+++ b/libs/appgrid/lib/res/values/colors.xml
@@ -17,7 +17,6 @@
 <resources>
     <color name="icon_tint">#FFF8F9FA</color>
     <color name="recent_apps_line_divider_color">#1FFFFFFF</color>
-    <color name="shortcuts_icon_color">#FFFFFFFF</color>
     <color name="page_indicator_bar_color">#FF66B5FF</color>
     <color name="app_item_on_hover_border_color">#FF66B5FF</color>
     <color name="app_item_on_hover_background_color">#3D66B5FF</color>
diff --git a/libs/appgrid/lib/res/values/config.xml b/libs/appgrid/lib/res/values/config.xml
index 5d214fc..34497aa 100644
--- a/libs/appgrid/lib/res/values/config.xml
+++ b/libs/appgrid/lib/res/values/config.xml
@@ -39,4 +39,17 @@
         msg_mirroring_redirect_uri_key
     </string>
 
+    <!--- The number of days to wait before resurfacing the terms of service banner
+         on the app grid after being dismissed
+
+         * Integer value 0 implies to show the banner after every reboot
+   -->
+    <integer name="config_tos_banner_resurface_time_days">1</integer>
+
+    <!--
+        Config for allowing NDO apps to be opened while driving if they contain an active media
+        session. These NDO apps will still be blocked by blocking UI, but may be provided controls.
+        This does not affect the media widget's ability to show or control media sessions.
+    -->
+    <bool name="config_enableMediaSessionAppsWhileDriving">true</bool>
 </resources>
diff --git a/libs/appgrid/lib/res/values/overlayable.xml b/libs/appgrid/lib/res/values/overlayable.xml
index 3e29924..90f36e6 100644
--- a/libs/appgrid/lib/res/values/overlayable.xml
+++ b/libs/appgrid/lib/res/values/overlayable.xml
@@ -1,5 +1,5 @@
 <?xml version='1.0' encoding='UTF-8'?>
-<!-- Copyright (C) 2023 The Android Open Source Project
+<!-- Copyright (C) 2024 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
@@ -23,6 +23,7 @@
       <item type="bool" name="car_app_show_recent_apps"/>
       <item type="bool" name="car_app_show_toolbar"/>
       <item type="bool" name="config_allow_reordering"/>
+      <item type="bool" name="config_enableMediaSessionAppsWhileDriving"/>
       <item type="bool" name="use_defined_app_grid_dimensions"/>
       <item type="bool" name="use_vertical_app_grid"/>
       <item type="color" name="app_item_on_hover_background_color"/>
@@ -34,7 +35,6 @@
       <item type="color" name="icon_tint"/>
       <item type="color" name="page_indicator_bar_color"/>
       <item type="color" name="recent_apps_line_divider_color"/>
-      <item type="color" name="shortcuts_icon_color"/>
       <item type="dimen" name="app_bar_height"/>
       <item type="dimen" name="app_grid_header_margin"/>
       <item type="dimen" name="app_grid_height"/>
@@ -75,29 +75,30 @@
       <item type="dimen" name="threshold_to_start_drag_drop"/>
       <item type="drawable" name="app_item_highlight"/>
       <item type="drawable" name="banner_warning_image"/>
-      <item type="drawable" name="ic_app_info"/>
-      <item type="drawable" name="ic_force_stop_caution_icon"/>
       <item type="drawable" name="page_indicator_bar"/>
       <item type="id" name="app_icon"/>
       <item type="id" name="app_item"/>
       <item type="id" name="app_name"/>
       <item type="id" name="apps_grid"/>
       <item type="id" name="apps_grid_background"/>
+      <item type="id" name="apps_grid_background_container"/>
+      <item type="id" name="banner_first_button"/>
+      <item type="id" name="banner_icon"/>
+      <item type="id" name="banner_second_button"/>
       <item type="id" name="banner_title"/>
       <item type="id" name="divider"/>
-      <item type="id" name="first_button"/>
       <item type="id" name="focus_area"/>
-      <item type="id" name="icon"/>
+      <item type="id" name="fragmentContainer"/>
       <item type="id" name="page_indicator"/>
       <item type="id" name="page_indicator_container"/>
       <item type="id" name="recent_apps_row"/>
-      <item type="id" name="second_button"/>
       <item type="id" name="tos_banner"/>
       <item type="integer" name="car_app_selector_column_number"/>
       <item type="integer" name="car_app_selector_row_number"/>
       <item type="integer" name="config_msg_register_mirroring_pkg_code"/>
       <item type="integer" name="config_msg_send_mirroring_pkg_code"/>
       <item type="integer" name="config_msg_unregister_mirroring_pkg_code"/>
+      <item type="integer" name="config_tos_banner_resurface_time_days"/>
       <item type="integer" name="ms_background_highlight_duration"/>
       <item type="integer" name="ms_drop_animation_delay"/>
       <item type="integer" name="ms_long_press_animation_duration"/>
@@ -107,16 +108,12 @@
       <item type="integer" name="ms_scrollbar_appear_animation_duration"/>
       <item type="integer" name="ms_scrollbar_fade_animation_delay"/>
       <item type="integer" name="ms_scrollbar_fade_animation_duration"/>
-      <item type="layout" name="app_grid_activity"/>
+      <item type="layout" name="app_grid_container_activity"/>
+      <item type="layout" name="app_grid_fragment"/>
       <item type="layout" name="app_item"/>
       <item type="layout" name="banner"/>
       <item type="layout" name="recent_apps_row"/>
-      <item type="string" name="app_launcher_app_info_action"/>
-      <item type="string" name="app_launcher_stop_app_action"/>
       <item type="string" name="app_launcher_stop_app_cant_stop_text"/>
-      <item type="string" name="app_launcher_stop_app_dialog_text"/>
-      <item type="string" name="app_launcher_stop_app_dialog_title"/>
-      <item type="string" name="app_launcher_stop_app_success_toast_text"/>
       <item type="string" name="app_launcher_title_all_apps"/>
       <item type="string" name="app_launcher_title_media_only"/>
       <item type="string" name="banner_dismiss_button_text"/>
@@ -126,7 +123,6 @@
       <item type="string" name="config_msg_mirroring_redirect_uri_key"/>
       <item type="string" name="config_msg_mirroring_service_class_name"/>
       <item type="string" name="config_msg_mirroring_service_pkg_name"/>
-      <item type="string" name="driving_toast_text"/>
       <item type="string" name="hide_debug_apps"/>
       <item type="string" name="reset_appgrid_dialogue_message"/>
       <item type="string" name="reset_appgrid_title"/>
diff --git a/libs/appgrid/lib/res/values/strings.xml b/libs/appgrid/lib/res/values/strings.xml
index 5d1d0c6..55abe29 100644
--- a/libs/appgrid/lib/res/values/strings.xml
+++ b/libs/appgrid/lib/res/values/strings.xml
@@ -22,26 +22,9 @@
     </string>
     <string name="app_launcher_title_all_apps">All apps</string>
     <string name="app_launcher_title_media_only">Media apps</string>
-    <string name="app_launcher_stop_app_action">Stop app</string>
-    <string name="app_launcher_app_info_action">App info</string>
-    <string name="app_launcher_stop_app_success_toast_text">
-        <xliff:g id="app_name" example="Radio">%1$s</xliff:g>
-        has been stopped.
-    </string>
-    <!-- Manage applications, title for dialog when killing persistent apps. [CHAR LIMIT=40] -->
-    <string name="app_launcher_stop_app_dialog_title">Stop app?</string>
-    <!-- Manage applications, text for dialog when killing persistent apps. [CHAR LIMIT=200] -->
-    <string name="app_launcher_stop_app_dialog_text">If you force stop an app, it may
-        misbehave.
-    </string>
     <!-- Manage applications, text for dialog when apps can not be stopped -->
     <string name="app_launcher_stop_app_cant_stop_text">App can’t be stopped.</string>
 
-    <string name="driving_toast_text">
-        <xliff:g id="app_name" example="Settings">%1$s</xliff:g>
-        can\'t be used while driving.
-    </string>
-
     <!-- Toolbar MenuItem text for hiding debug apps,
         only visible on debug builds [CHAR_LIMIT=50] -->
     <string name="hide_debug_apps">Hide debug apps</string>
diff --git a/libs/appgrid/lib/robotests/Android.bp b/libs/appgrid/lib/robotests/Android.bp
new file mode 100644
index 0000000..42aa1c7
--- /dev/null
+++ b/libs/appgrid/lib/robotests/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2024 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
+}
+
+android_robolectric_test {
+
+    name: "CarAppGridUnitTests",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    java_resource_dirs: ["config"],
+
+    static_libs: [
+        "mockito-robolectric-prebuilt",
+        "mockito-kotlin2",
+        "androidx.test.rules",
+        "kotlinx_coroutines_test",
+        "androidx.test.core",
+        "android.car.testapi",
+        "android.car-system-stubs",
+    ],
+
+    test_suites: [
+        "automotive-general-tests",
+        "general-tests",
+    ],
+
+    test_options: {
+        timeout: 36000,
+    },
+
+    instrumentation_for: "CarAppGridTestApp",
+
+    strict_mode: false,
+}
diff --git a/libs/appgrid/lib/robotests/config/robolectric.properties b/libs/appgrid/lib/robotests/config/robolectric.properties
new file mode 100644
index 0000000..9b85e56
--- /dev/null
+++ b/libs/appgrid/lib/robotests/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# Copyright (C) 2024 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.
+#
+
+sdk=NEWEST_SDK
diff --git a/libs/appgrid/lib/robotests/res/values/config.xml b/libs/appgrid/lib/robotests/res/values/config.xml
new file mode 100644
index 0000000..2372d43
--- /dev/null
+++ b/libs/appgrid/lib/robotests/res/values/config.xml
@@ -0,0 +1,41 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<!--Robolectric test Resources-->
+<resources>
+
+    <!-- Service information to get screen mirroring information -->
+    <string name="config_msg_mirroring_service_pkg_name" translatable="false">
+        com.android.car.mirroring.test
+    </string>
+    <string name="config_msg_mirroring_service_class_name" translatable="false">
+        com.android.car.mirroring.test.ServiceClassName
+    </string>
+
+    <integer name="config_msg_register_mirroring_pkg_code">1</integer>
+    <integer name="config_msg_unregister_mirroring_pkg_code">2</integer>
+    <integer name="config_msg_send_mirroring_pkg_code">3</integer>
+    <string name="config_msg_mirroring_pkg_name_key" translatable="false">
+        msg_mirroring_pkg_name_key
+    </string>
+    <string name="config_msg_mirroring_redirect_uri_key" translatable="false">
+        msg_mirroring_redirect_uri_key
+    </string>
+
+    <!-- Launcher Apps to hide -->
+    <string-array name="hidden_apps" />
+
+</resources>
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/AppOrderProtoDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/AppOrderProtoDataSourceImplTest.kt
new file mode 100644
index 0000000..f45b10f
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/AppOrderProtoDataSourceImplTest.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import com.android.car.carlauncher.LauncherItemProto
+import com.android.car.carlauncher.LauncherItemProto.LauncherItemMessage
+import com.android.car.carlauncher.datasources.AppOrderDataSource.AppOrderInfo
+import com.android.car.carlauncher.datastore.launcheritem.LauncherItemListSource
+import junit.framework.TestCase.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class AppOrderProtoDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetSavedAppOrder_noSavedOrder_sendsEmptyList() = scope.runTest {
+        val launcherItemListSource: LauncherItemListSource = mock()
+        val appOrderDataSource = AppOrderProtoDataSourceImpl(launcherItemListSource, bgDispatcher)
+        val flows = mutableListOf<List<AppOrderInfo>>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrder().toList(flows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        verify(launcherItemListSource).readFromFile()
+        assertEquals(emptyList<AppOrderInfo>(), flows[0])
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetSavedAppOrder_hasSavedOrder_sendsUpdatedList() = scope.runTest {
+        val previouslySavedApp1 = LauncherItemMessage.newBuilder()
+            .setPackageName("PackageName1")
+            .setClassName("ClassName1")
+            .setDisplayName("DisplayName1")
+            .setRelativePosition(1)
+            .setContainerID(-1).build()
+        val previouslySavedApp2 = LauncherItemMessage.newBuilder()
+            .setPackageName("PackageName2")
+            .setClassName("ClassName2")
+            .setDisplayName("DisplayName2")
+            .setRelativePosition(2)
+            .setContainerID(-1).build()
+        val savedProtoAppOrder = listOf(previouslySavedApp1, previouslySavedApp2)
+        val expectedAppOrder =
+            savedProtoAppOrder.map {
+                AppOrderInfo(
+                    it.packageName,
+                    it.className,
+                    it.displayName
+                )
+            }
+        val launcherItemListSource: LauncherItemListSource = mock {
+            on { readFromFile() } doReturn
+                    LauncherItemProto.LauncherItemListMessage.newBuilder()
+                        .addAllLauncherItemMessage(savedProtoAppOrder).build()
+        }
+        val appOrderDataSource = AppOrderProtoDataSourceImpl(launcherItemListSource, bgDispatcher)
+        val flows = mutableListOf<List<AppOrderInfo>>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrder().toList(flows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        verify(launcherItemListSource).readFromFile()
+        assertEquals(expectedAppOrder, flows[0])
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testSavedAppOrder_savesPersistently_reportsChangeToCollectors() = scope.runTest {
+        val newAppInfo1 = AppOrderInfo("PackageName1", "ClassName1", "DisplayName1")
+        val newAppInfo2 = AppOrderInfo("PackageName2", "ClassName2", "DisplayName2")
+        val newSaveOrder = listOf(newAppInfo1, newAppInfo2)
+        val expectedProtoAppOrder = newSaveOrder.mapIndexed { index, it ->
+            LauncherItemMessage.newBuilder()
+                .setPackageName(it.packageName)
+                .setClassName(it.className)
+                .setDisplayName(it.displayName)
+                .setRelativePosition(index)
+                .setContainerID(-1).build()
+        }
+        val expectedAppOrderProtoMessage = LauncherItemProto.LauncherItemListMessage.newBuilder()
+            .addAllLauncherItemMessage(expectedProtoAppOrder).build()
+        val launcherItemListSource: LauncherItemListSource = mock()
+        val appOrderDataSource = AppOrderProtoDataSourceImpl(launcherItemListSource, bgDispatcher)
+        val savedOrderFlows = mutableListOf<List<AppOrderInfo>>()
+        val savedOrderComparatorFlows = mutableListOf<Comparator<AppOrderInfo>>()
+        // collect flows to listen for updates.
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrder().toList(savedOrderFlows)
+        }
+        advanceUntilIdle()
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrderComparator().toList(savedOrderComparatorFlows)
+        }
+        advanceUntilIdle()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.saveAppOrder(newSaveOrder)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, savedOrderFlows.size)
+        assertEquals(2, savedOrderComparatorFlows.size)
+        verify(launcherItemListSource, times(2)).readFromFile()
+        verify(launcherItemListSource).writeToFile(expectedAppOrderProtoMessage)
+        // initial empty app order.
+        assertEquals(emptyList<AppOrderInfo>(), savedOrderFlows[0])
+        // app order after newly saved order.
+        assertEquals(newSaveOrder, savedOrderFlows[1])
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetSavedAppOrderComparator_sortsAppsWithSavedOrder() = scope.runTest {
+        val previouslySavedApp1 = LauncherItemMessage.newBuilder()
+            .setPackageName("PackageName1")
+            .setClassName("ClassName1")
+            .setDisplayName("DisplayName1")
+            .setRelativePosition(1)
+            .setContainerID(-1).build()
+        val previouslySavedApp2 = LauncherItemMessage.newBuilder()
+            .setPackageName("PackageName2")
+            .setClassName("ClassName2")
+            .setDisplayName("DisplayName2")
+            .setRelativePosition(2)
+            .setContainerID(-1).build()
+        val savedProtoAppOrder = listOf(previouslySavedApp1, previouslySavedApp2)
+        val launcherItemListSource: LauncherItemListSource = mock {
+            on { readFromFile() } doReturn
+                    LauncherItemProto.LauncherItemListMessage.newBuilder()
+                        .addAllLauncherItemMessage(savedProtoAppOrder).build()
+        }
+        // mix of known and unknown app order
+        val appToBeSorted1 = AppOrderInfo("PackageName4", "ClassName4", "DisplayName4")
+        val appToBeSorted2 = AppOrderInfo("PackageName2", "ClassName2", "DisplayName2")
+        val appToBeSorted3 = AppOrderInfo("PackageName1", "ClassName1", "DisplayName1")
+        val appToBeSorted4 = AppOrderInfo("PackageName3", "ClassName3", "DisplayName3")
+        // Unsorted list of apps applied with the comparator under test.
+        val appsTobeSorted = listOf(appToBeSorted1, appToBeSorted2, appToBeSorted3, appToBeSorted4)
+        // The above list is expected to be sorted as below. Apps with unknown app order are sorted
+        // by AppOrderInfo#displayName.
+        val expectedOrder = listOf(appToBeSorted3, appToBeSorted2, appToBeSorted4, appToBeSorted1)
+        val appOrderDataSource = AppOrderProtoDataSourceImpl(launcherItemListSource, bgDispatcher)
+        val flows = mutableListOf<Comparator<AppOrderInfo>>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrderComparator().toList(flows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        verify(launcherItemListSource).readFromFile()
+        assertEquals(expectedOrder, appsTobeSorted.sortedWith(flows[0]))
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testClearAppOrder_clearsAppOrder_reportsUpdateToCollectors() = scope.runTest {
+        val previouslySavedApp1 = LauncherItemMessage.newBuilder()
+            .setPackageName("PackageName1")
+            .setClassName("ClassName1")
+            .setDisplayName("DisplayName1")
+            .setRelativePosition(1)
+            .setContainerID(-1).build()
+        val savedProtoAppOrder = listOf(previouslySavedApp1)
+        val expectedAppOrder = savedProtoAppOrder.map {
+            AppOrderInfo(
+                it.packageName,
+                it.className,
+                it.displayName
+            )
+        }
+        val launcherItemListSource: LauncherItemListSource = mock {
+            on { readFromFile() } doReturn
+                    LauncherItemProto.LauncherItemListMessage.newBuilder()
+                        .addAllLauncherItemMessage(savedProtoAppOrder).build()
+            on { deleteFile() } doReturn true
+        }
+        val appOrderDataSource = AppOrderProtoDataSourceImpl(launcherItemListSource, bgDispatcher)
+        // collect flows to listen for updates.
+        val savedOrderFlows = mutableListOf<List<AppOrderInfo>>()
+        val savedOrderComparatorFlows = mutableListOf<Comparator<AppOrderInfo>>()
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrder().toList(savedOrderFlows)
+        }
+        advanceUntilIdle()
+        launch(StandardTestDispatcher(testScheduler)) {
+            appOrderDataSource.getSavedAppOrderComparator().toList(savedOrderComparatorFlows)
+        }
+        advanceUntilIdle()
+
+        appOrderDataSource.clearAppOrder()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, savedOrderFlows.size)
+        assertEquals(2, savedOrderComparatorFlows.size)
+        verify(launcherItemListSource).deleteFile()
+        assertEquals(expectedAppOrder, savedOrderFlows[0])
+        assertEquals(expectedAppOrder, savedOrderFlows[0])
+        assertEquals(emptyList<AppOrderInfo>(), savedOrderFlows[1])
+        assertEquals(emptyList<AppOrderInfo>(), savedOrderFlows[1])
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSourceImplTest.kt
new file mode 100644
index 0000000..abbad98
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSourceImplTest.kt
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import com.android.car.carlauncher.R
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSource.MirroringPackageData
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSourceImpl.MirroringServiceConnection
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertNotNull
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.shadows.ShadowLooper
+
+@RunWith(RobolectricTestRunner::class)
+class ControlCenterMirroringDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+    private val resources = RuntimeEnvironment.getApplication().resources
+    private var bindServiceIntent: Intent? = null
+    private var bindServiceConnection: MirroringServiceConnection? = null
+    private var bindServiceFlags: Int? = null
+    private val bindService: (Intent, MirroringServiceConnection, Int) -> Unit =
+        { intent, serviceConnection, flags ->
+            bindServiceIntent = intent
+            bindServiceConnection = serviceConnection
+            bindServiceFlags = flags
+        }
+
+    private val unbindService: (MirroringServiceConnection) -> Unit = mock()
+
+    private val packageManager: PackageManager = mock {
+        on { resolveService(any(), anyInt()) } doReturn ResolveInfo()
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun getAppMirroringSession_registersService() = scope.runTest {
+        val expectedBindServiceFlags = Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT
+        val controlCenterMirroringDataSource = ControlCenterMirroringDataSourceImpl(
+            resources,
+            bindService,
+            unbindService,
+            packageManager,
+            bgDispatcher
+        )
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            controlCenterMirroringDataSource.getAppMirroringSession().collect()
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        val intentCaptor = ArgumentCaptor.forClass(Intent::class.java)
+        verify(packageManager).resolveService(intentCaptor.capture(), anyInt())
+        assertEquals(
+            ComponentName(
+                resources.getString(R.string.config_msg_mirroring_service_pkg_name),
+                resources.getString(R.string.config_msg_mirroring_service_class_name)
+            ),
+            intentCaptor.value.component
+        )
+        assertEquals(bindServiceIntent, intentCaptor.value)
+        assertNotNull(bindServiceConnection)
+        assertNotNull(bindServiceConnection?.mClientMessenger)
+        assertEquals(expectedBindServiceFlags, bindServiceFlags)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun getAppMirroringSession_noActiveSession_sendsNoMirroringData() = scope.runTest {
+        val controlCenterMirroringDataSource = ControlCenterMirroringDataSourceImpl(
+            resources,
+            bindService,
+            unbindService,
+            packageManager,
+            bgDispatcher
+        )
+        val flows = mutableListOf<MirroringPackageData>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            controlCenterMirroringDataSource.getAppMirroringSession().toList(flows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        assertEquals(MirroringPackageData.NO_MIRRORING, flows[0])
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun getAppMirroringSession_activeSession_sendsMirroringData() = scope.runTest {
+        val msg = createClientMessage()
+        val controlCenterMirroringDataSource = ControlCenterMirroringDataSourceImpl(
+            resources,
+            bindService,
+            unbindService,
+            packageManager,
+            bgDispatcher
+        )
+        val flows = mutableListOf<MirroringPackageData>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            controlCenterMirroringDataSource.getAppMirroringSession().toList(flows)
+        }
+        advanceUntilIdle()
+        // Fake sending Mirroring messages to receiver
+        val clientMessenger = bindServiceConnection?.mClientMessenger
+        clientMessenger?.send(msg)
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, flows.size)
+        assertEquals(MirroringPackageData.NO_MIRRORING, flows[0]) // Initial no mirroring packet
+        assertEquals(mirroringPackageName, flows[1].packageName)
+        assert(
+            Intent.parseUri(mirroringRedirectUri, Intent.URI_INTENT_SCHEME)
+                .filterEquals(flows[1].launchIntent)
+        )
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun getAppMirroringSession_sessionEnd_sendsNoMirroring() = scope.runTest {
+        // Empty packageName for no mirroring
+        val msg = createClientMessage(packageName = "")
+        val controlCenterMirroringDataSource = ControlCenterMirroringDataSourceImpl(
+            resources,
+            bindService,
+            unbindService,
+            packageManager,
+            bgDispatcher
+        )
+        val flows = mutableListOf<MirroringPackageData>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            controlCenterMirroringDataSource.getAppMirroringSession().toList(flows)
+        }
+        advanceUntilIdle()
+        // Fake sending Mirroring messages to receiver
+        val clientMessenger = bindServiceConnection?.mClientMessenger
+        clientMessenger?.send(msg)
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, flows.size)
+        assertEquals(MirroringPackageData.NO_MIRRORING, flows[0]) // Initial no mirroring packet
+        assertEquals(MirroringPackageData.NO_MIRRORING, flows[1]) // Service no mirroring packet
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun getAppMirroringSession_scopeClosed_shouldCleanUp() = scope.runTest {
+        val expectedUnRegisterMsg = Message.obtain(
+            null,
+            resources.getInteger(R.integer.config_msg_unregister_mirroring_pkg_code)
+        )
+        var actualUnRegisterMsg: Message? = null
+        val serviceMessenger = Messenger(object : Handler(Looper.myLooper()!!) {
+            override fun handleMessage(msg: Message) {
+                super.handleMessage(msg)
+                actualUnRegisterMsg = msg
+            }
+        })
+        val controlCenterMirroringDataSource = ControlCenterMirroringDataSourceImpl(
+            resources,
+            bindService,
+            unbindService,
+            packageManager,
+            bgDispatcher
+        )
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            controlCenterMirroringDataSource.getAppMirroringSession().collect()
+        }
+        advanceUntilIdle()
+        // Assign an external serviceMessenger
+        bindServiceConnection?.mServiceMessenger = serviceMessenger
+        coroutineContext.cancelChildren()
+        advanceUntilIdle()
+        ShadowLooper.runUiThreadTasks()
+
+        assertEquals(expectedUnRegisterMsg.toString(), actualUnRegisterMsg.toString())
+        assertNotNull(bindServiceConnection)
+        verify(unbindService).invoke(bindServiceConnection!!)
+    }
+
+    /**
+     * @param packageName name of the package under mirroring session,
+     *  If Empty it signifies [MirroringPackageData.NO_MIRRORING].
+     * @return [Message] to send to the Client to report mirroring session.
+     */
+    private fun createClientMessage(packageName: String = mirroringPackageName): Message {
+        val sendMirroringPackageCode =
+            resources.getInteger(R.integer.config_msg_send_mirroring_pkg_code)
+        val packageNameKey = resources.getString(R.string.config_msg_mirroring_pkg_name_key)
+        val redirectUriKey = resources.getString(R.string.config_msg_mirroring_redirect_uri_key)
+        val msg: Message = Message.obtain(
+            null,
+            resources.getInteger(R.integer.config_msg_register_mirroring_pkg_code)
+        )
+        msg.what = sendMirroringPackageCode
+        msg.obj = Bundle().apply {
+            putString(packageNameKey, packageName)
+            putString(redirectUriKey, mirroringRedirectUri)
+        }
+        return msg
+    }
+
+    companion object {
+        const val mirroringPackageName = "TestPackageName"
+        const val mirroringRedirectUri = "intent:#Intent;action=android.test.MIRRORING_TEST;end"
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSourceImplTest.kt
new file mode 100644
index 0000000..d32ccc6
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSourceImplTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
+import android.net.Uri
+import android.os.UserHandle
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertNotNull
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+
+@RunWith(RobolectricTestRunner::class)
+class LauncherActivitiesDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+
+    private val launcherActivities: List<LauncherActivityInfo> = listOf(mock(), mock())
+    private var broadcastReceiverCallback: BroadcastReceiver? = null
+    private val registerReceiverFun: (BroadcastReceiver, IntentFilter) -> Unit =
+        { broadcastReceiver, _ ->
+            broadcastReceiverCallback = broadcastReceiver
+        }
+    private val unregisterReceiverFun: (BroadcastReceiver) -> Unit = mock()
+    private val myUserHandle: UserHandle = mock()
+    private val launcherApps: LauncherApps = mock {
+        on { getActivityList(null, myUserHandle) } doReturn launcherActivities
+    }
+    private val dataSource: LauncherActivitiesDataSource = LauncherActivitiesDataSourceImpl(
+        launcherApps,
+        registerReceiverFun,
+        unregisterReceiverFun,
+        myUserHandle,
+        RuntimeEnvironment.getApplication().resources,
+        bgDispatcher
+    )
+
+    @Test
+    fun testGetAllLauncherActivities() = scope.runTest {
+        val listOfApps = dataSource.getAllLauncherActivities()
+
+        assertEquals(listOfApps.size, 2)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testOnPackagesChanged_broadcastReceived_shouldUpdateFlow() = scope.runTest {
+        // reset the broadcastReceiverCallback to null
+        broadcastReceiverCallback = null
+        val flows = mutableListOf<String>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            dataSource.getOnPackagesChanged().toList(flows)
+        }
+        advanceUntilIdle()
+        // Make a fake change in packages broadcast event.
+        val uri1 =
+            mock<Uri> { on { schemeSpecificPart } doReturn BROADCAST_EXPECTED_PACKAGE_NAME_1 }
+        val intent1: Intent = mock {
+            on { data } doReturn uri1
+        }
+        broadcastReceiverCallback?.onReceive(mock(), intent1)
+        advanceUntilIdle()
+        // Make another fake broadcast event with different package name
+        val uri2 =
+            mock<Uri> { on { schemeSpecificPart } doReturn BROADCAST_EXPECTED_PACKAGE_NAME_2 }
+        val intent2: Intent = mock {
+            on { data } doReturn uri2
+        }
+        broadcastReceiverCallback?.onReceive(mock(), intent2)
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        // BroadcastReceiver must been set after the producer call is trigger.
+        assertNotNull(broadcastReceiverCallback)
+        // Producer block sends an empty package immediately to the collector.
+        assertEquals(flows[0], "")
+        assertEquals(flows[1], BROADCAST_EXPECTED_PACKAGE_NAME_1)
+        assertEquals(flows[2], BROADCAST_EXPECTED_PACKAGE_NAME_2)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testOnPackagesChanged_scopeClosed_shouldCleanup() = scope.runTest {
+        // reset the broadcastReceiverCallback to null
+        broadcastReceiverCallback = null
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            dataSource.getOnPackagesChanged().collect()
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+        advanceUntilIdle()
+
+        // close all child coroutines, this should close the scope.
+        assertNotNull(broadcastReceiverCallback)
+        broadcastReceiverCallback?.let {
+            verify(unregisterReceiverFun).invoke(it)
+        }
+    }
+
+    companion object {
+        const val BROADCAST_EXPECTED_PACKAGE_NAME_1 = "com.test.example1"
+        const val BROADCAST_EXPECTED_PACKAGE_NAME_2 = "com.test.example2"
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSourceImplTest.kt
new file mode 100644
index 0000000..066fe27
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSourceImplTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.ComponentName
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.content.pm.ServiceInfo
+import junit.framework.TestCase.assertEquals
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.annotation.Config
+import shadows.ShadowMediaSource
+
+@RunWith(RobolectricTestRunner::class)
+@Config(shadows = [ShadowMediaSource::class])
+class MediaTemplateAppsDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val appContext = RuntimeEnvironment.getApplication().applicationContext
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+
+    private val listOfComponentNames = listOf(
+        ComponentName("com.test.media.package1", "Media"), // 0, MediaService
+        ComponentName("com.test.media.package2", "Media"), // 1, MediaService
+        ComponentName(
+            "com.test.custom.media.package2",
+            "CustomMedia"
+        ), // 2, has MediaService + Launcher Activity =  CustomMediaSource
+        ComponentName("com.test.not.media.package3", "NotMedia"), // 3, Not a MediaService
+    )
+
+    // List of MediaServices returned by the PackageManager for queryIntentServices.
+    private val mediaServices: List<ResolveInfo> = listOfComponentNames.map { getResolveInfo(it) }
+
+    /**
+     * Returns a mocked ResolveInfo
+     * @param componentName packageName + className of the mocked [ServiceInfo]
+     */
+    private fun getResolveInfo(componentName: ComponentName): ResolveInfo {
+        return ResolveInfo().apply {
+            serviceInfo = ServiceInfo().apply {
+                packageName = componentName.packageName
+                name = componentName.className
+            }
+        }
+    }
+
+    @Test
+    fun getAllMediaServices_noCustomComponents_shouldFilterMediaTemplateApps() = scope.runTest {
+        ShadowMediaSource.setMediaTemplates(
+            listOf(
+                listOfComponentNames[0],
+                listOfComponentNames[1]
+            )
+        )
+        val packageManager: PackageManager = mock {
+            on {
+                queryIntentServices(
+                    any(), anyInt()
+                )
+            } doReturn mediaServices
+        }
+        val mediaAppsDataSource = MediaTemplateAppsDataSourceImpl(
+            packageManager,
+            appContext,
+            bgDispatcher
+        )
+
+        val outputMediaServiceInfoList =
+            mediaAppsDataSource.getAllMediaServices(includeCustomMediaPackages = false)
+
+        val expectedMediaList =
+            mediaServices.filterIndexed { index, _ -> index != 2 && index != 3 }
+        assertEquals(expectedMediaList, outputMediaServiceInfoList)
+    }
+
+    @Test
+    fun getAllMediaServices_includeCustomComponents_shouldFilterMediaTemplateAndCustomApps() =
+        scope.runTest {
+            ShadowMediaSource.setMediaTemplates(
+                listOf(
+                    listOfComponentNames[0],
+                    listOfComponentNames[1]
+                )
+            )
+            ShadowMediaSource.setCustomTemplates(listOf(listOfComponentNames[2]))
+            val packageManager: PackageManager = mock {
+                on {
+                    queryIntentServices(
+                        any(), anyInt()
+                    )
+                } doReturn mediaServices
+            }
+            val mediaAppsDataSource = MediaTemplateAppsDataSourceImpl(
+                packageManager,
+                appContext,
+                bgDispatcher
+            )
+
+            val outputMediaServiceInfoList =
+                mediaAppsDataSource.getAllMediaServices(includeCustomMediaPackages = true)
+
+            val expectedMediaList = mediaServices.dropLast(1)
+            assertEquals(expectedMediaList, outputMediaServiceInfoList)
+        }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/UXRestrictionsDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/UXRestrictionsDataSourceImplTest.kt
new file mode 100644
index 0000000..e7d9e64
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/UXRestrictionsDataSourceImplTest.kt
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.car.Car
+import android.car.content.pm.CarPackageManager
+import android.car.drivingstate.CarUxRestrictions
+import android.car.drivingstate.CarUxRestrictionsManager
+import android.car.testapi.FakeCar
+import android.content.Context
+import android.media.session.MediaSessionManager
+import androidx.test.core.app.ApplicationProvider
+import java.lang.reflect.Field
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertFalse
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.shadows.ShadowLooper
+
+@RunWith(RobolectricTestRunner::class)
+class UXRestrictionsDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+    private val fakeCar: FakeCar =
+        FakeCar.createFakeCar(ApplicationProvider.getApplicationContext())
+    private val carUxRestrictionsManager =
+        fakeCar.car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
+    private val carUxRestrictionsController = fakeCar.carUxRestrictionController
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    /**
+     * Updates the CarUxRestrictions and notifies any active listeners.
+     *
+     * TODO: b/319266967 - FakeCarUxRestrictionsService always sets requiresDistractionOptimization
+     *  to 'false' when creating CarUxRestrictions.
+     *  Use reflection to set CarUxRestrictions with mRequiresDistractionOptimization as true.
+     */
+    private fun updateCarUxRestrictions(isDistractionOptimized: Boolean) {
+        // Set Restrictions to trigger callback.
+        carUxRestrictionsController.setUxRestrictions(CarUxRestrictions.UX_RESTRICTIONS_BASELINE)
+
+        val carUxRestrictionsField: Field =
+            carUxRestrictionsController.javaClass.getDeclaredField("mCarUxRestrictions")
+        carUxRestrictionsField.isAccessible = true
+        val currentCarUxRestrictions =
+            carUxRestrictionsField[carUxRestrictionsController] as CarUxRestrictions
+        val requiresDistractionOptimizationField: Field =
+            currentCarUxRestrictions.javaClass.getDeclaredField("mRequiresDistractionOptimization")
+        requiresDistractionOptimizationField.isAccessible = true
+
+        requiresDistractionOptimizationField[currentCarUxRestrictions] = isDistractionOptimized
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun requiresDistractionOptimization_sendsRequired() =
+        scope.runTest {
+            val uxRestrictionDataSource =
+                UXRestrictionDataSourceImpl(
+                    context,
+                    carUxRestrictionsManager,
+                    mock(CarPackageManager::class.java),
+                    mock(MediaSessionManager::class.java),
+                    RuntimeEnvironment.getApplication().resources,
+                    bgDispatcher
+                )
+            val outputFlows = mutableListOf<Boolean>()
+
+            launch(StandardTestDispatcher(testScheduler)) {
+                uxRestrictionDataSource.requiresDistractionOptimization().toList(outputFlows)
+            }
+            advanceUntilIdle()
+            updateCarUxRestrictions(isDistractionOptimized = true)
+            ShadowLooper.runUiThreadTasks()
+            advanceUntilIdle()
+            coroutineContext.cancelChildren()
+
+            // Asserts initial value of the DO(Distraction Optimization) as false and updated
+            // callback as true.
+            assertEquals(listOf(false, true), outputFlows)
+        }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun requiresDistractionOptimization_sendsNotRequired() = scope.runTest {
+        val uxRestrictionDataSource =
+            UXRestrictionDataSourceImpl(
+                context,
+                carUxRestrictionsManager,
+                mock(CarPackageManager::class.java),
+                mock(MediaSessionManager::class.java),
+                RuntimeEnvironment.getApplication().resources,
+                bgDispatcher
+            )
+        val outputFlows = mutableListOf<Boolean>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            uxRestrictionDataSource.requiresDistractionOptimization().toList(outputFlows)
+        }
+        advanceUntilIdle()
+        updateCarUxRestrictions(isDistractionOptimized = false)
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        // Asserts initial value of the DO(Distraction Optimization) as false and updated
+        // callback as false.
+        assertEquals(listOf(false, false), outputFlows)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun requiresDistractionOptimization_scopeClosed_shouldCleanUp() = scope.runTest {
+        val uxRestrictionDataSource =
+            UXRestrictionDataSourceImpl(
+                context,
+                carUxRestrictionsManager,
+                mock(CarPackageManager::class.java),
+                mock(MediaSessionManager::class.java),
+                RuntimeEnvironment.getApplication().resources,
+                bgDispatcher
+            )
+        val outputFlows = mutableListOf<Boolean>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            uxRestrictionDataSource.requiresDistractionOptimization().toList(outputFlows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+        advanceUntilIdle()
+
+        assertFalse(carUxRestrictionsController.isListenerRegistered)
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSourceImplTest.kt
new file mode 100644
index 0000000..71c3f72
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSourceImplTest.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources.restricted
+
+import android.car.settings.CarSettings
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.provider.Settings
+import com.android.car.carlauncher.datasources.restricted.DisabledAppsDataSourceImpl.Companion.PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR
+import junit.framework.TestCase.assertEquals
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.shadows.ShadowLooper
+
+@RunWith(RobolectricTestRunner::class)
+class DisabledAppsDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+
+    private val packageManager: PackageManager = mock {
+        on {
+            queryIntentActivities(
+                any(), any<PackageManager.ResolveInfoFlags>()
+            )
+        } doReturn listOf(
+            getResolveInfo(INSTALLED_COMPONENT_NAME_1),
+            getResolveInfo(INSTALLED_COMPONENT_NAME_2),
+            getResolveInfo(INSTALLED_COMPONENT_NAME_3),
+        )
+    }
+
+    private val contentResolver: ContentResolver =
+        RuntimeEnvironment.getApplication().contentResolver
+
+    /**
+     * Returns a mocked ResolveInfo
+     * @param componentName packageName + className of the mocked [ActivityInfo]
+     */
+    private fun getResolveInfo(componentName: ComponentName): ResolveInfo {
+        return ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = componentName.packageName
+                name = componentName.className
+            }
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetDisabledApps_shouldReturnMatchedDisabledApps() = scope.runTest {
+        Settings.Secure.putString(
+            contentResolver,
+            CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
+            EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1 +
+                    PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR +
+                    EXPECTED_NO_MATCH_DISABLED_PACKAGE_NAME_3
+        )
+        val disabledAppsDataSource = DisabledAppsDataSourceImpl(
+            contentResolver,
+            packageManager,
+            bgDispatcher
+        )
+        val flows = mutableListOf<List<ResolveInfo>>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            disabledAppsDataSource.getDisabledApps().toList(flows)
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        val actualMatchedPackages =
+            flows[0].map { it.activityInfo.packageName }
+        assertEquals(listOf(EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1), actualMatchedPackages)
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetDisabledApps_disabledAppsUpdated_shouldUpdateFlow() = scope.runTest {
+        Settings.Secure.putString(
+            contentResolver,
+            CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
+            EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1 +
+                    PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR +
+                    EXPECTED_NO_MATCH_DISABLED_PACKAGE_NAME_3
+        )
+        val disabledAppsDataSource = DisabledAppsDataSourceImpl(
+            contentResolver,
+            packageManager,
+            bgDispatcher
+        )
+        val flows = mutableListOf<List<ResolveInfo>>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            disabledAppsDataSource.getDisabledApps().toList(flows)
+        }
+
+        advanceUntilIdle()
+        // Changes in Disabled app while the client is still subscribed to the changes.
+        Settings.Secure.putString(
+            contentResolver,
+            CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
+            EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1 +
+                    PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR +
+                    EXPECTED_NO_MATCH_DISABLED_PACKAGE_NAME_3 +
+                    PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR +
+                    EXPECTED_MATCH_DISABLED_PACKAGE_NAME_2
+        )
+        advanceUntilIdle()
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, flows.size)
+        val actualUpdatedMatchedPackages =
+            flows[1].map { it.activityInfo.packageName }
+        assertEquals(
+            listOf(
+                EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1,
+                EXPECTED_MATCH_DISABLED_PACKAGE_NAME_2
+            ),
+            actualUpdatedMatchedPackages
+        )
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetDisabledApps_scopeClosed_shouldCleanUp() = scope.runTest {
+        val contentResolverSpy = spy(contentResolver)
+        val disabledAppsDataSource = DisabledAppsDataSourceImpl(
+            contentResolverSpy,
+            packageManager,
+            bgDispatcher
+        )
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            disabledAppsDataSource.getDisabledApps().collect()
+        }
+
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+        advanceUntilIdle()
+
+        verify(contentResolverSpy).unregisterContentObserver(any())
+    }
+
+    companion object {
+        // packageNames listed in Settings.Secure for disabled apps
+        const val EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1 = "com.test.example1"
+        const val EXPECTED_MATCH_DISABLED_PACKAGE_NAME_2 = "com.test.example2"
+        const val EXPECTED_NO_MATCH_DISABLED_PACKAGE_NAME_3 = "com.test.example3"
+
+        // componentNames available for the packageManager to query
+        val INSTALLED_COMPONENT_NAME_1 =
+            ComponentName(EXPECTED_MATCH_DISABLED_PACKAGE_NAME_1, "ExampleClass1")
+        val INSTALLED_COMPONENT_NAME_2 = ComponentName("com.test.example4", "ExampleClass2")
+        val INSTALLED_COMPONENT_NAME_3 =
+            ComponentName(EXPECTED_MATCH_DISABLED_PACKAGE_NAME_2, "ExampleClass3")
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/TosAppsDataSourceImplTest.kt b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/TosAppsDataSourceImplTest.kt
new file mode 100644
index 0000000..79d1530
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/com/android/car/carlauncher/datasources/restricted/TosAppsDataSourceImplTest.kt
@@ -0,0 +1,322 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources.restricted
+
+import android.car.settings.CarSettings
+import android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED
+import android.content.ComponentName
+import android.content.ContentResolver
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.database.ContentObserver
+import android.provider.Settings
+import com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.Companion.TOS_ACCEPTED
+import com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.Companion.TOS_DISABLED_APPS_SEPARATOR
+import com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.Companion.TOS_NOT_ACCEPTED
+import com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.Companion.TOS_UNINITIALIZED
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertTrue
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.cancelChildren
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.RuntimeEnvironment
+import org.robolectric.shadows.ShadowLooper
+
+@RunWith(RobolectricTestRunner::class)
+class TosAppsDataSourceImplTest {
+
+    private val scope = TestScope()
+    private val bgDispatcher =
+        StandardTestDispatcher(scope.testScheduler, name = "Background dispatcher")
+
+    private val packageManager: PackageManager = mock {
+        on {
+            queryIntentActivities(
+                any(), any<PackageManager.ResolveInfoFlags>()
+            )
+        } doReturn listOf(
+            getResolveInfo(INSTALLED_COMPONENT_NAME_1),
+            getResolveInfo(INSTALLED_COMPONENT_NAME_2),
+            getResolveInfo(INSTALLED_COMPONENT_NAME_3),
+        )
+    }
+
+    private val contentResolver: ContentResolver =
+        RuntimeEnvironment.getApplication().contentResolver
+
+    /**
+     * Returns a mocked ResolveInfo
+     * @param componentName packageName + className of the mocked [ActivityInfo]
+     */
+    private fun getResolveInfo(componentName: ComponentName): ResolveInfo {
+        return ResolveInfo().apply {
+            activityInfo = ActivityInfo().apply {
+                packageName = componentName.packageName
+                name = componentName.className
+            }
+        }
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_tosAccepted_shouldReturnBlockAppsAsFalse() = scope.runTest {
+        Settings.Secure.putString(
+            contentResolver,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_ACCEPTED
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolver, packageManager, bgDispatcher)
+        val flows = mutableListOf<TosState>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().toList(flows)
+        }
+        advanceUntilIdle()
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+
+        assertEquals(1, flows.size)
+        assertEquals(false, flows[0].shouldBlockTosApps)
+        assertTrue(flows[0].restrictedApps.isEmpty())
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_tosUnInitialized_shouldReturnBlockAppsAsFalse() = scope.runTest {
+        Settings.Secure.putString(
+            contentResolver,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_UNINITIALIZED
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolver, packageManager, bgDispatcher)
+        val flows = mutableListOf<TosState>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().toList(flows)
+        }
+        advanceUntilIdle()
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        assertEquals(false, flows[0].shouldBlockTosApps)
+        assertTrue(flows[0].restrictedApps.isEmpty())
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_tosNotAccepted_shouldReturnBlockAppsAsTrue() = scope.runTest {
+        Settings.Secure.putString(
+            contentResolver,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_NOT_ACCEPTED
+        )
+        Settings.Secure.putString(
+            contentResolver,
+            CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS,
+            "$EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1$TOS_DISABLED_APPS_SEPARATOR" +
+                    "$EXPECTED_NO_MATCH_TOS_DISABLED_PACKAGE_NAME_3$TOS_DISABLED_APPS_SEPARATOR" +
+                    EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolver, packageManager, bgDispatcher)
+        val flows = mutableListOf<TosState>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().toList(flows)
+        }
+        advanceUntilIdle()
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(1, flows.size)
+        assertEquals(true, flows[0].shouldBlockTosApps)
+        val actualTosDisabledApps = flows[0].restrictedApps.map { it.activityInfo.packageName }
+        assertEquals(
+            listOf(
+                EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1,
+                EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2
+            ),
+            actualTosDisabledApps
+        )
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_tosChangedToAccepted_shouldUnregisterObserver() = scope.runTest {
+        val contentResolverSpy = spy(contentResolver)
+        Settings.Secure.putString(
+            contentResolverSpy,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_NOT_ACCEPTED
+        )
+        Settings.Secure.putString(
+            contentResolverSpy,
+            CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS,
+            "$EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1$TOS_DISABLED_APPS_SEPARATOR" +
+                    "$EXPECTED_NO_MATCH_TOS_DISABLED_PACKAGE_NAME_3$TOS_DISABLED_APPS_SEPARATOR" +
+                    EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolverSpy, packageManager, bgDispatcher)
+        val flows = mutableListOf<TosState>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().toList(flows)
+        }
+        advanceUntilIdle()
+        // Tos state changed to Accepted
+        Settings.Secure.putString(
+            contentResolverSpy,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_ACCEPTED
+        )
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        assertEquals(2, flows.size)
+        // Initially shouldBlockTosApps is expected to be true.
+        assertEquals(true, flows[0].shouldBlockTosApps)
+        // After change in TOS state to Accepted shouldBlockTosApps is expected to be false.
+        assertEquals(false, flows[1].shouldBlockTosApps)
+        val actualChangedTosDisabledApps = flows[1].restrictedApps.map {
+            it.activityInfo.packageName
+        }
+        assertTrue(actualChangedTosDisabledApps.isEmpty())
+        verify(contentResolverSpy).unregisterContentObserver(any())
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_tosChangedToNotAccepted_shouldNotUnregisterObserver() = scope.runTest {
+        val contentResolverSpy = spy(contentResolver)
+        Settings.Secure.putString(
+            contentResolverSpy,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_UNINITIALIZED
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolverSpy, packageManager, bgDispatcher)
+        val flows = mutableListOf<TosState>()
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().toList(flows)
+        }
+        advanceUntilIdle()
+        // Tos state changed to Not_Accepted
+        Settings.Secure.putString(
+            contentResolverSpy,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_NOT_ACCEPTED
+        )
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        // Tos state updates the list of blocked apps.
+        Settings.Secure.putString(
+            contentResolverSpy,
+            CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS,
+            "$EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1$TOS_DISABLED_APPS_SEPARATOR" +
+                    "$EXPECTED_NO_MATCH_TOS_DISABLED_PACKAGE_NAME_3$TOS_DISABLED_APPS_SEPARATOR" +
+                    EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2
+        )
+        ShadowLooper.runUiThreadTasks()
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+
+        // Three updates: 1-UnInitialized, 2-NotAccepted, 3-BlockedAppsChanged.
+        assertEquals(3, flows.size)
+        // Initially shouldBlockTosApps is expected to be false in NotInitialized state.
+        assertEquals(false, flows[0].shouldBlockTosApps)
+        assertTrue(flows[0].restrictedApps.isEmpty())
+        // After change in TOS state to NotAccepted shouldBlockTosApps is expected to be true.
+        assertEquals(true, flows[1].shouldBlockTosApps)
+        // Since the list of blocked apps is also updated we will received another update.
+        assertEquals(true, flows[2].shouldBlockTosApps)
+        val actualChangedTosDisabledApps = flows[2].restrictedApps.map {
+            it.activityInfo.packageName
+        }
+        // Updates the list of blocked apps.
+        assertEquals(
+            listOf(
+                EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1,
+                EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2
+            ),
+            actualChangedTosDisabledApps
+        )
+        // We should not unregister the contentObservers.
+        verify(contentResolverSpy, never()).unregisterContentObserver(any())
+    }
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    @Test
+    fun testGetTosState_scopeClosed_shouldCleanUp() = scope.runTest {
+        val contentResolverSpy = spy(contentResolver)
+        Settings.Secure.putString(
+            contentResolverSpy,
+            KEY_USER_TOS_ACCEPTED,
+            TOS_UNINITIALIZED
+        )
+        val tosDataSource = TosDataSourceImpl(contentResolverSpy, packageManager, bgDispatcher)
+
+        launch(StandardTestDispatcher(testScheduler)) {
+            tosDataSource.getTosState().collect()
+        }
+        advanceUntilIdle()
+        coroutineContext.cancelChildren()
+        advanceUntilIdle()
+
+        val observerCapture = ArgumentCaptor.forClass(ContentObserver::class.java)
+        // registers same observer for two uri.
+        verify(contentResolverSpy, times(2))
+            .registerContentObserver(any(), any(), observerCapture.capture())
+        // After scope it closed it unregisters the content observer.
+        verify(contentResolverSpy).unregisterContentObserver(observerCapture.value)
+    }
+
+    companion object {
+        // packageNames listed in Settings.Secure for tos disabled apps
+        private const val EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1 = "com.test.example1"
+        private const val EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2 = "com.test.example2"
+        private const val EXPECTED_NO_MATCH_TOS_DISABLED_PACKAGE_NAME_3 = "com.test.example3"
+
+        // componentNames available for the packageManager to query
+        private val INSTALLED_COMPONENT_NAME_1 =
+            ComponentName(EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_1, "ExampleClass1")
+        private val INSTALLED_COMPONENT_NAME_2 =
+            ComponentName("com.test.example4", "ExampleClass2")
+        private val INSTALLED_COMPONENT_NAME_3 =
+            ComponentName(EXPECTED_MATCH_TOS_DISABLED_PACKAGE_NAME_2, "ExampleClass3")
+    }
+}
diff --git a/libs/appgrid/lib/robotests/src/shadows/ShadowMediaSource.kt b/libs/appgrid/lib/robotests/src/shadows/ShadowMediaSource.kt
new file mode 100644
index 0000000..69640b1
--- /dev/null
+++ b/libs/appgrid/lib/robotests/src/shadows/ShadowMediaSource.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 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 shadows
+
+import android.content.ComponentName
+import android.content.Context
+import com.android.car.media.common.source.MediaSource
+import org.robolectric.annotation.Implements
+
+@Implements(MediaSource::class)
+class ShadowMediaSource {
+    companion object {
+        private val mediaTemplateComponents = mutableSetOf<ComponentName>()
+        private val customComponents = mutableSetOf<ComponentName>()
+
+        @JvmStatic
+        fun isMediaTemplate(context: Context, mbsComponentName: ComponentName): Boolean {
+            return mediaTemplateComponents.contains(mbsComponentName)
+        }
+
+        @JvmStatic
+        fun isAudioMediaSource(context: Context, mbsComponentName: ComponentName): Boolean {
+            return customComponents.contains(mbsComponentName) ||
+                    mediaTemplateComponents.contains(mbsComponentName)
+        }
+
+        fun setMediaTemplates(mediaTemplateComponents: List<ComponentName>) {
+            this.mediaTemplateComponents.clear()
+            this.mediaTemplateComponents.addAll(mediaTemplateComponents)
+        }
+
+        fun setCustomTemplates(customComponents: List<ComponentName>) {
+            this.customComponents.clear()
+            this.customComponents.addAll(customComponents)
+        }
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridActivity.java b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridActivity.java
index 7d24e6b..4f6827b 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridActivity.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridActivity.java
@@ -16,343 +16,47 @@
 
 package com.android.car.carlauncher;
 
-import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
-import static android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED;
-import static android.content.Intent.URI_INTENT_SCHEME;
+import static com.android.car.carlauncher.AppGridFragment.MODE_INTENT_EXTRA;
 
-import static com.android.car.carlauncher.AppGridConstants.AppItemBoundDirection;
-import static com.android.car.carlauncher.AppGridConstants.PageOrientation;
-import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_LAUNCHABLES;
-import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_MEDIA_SERVICES;
-import static com.android.car.carlauncher.hidden.HiddenApiAccess.getDragSurface;
-
-import android.animation.ValueAnimator;
-import android.app.AlertDialog;
-import android.app.usage.UsageStats;
-import android.app.usage.UsageStatsManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.content.pm.CarPackageManager;
-import android.car.drivingstate.CarUxRestrictions;
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.car.media.CarMediaManager;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.database.ContentObserver;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.util.Log;
-import android.view.DragEvent;
-import android.view.SurfaceControl;
-import android.view.View;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
-import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
-import androidx.annotation.StringRes;
-import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AppCompatActivity;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-import androidx.recyclerview.widget.RecyclerView;
+import androidx.fragment.app.Fragment;
 
-import com.android.car.carlauncher.AppLauncherUtils.LauncherAppsInfo;
-import com.android.car.carlauncher.pagination.PageMeasurementHelper;
-import com.android.car.carlauncher.pagination.PaginationController;
-import com.android.car.carlauncher.recyclerview.AppGridAdapter;
-import com.android.car.carlauncher.recyclerview.AppGridItemAnimator;
-import com.android.car.carlauncher.recyclerview.AppGridLayoutManager;
-import com.android.car.carlauncher.recyclerview.AppItemViewHolder;
-import com.android.car.ui.AlertDialogBuilder;
-import com.android.car.ui.FocusArea;
-import com.android.car.ui.baselayout.Insets;
-import com.android.car.ui.baselayout.InsetsChangedListener;
+import com.android.car.carlauncher.AppGridFragment.Mode;
 import com.android.car.ui.core.CarUi;
-import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup;
 import com.android.car.ui.toolbar.MenuItem;
 import com.android.car.ui.toolbar.NavButtonMode;
 import com.android.car.ui.toolbar.ToolbarController;
 
-import java.net.URISyntaxException;
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
 
 /**
  * Launcher activity that shows a grid of apps.
  */
-public class AppGridActivity extends AppCompatActivity implements InsetsChangedListener,
-        AppGridPageSnapper.PageSnapListener, AppItemViewHolder.AppItemDragListener,
-        AppLauncherUtils.ShortcutsListener, PaginationController.DimensionUpdateListener {
+public class AppGridActivity extends AppCompatActivity {
     private static final String TAG = "AppGridActivity";
+    boolean mShowToolbar = false;
+    boolean mShowAllApps = true;
     private static final boolean DEBUG_BUILD = false;
-    private static final String MODE_INTENT_EXTRA = "com.android.car.carlauncher.mode";
-    private static CarUiShortcutsPopup sCarUiShortcutsPopup;
-
-    private boolean mShowAllApps = true;
-    private boolean mShowToolbar = true;
-    private final Set<String> mHiddenApps = new HashSet<>();
-    private PackageManager mPackageManager;
-    private UsageStatsManager mUsageStatsManager;
-    private AppInstallUninstallReceiver mInstallUninstallReceiver;
-    private Car mCar;
-    private CarUxRestrictionsManager mCarUxRestrictionsManager;
-    private CarPackageManager mCarPackageManager;
-    private CarMediaManager mCarMediaManager;
-    private Mode mMode;
-    private AlertDialog mStopAppAlertDialog;
-    private LauncherAppsInfo mAppsInfo;
-    private LauncherViewModel mLauncherModel;
-    private AppGridAdapter mAdapter;
-    private AppGridRecyclerView mRecyclerView;
-    private PageIndicator mPageIndicator;
-    private AppGridLayoutManager mLayoutManager;
-    private boolean mIsCurrentlyDragging;
-    private long mOffPageHoverBeforeScrollMs;
-    private Banner mBanner;
-
-    private AppGridDragController mAppGridDragController;
-    private PaginationController mPaginationController;
-
-    private int mNumOfRows;
-    private int mNumOfCols;
-    private int mAppGridMarginHorizontal;
-    private int mAppGridMarginVertical;
-    private int mAppGridWidth;
-    private int mAppGridHeight;
-    @PageOrientation
-    private int mPageOrientation;
-
-    private int mCurrentScrollOffset;
-    private int mCurrentScrollState;
-    private int mNextScrollDestination;
-    private RecyclerView.ItemDecoration mPageMarginDecorator;
-    private AppGridPageSnapper.AppGridPageSnapCallback mSnapCallback;
-    private AppItemViewHolder.AppItemDragCallback mDragCallback;
-
-    private Messenger mMirroringService;
-    private Messenger mMessenger;
-    private String mMirroringPackageName;
-    private Intent mMirroringIntentRedirect;
-    @VisibleForTesting
-    ContentObserver mTosContentObserver;
-    @VisibleForTesting
-    ContentObserver mTosDisabledAppsContentObserver;
-
-    /**
-     * enum to define the state of display area possible.
-     * CONTROL_BAR state is when only control bar is visible.
-     * FULL state is when display area hosting default apps  cover the screen fully.
-     * DEFAULT state where maps are shown above DA for default apps.
-     */
-    public enum CAR_LAUNCHER_STATE {
-        CONTROL_BAR, DEFAULT, FULL
-    }
-
-    public enum Mode {
-        ALL_APPS(R.string.app_launcher_title_all_apps,
-                APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
-                true),
-        MEDIA_ONLY(R.string.app_launcher_title_media_only,
-                APP_TYPE_MEDIA_SERVICES,
-                true),
-        MEDIA_POPUP(R.string.app_launcher_title_media_only,
-                APP_TYPE_MEDIA_SERVICES,
-                false),
-        ;
-        public final @StringRes int mTitleStringId;
-        public final @AppLauncherUtils.AppTypes int mAppTypes;
-        public final boolean mOpenMediaCenter;
-
-        Mode(@StringRes int titleStringId, @AppLauncherUtils.AppTypes int appTypes,
-                boolean openMediaCenter) {
-            mTitleStringId = titleStringId;
-            mAppTypes = appTypes;
-            mOpenMediaCenter = openMediaCenter;
-        }
-    }
-
-    private ServiceConnection mCarConnectionListener = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            try {
-                mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
-                        Car.CAR_UX_RESTRICTION_SERVICE);
-                CarUxRestrictions carUxRestrictions = mCarUxRestrictionsManager
-                        .getCurrentCarUxRestrictions();
-                boolean isDistractionOptimizationRequired;
-                if (carUxRestrictions == null) {
-                    Log.v(TAG, "No CarUxRestrictions on display");
-                    isDistractionOptimizationRequired = false;
-                } else {
-                    isDistractionOptimizationRequired = carUxRestrictions
-                            .isRequiresDistractionOptimization();
-                }
-                mAdapter.setIsDistractionOptimizationRequired(isDistractionOptimizationRequired);
-                mAdapter.setMode(mMode);
-                // set listener to update the app grid components and apply interaction restrictions
-                // when driving state changes
-                mCarUxRestrictionsManager.registerListener(restrictionInfo -> {
-                    handleDistractionOptimization(/* requiresDistractionOptimization */
-                            restrictionInfo.isRequiresDistractionOptimization());
-                });
-                mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
-                mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE);
-                reinitializeLauncherModel();
-            } catch (CarNotConnectedException e) {
-                Log.e(TAG, "Car not connected in CarConnectionListener", e);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            mCarUxRestrictionsManager = null;
-            mCarPackageManager = null;
-        }
-    };
-
-    /**
-     * Updates the state of the app grid components depending on the driving state.
-     */
-    private void handleDistractionOptimization(boolean requiresDistractionOptimization) {
-        mAdapter.setIsDistractionOptimizationRequired(requiresDistractionOptimization);
-        if (requiresDistractionOptimization) {
-            // if the user start driving while drag is in action, we cancel existing drag operations
-            if (mIsCurrentlyDragging) {
-                mIsCurrentlyDragging = false;
-                mLayoutManager.setShouldLayoutChildren(true);
-                mRecyclerView.cancelDragAndDrop();
-            }
-            dismissForceStopMenus();
-        }
-    }
-
-    private void reinitializeLauncherModel() {
-        ExecutorService fetchOrderExecutorService = Executors.newSingleThreadExecutor();
-        fetchOrderExecutorService.execute(() -> {
-            // first, we fetch apps order from data store file into memory
-            mLauncherModel.loadAppsOrderFromFile();
-            fetchOrderExecutorService.shutdown();
-        });
-        ExecutorService alphabetizeExecutorService = Executors.newSingleThreadExecutor();
-        alphabetizeExecutorService.execute(() -> {
-            Set<String> appsToHide = mShowAllApps ? Collections.emptySet() : mHiddenApps;
-            mAppsInfo = AppLauncherUtils.getLauncherApps(getApplicationContext(),
-                    appsToHide,
-                    mMode.mAppTypes,
-                    mMode.mOpenMediaCenter,
-                    getSystemService(LauncherApps.class),
-                    mCarPackageManager,
-                    mPackageManager,
-                    mCarMediaManager,
-                    AppGridActivity.this,
-                    mMirroringPackageName,
-                    mMirroringIntentRedirect);
-            // then, we ingest all apps info
-            mLauncherModel.processAppsInfoFromPlatform(mAppsInfo);
-            alphabetizeExecutorService.shutdown();
-        });
-    }
 
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         // TODO (b/267548246) deprecate toolbar and find another way to hide debug apps
-        mShowToolbar = false;
         if (mShowToolbar) {
             setTheme(R.style.Theme_Launcher_AppGridActivity);
         } else {
             setTheme(R.style.Theme_Launcher_AppGridActivity_NoToolbar);
         }
         super.onCreate(savedInstanceState);
-
-        mPackageManager = getPackageManager();
-        mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
-        mLauncherModel = new ViewModelProvider(AppGridActivity.this,
-                new LauncherViewModelFactory(getFilesDir())).get(
-                LauncherViewModel.class);
-        mLauncherModel.getCurrentLauncher().observe(
-                AppGridActivity.this, new Observer<List<LauncherItem>>() {
-                    @Override
-                    public void onChanged(List<LauncherItem> launcherItems) {
-                        mAdapter.setLauncherItems(launcherItems);
-                        mNextScrollDestination = mSnapCallback.getSnapPosition();
-                        updateScrollState();
-                        if (mMode == Mode.ALL_APPS) {
-                            mLauncherModel.handleAppListChange();
-                        }
-                    }
-                }
-        );
-        mCar = Car.createCar(this, mCarConnectionListener);
-        mHiddenApps.addAll(Arrays.asList(getResources().getStringArray(R.array.hidden_apps)));
-        setContentView(R.layout.app_grid_activity);
-        updateMode();
-
-        ServiceConnection mirroringConnectionListener = new ServiceConnection() {
-            @Override
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                Log.d(TAG, "Mirroring service connected");
-                mMirroringService = new Messenger(service);
-                mMessenger = new Messenger(new IncomingHandler(Looper.getMainLooper()));
-                Message msg = Message.obtain(null, getResources()
-                        .getInteger(R.integer.config_msg_register_mirroring_pkg_code));
-                msg.replyTo = mMessenger;
-                try {
-                    mMirroringService.send(msg);
-                } catch (RemoteException e) {
-                    Log.d(TAG, "Exception sending message to mirroring service: " + e);
-                }
-            }
-
-            @Override
-            public void onServiceDisconnected(ComponentName name) {
-                Log.d(TAG, "Mirroring service disconnected");
-                mMirroringPackageName = null;
-                mMirroringIntentRedirect = null;
-            }
-        };
-
-        // Bind to service that will inform about apps that are being mirrored
-        try {
-            Intent intent = new Intent();
-            intent.setComponent(new ComponentName(
-                    getString(R.string.config_msg_mirroring_service_pkg_name),
-                    getString(R.string.config_msg_mirroring_service_class_name)));
-            if (mPackageManager.resolveService(intent, /* flags = */ 0) != null) {
-                bindService(intent, mirroringConnectionListener,
-                        BIND_AUTO_CREATE | BIND_IMPORTANT);
-            }
-        } catch (SecurityException e) {
-            Log.e(TAG, "Error binding to mirroring service: " + e);
-        }
+        setContentView(R.layout.app_grid_container_activity);
 
         if (mShowToolbar) {
             ToolbarController toolbar = CarUi.requireToolbar(this);
-
             toolbar.setNavButtonMode(NavButtonMode.CLOSE);
-
             if (DEBUG_BUILD) {
                 toolbar.setMenuItems(Collections.singletonList(MenuItem.builder(this)
                         .setDisplayBehavior(MenuItem.DisplayBehavior.NEVER)
@@ -366,132 +70,23 @@
                         .build()));
             }
         }
-
-        mSnapCallback = new AppGridPageSnapper.AppGridPageSnapCallback(this);
-        mDragCallback = new AppItemViewHolder.AppItemDragCallback(this);
-
-        mNumOfCols = getResources().getInteger(R.integer.car_app_selector_column_number);
-        mNumOfRows = getResources().getInteger(R.integer.car_app_selector_row_number);
-        mAppGridDragController = new AppGridDragController();
-        mOffPageHoverBeforeScrollMs = getResources().getInteger(
-                R.integer.ms_off_page_hover_before_scroll);
-
-        mPageOrientation = getResources().getBoolean(R.bool.use_vertical_app_grid)
-                ? PageOrientation.VERTICAL : PageOrientation.HORIZONTAL;
-
-        mRecyclerView = requireViewById(R.id.apps_grid);
-        mRecyclerView.setFocusable(false);
-        mLayoutManager = new AppGridLayoutManager(this, mNumOfCols, mNumOfRows, mPageOrientation);
-        mRecyclerView.setLayoutManager(mLayoutManager);
-
-        AppGridPageSnapper pageSnapper = new AppGridPageSnapper(
-                this,
-                mNumOfCols,
-                mNumOfRows,
-                mSnapCallback);
-        pageSnapper.attachToRecyclerView(mRecyclerView);
-
-        mRecyclerView.setItemAnimator(new AppGridItemAnimator());
-
-        // hide the default scrollbar and replace it with a visual page indicator
-        mRecyclerView.setVerticalScrollBarEnabled(false);
-        mRecyclerView.setHorizontalScrollBarEnabled(false);
-        mRecyclerView.addOnScrollListener(new AppGridOnScrollListener());
-
-        // TODO: (b/271637411) move this to be contained in a scroll controller
-        mPageIndicator = requireViewById(R.id.page_indicator);
-        FrameLayout pageIndicatorContainer = requireViewById(R.id.page_indicator_container);
-        mPageIndicator.setContainer(pageIndicatorContainer);
-
-        // recycler view is set to LTR to prevent layout manager from reassigning layout direction.
-        // instead, PageIndexinghelper will determine the grid index based on the system layout
-        // direction and provide LTR mapping at adapter level.
-        mRecyclerView.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-        pageIndicatorContainer.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
-
-        // we create but do not attach the adapter to recyclerview until view tree layout is
-        // complete and the total size of the app grid is measureable.
-        mAdapter = new AppGridAdapter(this, mNumOfCols, mNumOfRows,
-                /* dataModel */ mLauncherModel, /* dragCallback */ mDragCallback,
-                /* snapCallback */ mSnapCallback);
-        mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
-            @Override
-            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
-                // scroll state will need to be updated after item has been dropped
-                mNextScrollDestination = mSnapCallback.getSnapPosition();
-                updateScrollState();
-            }
-        });
-        mRecyclerView.setAdapter(mAdapter);
-
-        // set drag listener and global layout listener, which will dynamically adjust app grid
-        // height and width depending on device screen size.
-        if (getResources().getBoolean(R.bool.config_allow_reordering)) {
-            mRecyclerView.setOnDragListener(new AppGridDragListener());
-        }
-
-        // since some measurements for window size may not be available yet during onCreate or may
-        // later change, we add a listener that redraws the app grid when window size changes.
-        LinearLayout windowBackground = requireViewById(R.id.apps_grid_background);
-        windowBackground.setOrientation(
-                isHorizontal() ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
-        PaginationController.DimensionUpdateCallback dimensionUpdateCallback =
-                new PaginationController.DimensionUpdateCallback();
-        dimensionUpdateCallback.addListener(mRecyclerView);
-        dimensionUpdateCallback.addListener(mPageIndicator);
-        dimensionUpdateCallback.addListener(this);
-        mPaginationController = new PaginationController(windowBackground, dimensionUpdateCallback);
-
-        mBanner = requireViewById(R.id.tos_banner);
-        updateTosBanner();
-
-        setupContentObserversForTos();
+        getSupportFragmentManager().beginTransaction().replace(R.id.fragmentContainer,
+                AppGridFragment.newInstance(parseMode(getIntent()))).commit();
     }
 
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
         setIntent(intent);
-        updateMode();
-        if (mCar.isConnected()) {
-            reinitializeLauncherModel();
-        }
-    }
-
-    @Override
-    protected void onDestroy() {
-        if (mCar != null && mCar.isConnected()) {
-            mCar.disconnect();
-            mCar = null;
-        }
-
-        if (mMirroringService != null) {
-            Message msg = Message.obtain(null,
-                    getResources().getInteger(R.integer.config_msg_unregister_mirroring_pkg_code));
-            msg.replyTo = mMessenger;
-            try {
-                mMirroringService.send(msg);
-            } catch (RemoteException e) {
-                Log.d(TAG, "Exception sending message to mirroring service: " + e);
-            }
-        }
-
-        unregisterContentObserversForTos();
-
-        super.onDestroy();
-    }
-
-    private void updateMode() {
-        mMode = parseMode(getIntent());
-        setTitle(mMode.mTitleStringId);
+        Mode mode = parseMode(intent);
+        setTitle(mode.getTitleStringId());
         if (mShowToolbar) {
-            CarUi.requireToolbar(this).setTitle(mMode.mTitleStringId);
+            CarUi.requireToolbar(this).setTitle(mode.getTitleStringId());
         }
-    }
-
-    @VisibleForTesting
-    boolean isHorizontal() {
-        return AppGridConstants.isHorizontal(mPageOrientation);
+        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
+        if (fragment instanceof AppGridFragment) {
+            ((AppGridFragment) fragment).updateMode(mode);
+        }
     }
 
     /**
@@ -508,517 +103,4 @@
         }
     }
 
-    @Override
-    protected void onResume() {
-        super.onResume();
-        updateTosBannerVisibility();
-        updateScrollState();
-        mAdapter.setLayoutDirection(getResources().getConfiguration().getLayoutDirection());
-    }
-
-    @Override
-    public void onDimensionsUpdated(PageMeasurementHelper.PageDimensions pageDimens,
-            PageMeasurementHelper.GridDimensions gridDimens) {
-        // TODO(b/271637411): move this method into a scroll controller
-        mAppGridMarginHorizontal = pageDimens.marginHorizontalPx;
-        mAppGridMarginVertical = pageDimens.marginVerticalPx;
-        mAppGridWidth = gridDimens.gridWidthPx;
-        mAppGridHeight = gridDimens.gridHeightPx;
-    }
-
-    /**
-     * Updates the scroll state after receiving data changes, such as new apps being added or
-     * reordered, and when user returns to launcher onResume.
-     *
-     * Additionally, notify page indicator to handle resizing in case new app addition creates a
-     * new page or deleted a page.
-     */
-    void updateScrollState() {
-        // TODO(b/271637411): move this method into a scroll controller
-        // to calculate how many pages we need to offset, we use the scroll offset anchor position
-        // as item count and map to the page which the anchor is on.
-        int offsetPageCount = mAdapter.getPageCount(mNextScrollDestination + 1) - 1;
-        mRecyclerView.suppressLayout(false);
-        mCurrentScrollOffset = offsetPageCount * (isHorizontal()
-                ? (mAppGridWidth + 2 * mAppGridMarginHorizontal)
-                : (mAppGridHeight + 2 * mAppGridMarginVertical));
-        mLayoutManager.scrollToPositionWithOffset(/* position */
-                offsetPageCount * mNumOfRows * mNumOfCols, /* offset */ 0);
-
-        mPageIndicator.updateOffset(mCurrentScrollOffset);
-        mPageIndicator.updatePageCount(mAdapter.getPageCount());
-    }
-
-    @Override
-    protected void onStart() {
-        super.onStart();
-        // register broadcast receiver for package installation and uninstallation
-        mInstallUninstallReceiver = new AppInstallUninstallReceiver();
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        registerReceiver(mInstallUninstallReceiver, filter);
-
-        // Connect to car service
-        mCar.connect();
-    }
-
-    @Override
-    protected void onStop() {
-        super.onStop();
-        // disconnect from app install/uninstall receiver
-        if (mInstallUninstallReceiver != null) {
-            unregisterReceiver(mInstallUninstallReceiver);
-            mInstallUninstallReceiver = null;
-        }
-        // disconnect from car listeners
-        try {
-            if (mCarUxRestrictionsManager != null) {
-                mCarUxRestrictionsManager.unregisterListener();
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Error unregistering listeners", e);
-        }
-        if (mCar != null) {
-            mCar.disconnect();
-        }
-    }
-
-    @Override
-    protected void onPause() {
-        dismissForceStopMenus();
-        super.onPause();
-    }
-
-    @Override
-    public void onSnapToPosition(int position) {
-        mNextScrollDestination = position;
-    }
-
-    @Override
-    public void onItemLongPressed(boolean isLongPressed) {
-        // after the user long presses the app icon, scrolling should be disabled until long press
-        // is canceled as to allow MotionEvent to be interpreted as attempt to drag the app icon.
-        mRecyclerView.suppressLayout(isLongPressed);
-    }
-
-    @Override
-    public void onItemSelected(int gridPositionFrom) {
-        mIsCurrentlyDragging = true;
-        mLayoutManager.setShouldLayoutChildren(false);
-        mAdapter.setDragStartPoint(gridPositionFrom);
-        dismissShortcutPopup();
-    }
-
-    @Override
-    public void onItemDragged() {
-        mAppGridDragController.cancelDelayedPageFling();
-    }
-
-    @Override
-    public void onDragExited(int gridPosition, @AppItemBoundDirection int exitDirection) {
-        if (mAdapter.getOffsetBoundDirection(gridPosition) == exitDirection) {
-            mAppGridDragController.postDelayedPageFling(exitDirection);
-        }
-    }
-
-    @Override
-    public void onItemDropped(int gridPositionFrom, int gridPositionTo) {
-        mLayoutManager.setShouldLayoutChildren(true);
-        mAdapter.moveAppItem(gridPositionFrom, gridPositionTo);
-    }
-
-    /**
-     * Note that in order to obtain usage stats from the previous boot,
-     * the device must have gone through a clean shut down process.
-     */
-    private List<AppMetaData> getMostRecentApps(LauncherAppsInfo appsInfo) {
-        ArrayList<AppMetaData> apps = new ArrayList<>();
-        if (appsInfo.isEmpty()) {
-            return apps;
-        }
-
-        // get the usage stats starting from 1 year ago with a INTERVAL_YEARLY granularity
-        // returning entries like:
-        // "During 2017 App A is last used at 2017/12/15 18:03"
-        // "During 2017 App B is last used at 2017/6/15 10:00"
-        // "During 2018 App A is last used at 2018/1/1 15:12"
-        List<UsageStats> stats =
-                mUsageStatsManager.queryUsageStats(
-                        UsageStatsManager.INTERVAL_YEARLY,
-                        System.currentTimeMillis() - DateUtils.YEAR_IN_MILLIS,
-                        System.currentTimeMillis());
-
-        if (stats == null || stats.size() == 0) {
-            return apps; // empty list
-        }
-
-        stats.sort(new LastTimeUsedComparator());
-
-        int currentIndex = 0;
-        int itemsAdded = 0;
-        int statsSize = stats.size();
-        int itemCount = Math.min(mNumOfCols, statsSize);
-        while (itemsAdded < itemCount && currentIndex < statsSize) {
-            UsageStats usageStats = stats.get(currentIndex);
-            String packageName = usageStats.getPackageName();
-            currentIndex++;
-
-            // do not include self
-            if (packageName.equals(getPackageName())) {
-                continue;
-            }
-
-            // TODO(b/136222320): UsageStats is obtained per package, but a package may contain
-            //  multiple media services. We need to find a way to get the usage stats per service.
-            ComponentName componentName = AppLauncherUtils.getMediaSource(mPackageManager,
-                    packageName);
-            // Exempt media services from background and launcher checks
-            if (!appsInfo.isMediaService(componentName)) {
-                // do not include apps that only ran in the background
-                if (usageStats.getTotalTimeInForeground() == 0) {
-                    continue;
-                }
-
-                // do not include apps that don't support starting from launcher
-                Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
-                if (intent == null || !intent.hasCategory(Intent.CATEGORY_LAUNCHER)) {
-                    continue;
-                }
-            }
-
-            AppMetaData app = appsInfo.getAppMetaData(componentName);
-            // Prevent duplicated entries
-            // e.g. app is used at 2017/12/31 23:59, and 2018/01/01 00:00
-            if (app != null && !apps.contains(app)) {
-                apps.add(app);
-                itemsAdded++;
-            }
-        }
-        return apps;
-    }
-
-    @Override
-    public void onCarUiInsetsChanged(Insets insets) {
-        requireViewById(R.id.apps_grid)
-                .setPadding(0, insets.getTop(), 0, insets.getBottom());
-        FocusArea focusArea = requireViewById(R.id.focus_area);
-        focusArea.setHighlightPadding(0, insets.getTop(), 0, insets.getBottom());
-        focusArea.setBoundsOffset(0, insets.getTop(), 0, insets.getBottom());
-
-        requireViewById(android.R.id.content)
-                .setPadding(insets.getLeft(), 0, insets.getRight(), 0);
-    }
-
-    @Override
-    public void onShortcutsShow(CarUiShortcutsPopup carUiShortcutsPopup) {
-        sCarUiShortcutsPopup = carUiShortcutsPopup;
-    }
-
-    @Override
-    public void onShortcutsItemClick(String packageName, CharSequence displayName,
-            boolean allowStopApp) {
-        AlertDialogBuilder builder = new AlertDialogBuilder(this)
-                .setTitle(R.string.app_launcher_stop_app_dialog_title);
-
-        if (allowStopApp) {
-            builder.setMessage(R.string.app_launcher_stop_app_dialog_text)
-                    .setPositiveButton(android.R.string.ok,
-                            (d, w) -> AppLauncherUtils.forceStop(packageName, AppGridActivity.this,
-                                    displayName, mCarMediaManager, mAppsInfo.getMediaServices(),
-                                    this))
-                    .setNegativeButton(android.R.string.cancel, /* onClickListener= */ null);
-        } else {
-            builder.setMessage(R.string.app_launcher_stop_app_cant_stop_text)
-                    .setNeutralButton(android.R.string.ok, /* onClickListener= */ null);
-        }
-        mStopAppAlertDialog = builder.show();
-    }
-
-    @Override
-    public void onStopAppSuccess(String message) {
-        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
-    }
-
-    private void dismissShortcutPopup() {
-        // TODO (b/268563442): shortcut popup is set to be static since its
-        // sometimes recreated when taskview is present, find out why
-        if (sCarUiShortcutsPopup != null) {
-            sCarUiShortcutsPopup.dismiss();
-            sCarUiShortcutsPopup = null;
-        }
-    }
-
-    private void dismissForceStopMenus() {
-        if (sCarUiShortcutsPopup != null) {
-            sCarUiShortcutsPopup.dismissImmediate();
-            sCarUiShortcutsPopup = null;
-        }
-        if (mStopAppAlertDialog != null) {
-            mStopAppAlertDialog.dismiss();
-        }
-    }
-
-    /**
-     * Comparator for {@link UsageStats} that sorts the list by the "last time used" property
-     * in descending order.
-     */
-    private static class LastTimeUsedComparator implements Comparator<UsageStats> {
-        @Override
-        public int compare(UsageStats stat1, UsageStats stat2) {
-            Long time1 = stat1.getLastTimeUsed();
-            Long time2 = stat2.getLastTimeUsed();
-            return time2.compareTo(time1);
-        }
-    }
-
-    private class AppInstallUninstallReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String packageName = intent.getData().getSchemeSpecificPart();
-            if (TextUtils.isEmpty(packageName)) {
-                Log.e(TAG, "System sent an empty app install/uninstall broadcast");
-                return;
-            }
-            // TODO b/256684061: find better way to get AppInfo from package name.
-            reinitializeLauncherModel();
-        }
-    }
-
-    private class AppGridOnScrollListener extends RecyclerView.OnScrollListener {
-        @Override
-        public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
-            mCurrentScrollOffset = mCurrentScrollOffset + (isHorizontal() ? dx : dy);
-            mPageIndicator.updateOffset(mCurrentScrollOffset);
-        }
-
-        @Override
-        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
-            mCurrentScrollState = newState;
-            mSnapCallback.setScrollState(mCurrentScrollState);
-            switch (newState) {
-                case RecyclerView.SCROLL_STATE_DRAGGING:
-                    if (!mIsCurrentlyDragging) {
-                        mDragCallback.cancelDragTasks();
-                    }
-                    dismissShortcutPopup();
-                    mPageIndicator.animateAppearance();
-                    break;
-
-                case RecyclerView.SCROLL_STATE_SETTLING:
-                    mPageIndicator.animateAppearance();
-                    break;
-
-                case RecyclerView.SCROLL_STATE_IDLE:
-                    if (mIsCurrentlyDragging) {
-                        mLayoutManager.setShouldLayoutChildren(false);
-                    }
-                    mPageIndicator.animateFading();
-                    // in case the recyclerview was scrolled by rotary input, we need to handle
-                    // focusing the correct element: either on the first or last element on page
-                    mRecyclerView.maybeHandleRotaryFocus();
-            }
-        }
-    }
-
-    private class AppGridDragController {
-        // TODO: (b/271320404) move DragController to separate directory called dragndrop and
-        // migrate logic this class and AppItemViewHolder there.
-        private final Handler mHandler;
-
-        AppGridDragController() {
-            mHandler = new Handler(getMainLooper());
-        }
-
-        void cancelDelayedPageFling() {
-            mHandler.removeCallbacksAndMessages(null);
-        }
-
-        void postDelayedPageFling(@AppItemBoundDirection int exitDirection) {
-            boolean scrollToNextPage = isHorizontal()
-                    ? exitDirection == AppItemBoundDirection.RIGHT
-                    : exitDirection == AppItemBoundDirection.BOTTOM;
-            mHandler.removeCallbacksAndMessages(null);
-            mHandler.postDelayed(new Runnable() {
-                public void run() {
-                    if (mCurrentScrollState == RecyclerView.SCROLL_STATE_IDLE) {
-                        mAdapter.updatePageScrollDestination(scrollToNextPage);
-                        mNextScrollDestination = mSnapCallback.getSnapPosition();
-
-                        mLayoutManager.setShouldLayoutChildren(true);
-                        mRecyclerView.smoothScrollToPosition(mNextScrollDestination);
-                    }
-                    // another delayed scroll will be queued to enable the user to input multiple
-                    // page scrolls by holding the recyclerview at the app grid margin
-                    postDelayedPageFling(exitDirection);
-                }
-            }, mOffPageHoverBeforeScrollMs);
-        }
-    }
-
-    /**
-     * Private onDragListener for handling dispatching off page scroll event when user holds the app
-     * icon at the page margin.
-     */
-    private class AppGridDragListener implements View.OnDragListener {
-        @Override
-        public boolean onDrag(View v, DragEvent event) {
-            int action = event.getAction();
-            if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
-                mIsCurrentlyDragging = false;
-                mAppGridDragController.cancelDelayedPageFling();
-                mDragCallback.resetCallbackState();
-                mLayoutManager.setShouldLayoutChildren(true);
-                if (action == DragEvent.ACTION_DROP) {
-                    return false;
-                } else {
-                    animateDropEnded(getDragSurface(event));
-                }
-            }
-            return true;
-        }
-    }
-
-    private void animateDropEnded(@Nullable SurfaceControl dragSurface) {
-        if (dragSurface == null) {
-            Log.d(TAG, "animateDropEnded, dragSurface unavailable");
-            return;
-        }
-        // update default animation for the drag shadow after user lifts their finger
-        SurfaceControl.Transaction txn = new SurfaceControl.Transaction();
-        // set an animator to animate a delay before clearing the dragSurface
-        ValueAnimator delayedDismissAnimator = ValueAnimator.ofFloat(0f, 1f);
-        delayedDismissAnimator.setStartDelay(
-                getResources().getInteger(R.integer.ms_drop_animation_delay));
-        delayedDismissAnimator.addUpdateListener(
-                new ValueAnimator.AnimatorUpdateListener() {
-                    @Override
-                    public void onAnimationUpdate(ValueAnimator animation) {
-                        txn.setAlpha(dragSurface, 0);
-                        txn.apply();
-                    }
-                });
-        delayedDismissAnimator.start();
-    }
-
-    private void updateTosBanner() {
-        mBanner.setFirstButtonOnClickListener(v -> {
-            Intent tosIntent = AppLauncherUtils.getIntentForTosAcceptanceFlow(v.getContext());
-            AppLauncherUtils.launchApp(v.getContext(), tosIntent);
-        });
-        mBanner.setSecondButtonOnClickListener(
-                v -> mBanner.setVisibility(View.GONE));
-    }
-
-    private void updateTosBannerVisibility() {
-
-        if (AppLauncherUtils.showTosBanner(this)) {
-            runOnUiThread(() -> {
-                mBanner.setVisibility(View.VISIBLE);
-            });
-        } else {
-            mBanner.setVisibility(View.GONE);
-        }
-    }
-
-    private void setupContentObserversForTos() {
-        if (AppLauncherUtils.tosStatusUninitialized(/* context = */ this)
-                || !AppLauncherUtils.tosAccepted(/* context = */ this)) {
-            Log.i(TAG, "TOS not accepted, setting up content observers for TOS state");
-        } else {
-            Log.i(TAG, "TOS accepted, state will remain accepted, "
-                    + "don't need to observe this value");
-            return;
-        }
-        mTosContentObserver = new ContentObserver(new Handler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                super.onChange(selfChange);
-                boolean tosState = AppLauncherUtils.tosAccepted(getBaseContext());
-                Log.i(TAG, "TOS state updated:" + tosState);
-                reinitializeLauncherModel();
-                if (tosState) {
-                    unregisterContentObserversForTos();
-                }
-            }
-        };
-        mTosDisabledAppsContentObserver = new ContentObserver(new Handler()) {
-            @Override
-            public void onChange(boolean selfChange) {
-                super.onChange(selfChange);
-                reinitializeLauncherModel();
-            }
-        };
-        getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(KEY_USER_TOS_ACCEPTED),
-                /* notifyForDescendants*/ false,
-                mTosContentObserver);
-        getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(KEY_UNACCEPTED_TOS_DISABLED_APPS),
-                /* notifyForDescendants*/ false,
-                mTosDisabledAppsContentObserver
-        );
-    }
-
-    private void unregisterContentObserversForTos() {
-        if (mTosContentObserver != null) {
-            Log.i(TAG, "Unregister content observer for tos state");
-            getContentResolver().unregisterContentObserver(mTosContentObserver);
-            mTosContentObserver = null;
-        }
-        if (mTosDisabledAppsContentObserver != null) {
-            Log.i(TAG, "Unregister content observer for tos disabled apps");
-            getContentResolver().unregisterContentObserver(mTosDisabledAppsContentObserver);
-            mTosDisabledAppsContentObserver = null;
-        }
-    }
-
-    @VisibleForTesting
-    void setCarUxRestrictionsManager(CarUxRestrictionsManager carUxRestrictionsManager) {
-        mCarUxRestrictionsManager = carUxRestrictionsManager;
-    }
-
-    @VisibleForTesting
-    void setPageIndicator(PageIndicator pageIndicator) {
-        mPageIndicator = pageIndicator;
-    }
-
-    class IncomingHandler extends Handler {
-
-        int mSendMirroringPkgCode = getResources()
-                .getInteger(R.integer.config_msg_send_mirroring_pkg_code);
-        String mMirroringPkgNameKey = getString(R.string.config_msg_mirroring_pkg_name_key);
-        String mMirroringRedirectUriKey = getString(R.string.config_msg_mirroring_redirect_uri_key);
-
-        IncomingHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            Log.d(TAG, "Message received: " + msg);
-            if (msg.what
-                    == mSendMirroringPkgCode) {
-                Bundle bundle = (Bundle) msg.obj;
-                mMirroringPackageName =
-                        bundle.getString(mMirroringPkgNameKey);
-                Log.d(TAG, "message received with package name = " + mMirroringPackageName);
-                try {
-                    mMirroringIntentRedirect = Intent.parseUri(
-                            bundle.getString(mMirroringRedirectUriKey),
-                            URI_INTENT_SCHEME);
-                    Log.d(TAG, "intent is: " + mMirroringIntentRedirect);
-                    mLauncherModel.updateMirroringItem(mMirroringPackageName,
-                            mMirroringIntentRedirect);
-                } catch (URISyntaxException e) {
-                    Log.d(TAG, "Error parsing mirroring redirect intent " + e);
-                }
-            } else {
-                super.handleMessage(msg);
-            }
-        }
-    }
 }
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridFragment.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridFragment.kt
new file mode 100644
index 0000000..62735c0
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridFragment.kt
@@ -0,0 +1,632 @@
+/*
+ * Copyright (C) 2024 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.carlauncher
+
+import android.animation.ValueAnimator
+import android.car.Car
+import android.car.content.pm.CarPackageManager
+import android.car.drivingstate.CarUxRestrictionsManager
+import android.car.media.CarMediaManager
+import android.content.BroadcastReceiver
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.media.session.MediaSessionManager
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper.getMainLooper
+import android.os.Process
+import android.os.UserManager
+import android.util.Log
+import android.view.DragEvent
+import android.view.LayoutInflater
+import android.view.SurfaceControl
+import android.view.View
+import android.view.ViewGroup
+import android.view.ViewTreeObserver
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import androidx.annotation.StringRes
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.asLiveData
+import androidx.recyclerview.widget.RecyclerView
+import com.android.car.carlauncher.AppGridConstants.AppItemBoundDirection
+import com.android.car.carlauncher.AppGridConstants.PageOrientation
+import com.android.car.carlauncher.AppGridConstants.isHorizontal
+import com.android.car.carlauncher.AppGridFragment.AppTypes.Companion.APP_TYPE_LAUNCHABLES
+import com.android.car.carlauncher.AppGridFragment.AppTypes.Companion.APP_TYPE_MEDIA_SERVICES
+import com.android.car.carlauncher.AppGridPageSnapper.AppGridPageSnapCallback
+import com.android.car.carlauncher.AppGridPageSnapper.PageSnapListener
+import com.android.car.carlauncher.AppGridViewModel.Companion.provideFactory
+import com.android.car.carlauncher.datasources.AppOrderDataSource
+import com.android.car.carlauncher.datasources.AppOrderProtoDataSourceImpl
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSource
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSourceImpl
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSourceImpl.MirroringServiceConnection
+import com.android.car.carlauncher.datasources.LauncherActivitiesDataSource
+import com.android.car.carlauncher.datasources.LauncherActivitiesDataSourceImpl
+import com.android.car.carlauncher.datasources.MediaTemplateAppsDataSource
+import com.android.car.carlauncher.datasources.MediaTemplateAppsDataSourceImpl
+import com.android.car.carlauncher.datasources.UXRestrictionDataSource
+import com.android.car.carlauncher.datasources.UXRestrictionDataSourceImpl
+import com.android.car.carlauncher.datasources.restricted.DisabledAppsDataSource
+import com.android.car.carlauncher.datasources.restricted.DisabledAppsDataSourceImpl
+import com.android.car.carlauncher.datasources.restricted.TosDataSource
+import com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl
+import com.android.car.carlauncher.datastore.launcheritem.LauncherItemListSource
+import com.android.car.carlauncher.pagination.PageMeasurementHelper
+import com.android.car.carlauncher.pagination.PaginationController
+import com.android.car.carlauncher.pagination.PaginationController.DimensionUpdateCallback
+import com.android.car.carlauncher.pagination.PaginationController.DimensionUpdateListener
+import com.android.car.carlauncher.recyclerview.AppGridAdapter
+import com.android.car.carlauncher.recyclerview.AppGridAdapter.AppGridAdapterListener
+import com.android.car.carlauncher.recyclerview.AppGridItemAnimator
+import com.android.car.carlauncher.recyclerview.AppGridLayoutManager
+import com.android.car.carlauncher.recyclerview.AppItemViewHolder.AppItemDragCallback
+import com.android.car.carlauncher.recyclerview.AppItemViewHolder.AppItemDragListener
+import com.android.car.carlauncher.repositories.AppGridRepository
+import com.android.car.carlauncher.repositories.AppGridRepositoryImpl
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory
+import com.android.car.carlauncher.repositories.appactions.AppShortcutsFactory
+import com.android.car.carlauncher.repositories.appactions.AppShortcutsFactory.ShortcutsListener
+import com.android.car.hidden.apis.HiddenApiAccess
+import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
+import kotlinx.coroutines.Dispatchers.Default
+import kotlinx.coroutines.Dispatchers.IO
+
+/**
+ * Fragment which renders the Apps based on the [Mode] provided in the [setArguments]
+ *
+ * To create an instance of this Fragment use [newInstance]
+ */
+class AppGridFragment : Fragment(), PageSnapListener, AppItemDragListener, DimensionUpdateListener,
+    AppGridAdapterListener {
+
+    private lateinit var car: Car
+    private lateinit var mode: Mode
+    private lateinit var snapCallback: AppGridPageSnapCallback
+    private lateinit var dragCallback: AppItemDragCallback
+    private lateinit var appGridDragController: AppGridDragController
+    private lateinit var appGridRecyclerView: AppGridRecyclerView
+    private lateinit var layoutManager: AppGridLayoutManager
+    private lateinit var pageIndicator: PageIndicator
+    private lateinit var adapter: AppGridAdapter
+    private lateinit var paginationController: PaginationController
+    private lateinit var backgroundAnimationHelper: BackgroundAnimationHelper
+    private lateinit var appGridViewModel: AppGridViewModel
+    private lateinit var banner: Banner
+
+    private var appGridMarginHorizontal = 0
+    private var appGridMarginVertical = 0
+    private var appGridWidth = 0
+    private var appGridHeight = 0
+    private var offPageHoverBeforeScrollMs = 0L
+    private var numOfCols = 0
+    private var numOfRows = 0
+    private var nextScrollDestination = 0
+    private var currentScrollOffset = 0
+    private var currentScrollState = 0
+    private var isCurrentlyDragging = false
+    private var carUiShortcutsPopup: CarUiShortcutsPopup? = null
+
+    @PageOrientation
+    private var pageOrientation = 0
+
+    override fun onCreateView(
+        inflater: LayoutInflater,
+        container: ViewGroup?,
+        savedInstanceState: Bundle?
+    ): View? {
+        super.onCreateView(inflater, container, savedInstanceState)
+        return inflater.inflate(R.layout.app_grid_fragment, container, false)
+    }
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        car = Car.createCar(requireContext()) ?: throw IllegalStateException("Car not initialized")
+        mode = Mode.valueOf(requireArguments().getString(MODE_INTENT_EXTRA, Mode.ALL_APPS.name))
+        initViewModel()
+        updateMode(mode)
+
+        snapCallback = AppGridPageSnapCallback(this)
+        dragCallback = AppItemDragCallback(this)
+
+        numOfCols = resources.getInteger(R.integer.car_app_selector_column_number)
+        numOfRows = resources.getInteger(R.integer.car_app_selector_row_number)
+        appGridDragController = AppGridDragController()
+        offPageHoverBeforeScrollMs = resources.getInteger(
+            R.integer.ms_off_page_hover_before_scroll
+        ).toLong()
+
+        pageOrientation =
+            if (resources.getBoolean(R.bool.use_vertical_app_grid)) {
+                PageOrientation.VERTICAL
+            } else {
+                PageOrientation.HORIZONTAL
+            }
+
+        appGridRecyclerView = view.requireViewById(R.id.apps_grid)
+        appGridRecyclerView.isFocusable = false
+        layoutManager =
+            AppGridLayoutManager(requireContext(), numOfCols, numOfRows, pageOrientation)
+        appGridRecyclerView.layoutManager = layoutManager
+
+        val pageSnapper = AppGridPageSnapper(
+            requireContext(),
+            numOfCols,
+            numOfRows,
+            snapCallback
+        )
+        pageSnapper.attachToRecyclerView(appGridRecyclerView)
+
+        appGridRecyclerView.itemAnimator = AppGridItemAnimator()
+
+        // hide the default scrollbar and replace it with a visual page indicator
+        appGridRecyclerView.isVerticalScrollBarEnabled = false
+        appGridRecyclerView.isHorizontalScrollBarEnabled = false
+        appGridRecyclerView.addOnScrollListener(AppGridOnScrollListener())
+
+        // TODO: (b/271637411) move this to be contained in a scroll controller
+        pageIndicator = view.requireViewById(R.id.page_indicator)
+        val pageIndicatorContainer: FrameLayout =
+            view.requireViewById(R.id.page_indicator_container)
+        pageIndicator.setContainer(pageIndicatorContainer)
+
+        // recycler view is set to LTR to prevent layout manager from reassigning layout direction.
+        // instead, PageIndexinghelper will determine the grid index based on the system layout
+        // direction and provide LTR mapping at adapter level.
+        appGridRecyclerView.layoutDirection = View.LAYOUT_DIRECTION_LTR
+        pageIndicatorContainer.layoutDirection = View.LAYOUT_DIRECTION_LTR
+
+        // we create but do not attach the adapter to recyclerview until view tree layout is
+        // complete and the total size of the app grid is measureable.
+        adapter = AppGridAdapter(
+            requireContext(), numOfCols, numOfRows, dragCallback, snapCallback, this, mode
+        )
+
+        adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
+            override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
+                // scroll state will need to be updated after item has been dropped
+                nextScrollDestination = snapCallback.snapPosition
+                updateScrollState()
+            }
+        })
+        appGridRecyclerView.adapter = adapter
+
+        appGridViewModel.getAppList().asLiveData().observe(
+            viewLifecycleOwner
+        ) { appItems: List<AppItem?>? ->
+            adapter.setLauncherItems(appItems)
+            nextScrollDestination = snapCallback.snapPosition
+            updateScrollState()
+        }
+
+        appGridViewModel.requiresDistractionOptimization().asLiveData().observe(
+            viewLifecycleOwner
+        ) { uxRestrictions: Boolean ->
+            handleDistractionOptimization(
+                uxRestrictions
+            )
+        }
+
+        // set drag listener and global layout listener, which will dynamically adjust app grid
+        // height and width depending on device screen size. ize.
+        if (resources.getBoolean(R.bool.config_allow_reordering)) {
+            appGridRecyclerView.setOnDragListener(AppGridDragListener())
+        }
+
+        // since some measurements for window size may not be available yet during onCreate or may
+        // later change, we add a listener that redraws the app grid when window size changes.
+        val windowBackground: LinearLayout = view.requireViewById(R.id.apps_grid_background)
+        windowBackground.orientation =
+            if (isHorizontal(pageOrientation)) LinearLayout.VERTICAL else LinearLayout.HORIZONTAL
+        val dimensionUpdateCallback = DimensionUpdateCallback()
+        dimensionUpdateCallback.addListener(appGridRecyclerView)
+        dimensionUpdateCallback.addListener(pageIndicator)
+        dimensionUpdateCallback.addListener(this)
+        paginationController = PaginationController(windowBackground, dimensionUpdateCallback)
+
+        banner = view.requireViewById(R.id.tos_banner)
+
+        backgroundAnimationHelper = BackgroundAnimationHelper(windowBackground, banner)
+
+        setupTosBanner()
+    }
+
+    /**
+     * Updates the state of the app grid components depending on the driving state.
+     */
+    private fun handleDistractionOptimization(requiresDistractionOptimization: Boolean) {
+        adapter.setIsDistractionOptimizationRequired(requiresDistractionOptimization)
+        if (requiresDistractionOptimization) {
+            // if the user start driving while drag is in action, we cancel existing drag operations
+            if (isCurrentlyDragging) {
+                isCurrentlyDragging = false
+                layoutManager.setShouldLayoutChildren(true)
+                appGridRecyclerView.cancelDragAndDrop()
+            }
+            dismissShortcutPopup()
+        }
+    }
+
+    private fun initViewModel() {
+        val launcherActivities: LauncherActivitiesDataSource = LauncherActivitiesDataSourceImpl(
+            requireContext().getSystemService(LauncherApps::class.java),
+            { broadcastReceiver: BroadcastReceiver?, intentFilter: IntentFilter? ->
+                requireContext().registerReceiver(broadcastReceiver, intentFilter)
+            },
+            { broadcastReceiver: BroadcastReceiver? ->
+                requireContext().unregisterReceiver(broadcastReceiver)
+            },
+            Process.myUserHandle(),
+            requireContext().applicationContext.resources,
+            Default
+        )
+        val mediaTemplateApps: MediaTemplateAppsDataSource = MediaTemplateAppsDataSourceImpl(
+            requireContext().packageManager,
+            requireContext().applicationContext,
+            Default
+        )
+        val disabledApps: DisabledAppsDataSource = DisabledAppsDataSourceImpl(
+            requireContext().contentResolver,
+            requireContext().packageManager,
+            IO
+        )
+        val tosApps: TosDataSource = TosDataSourceImpl(
+            requireContext().contentResolver,
+            requireContext().packageManager,
+            IO
+        )
+        val controlCenterMirroringDataSource: ControlCenterMirroringDataSource =
+            ControlCenterMirroringDataSourceImpl(
+                requireContext().applicationContext.resources,
+                { intent: Intent, serviceConnection: MirroringServiceConnection, flags: Int ->
+                    requireContext().bindService(intent, serviceConnection, flags)
+                },
+                { serviceConnection: MirroringServiceConnection ->
+                    requireContext().unbindService(serviceConnection)
+                },
+                requireContext().packageManager,
+                IO
+            )
+        val uxRestrictionDataSource: UXRestrictionDataSource = UXRestrictionDataSourceImpl(
+            requireContext(),
+            requireNotNull(car.getCarManager(CarUxRestrictionsManager::class.java)),
+            requireNotNull(car.getCarManager(CarPackageManager::class.java)),
+            requireContext().getSystemService(MediaSessionManager::class.java),
+            requireContext().applicationContext.resources,
+            Default
+        )
+        val appOrderDataSource: AppOrderDataSource = AppOrderProtoDataSourceImpl(
+            LauncherItemListSource(requireContext().filesDir, "order.data"),
+            IO
+        )
+        val packageManager: PackageManager = requireContext().packageManager
+        val launchProviderFactory = AppLaunchProviderFactory(
+            requireNotNull(car.getCarManager(CarMediaManager::class.java)),
+            mode.openMediaCenter,
+            {
+                activity?.finish()
+            },
+            requireContext().packageManager
+        )
+
+        val appShortcutsFactory = AppShortcutsFactory(
+            requireNotNull(car.getCarManager(CarMediaManager::class.java)),
+            emptySet(),
+            object : ShortcutsListener {
+                override fun onShortcutsShow(carUiShortcutsPopup: CarUiShortcutsPopup) {
+                    [email protected] = carUiShortcutsPopup
+                }
+            }
+        )
+        val bgDispatcher = Default
+        val repo: AppGridRepository = AppGridRepositoryImpl(
+            launcherActivities, mediaTemplateApps,
+            disabledApps, tosApps, controlCenterMirroringDataSource, uxRestrictionDataSource,
+            appOrderDataSource, packageManager, launchProviderFactory, appShortcutsFactory,
+            requireContext().getSystemService(UserManager::class.java), bgDispatcher
+        )
+
+        appGridViewModel = ViewModelProvider(
+            this,
+            provideFactory(repo, requireActivity().application, this, null)
+        )[AppGridViewModel::class.java]
+    }
+
+    private fun animateDropEnded(dragSurface: SurfaceControl?) {
+        if (dragSurface == null) {
+            if (DEBUG_BUILD) {
+                Log.d(TAG, "animateDropEnded, dragSurface unavailable")
+            }
+            return
+        }
+        // update default animation for the drag shadow after user lifts their finger
+        val txn = SurfaceControl.Transaction()
+        // set an animator to animate a delay before clearing the dragSurface
+        val delayedDismissAnimator = ValueAnimator.ofFloat(0f, 1f)
+        delayedDismissAnimator.startDelay =
+            resources.getInteger(R.integer.ms_drop_animation_delay).toLong()
+        delayedDismissAnimator.addUpdateListener {
+            txn.setAlpha(dragSurface, 0f)
+            txn.apply()
+        }
+        delayedDismissAnimator.start()
+    }
+
+    private fun setupTosBanner() {
+        appGridViewModel.getShouldShowTosBanner().asLiveData()
+            .observe(
+                viewLifecycleOwner
+            ) { showBanner: Boolean ->
+                if (showBanner) {
+                    banner.visibility = View.VISIBLE
+                    // Pre draw is required for animation to work.
+                    banner.viewTreeObserver.addOnPreDrawListener(
+                        object : ViewTreeObserver.OnPreDrawListener {
+                            override fun onPreDraw(): Boolean {
+                                banner.viewTreeObserver.removeOnPreDrawListener(this)
+                                backgroundAnimationHelper.showBanner()
+                                return true
+                            }
+                        }
+                    )
+                } else {
+                    banner.visibility = View.GONE
+                }
+            }
+        banner.setFirstButtonOnClickListener { v: View ->
+            val tosIntent =
+                AppLauncherUtils.getIntentForTosAcceptanceFlow(v.context)
+            AppLauncherUtils.launchApp(v.context, tosIntent)
+        }
+        banner.setSecondButtonOnClickListener { _ ->
+            backgroundAnimationHelper.hideBanner()
+            appGridViewModel.saveTosBannerDismissalTime()
+        }
+    }
+
+    /**
+     * Updates the scroll state after receiving data changes, such as new apps being added or
+     * reordered, and when user returns to launcher onResume.
+     *
+     * Additionally, notify page indicator to handle resizing in case new app addition creates a
+     * new page or deleted a page.
+     */
+    fun updateScrollState() {
+        // TODO(b/271637411): move this method into a scroll controller
+        // to calculate how many pages we need to offset, we use the scroll offset anchor position
+        // as item count and map to the page which the anchor is on.
+        val offsetPageCount = adapter.getPageCount(nextScrollDestination + 1) - 1
+        appGridRecyclerView.suppressLayout(false)
+        currentScrollOffset =
+            offsetPageCount * if (isHorizontal(pageOrientation)) {
+                appGridWidth + 2 * appGridMarginHorizontal
+            } else {
+                appGridHeight + 2 * appGridMarginVertical
+            }
+        layoutManager.scrollToPositionWithOffset(offsetPageCount * numOfRows * numOfCols, 0)
+        pageIndicator.updateOffset(currentScrollOffset)
+        pageIndicator.updatePageCount(adapter.pageCount)
+    }
+
+    /**
+     * Change the mode of the apps shown in the AppGrid
+     * @see [Mode]
+     */
+    fun updateMode(mode: Mode) {
+        this.mode = mode
+        appGridViewModel.updateMode(mode)
+    }
+
+    private inner class AppGridOnScrollListener : RecyclerView.OnScrollListener() {
+        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+            currentScrollOffset += if (isHorizontal(pageOrientation)) dx else dy
+            pageIndicator.updateOffset(currentScrollOffset)
+        }
+
+        override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+            currentScrollState = newState
+            snapCallback.scrollState = currentScrollState
+            when (newState) {
+                RecyclerView.SCROLL_STATE_DRAGGING -> {
+                    if (!isCurrentlyDragging) {
+                        dragCallback.cancelDragTasks()
+                    }
+                    dismissShortcutPopup()
+                    pageIndicator.animateAppearance()
+                }
+
+                RecyclerView.SCROLL_STATE_SETTLING -> pageIndicator.animateAppearance()
+                RecyclerView.SCROLL_STATE_IDLE -> {
+                    if (isCurrentlyDragging) {
+                        layoutManager.setShouldLayoutChildren(false)
+                    }
+                    pageIndicator.animateFading()
+                    // in case the recyclerview was scrolled by rotary input, we need to handle
+                    // focusing the correct element: either on the first or last element on page
+                    appGridRecyclerView.maybeHandleRotaryFocus()
+                }
+            }
+        }
+    }
+
+    private fun dismissShortcutPopup() {
+        carUiShortcutsPopup?.let {
+            it.dismiss()
+            carUiShortcutsPopup = null
+        }
+    }
+
+    override fun onPause() {
+        dismissShortcutPopup()
+        super.onPause()
+    }
+
+    override fun onDestroy() {
+        if (car.isConnected) {
+            car.disconnect()
+        }
+        super.onDestroy()
+    }
+
+    override fun onSnapToPosition(gridPosition: Int) {
+        nextScrollDestination = gridPosition
+    }
+
+    override fun onDimensionsUpdated(
+        pageDimens: PageMeasurementHelper.PageDimensions,
+        gridDimens: PageMeasurementHelper.GridDimensions
+    ) {
+        // TODO(b/271637411): move this method into a scroll controller
+        appGridMarginHorizontal = pageDimens.marginHorizontalPx
+        appGridMarginVertical = pageDimens.marginVerticalPx
+        appGridWidth = gridDimens.gridWidthPx
+        appGridHeight = gridDimens.gridHeightPx
+    }
+
+    override fun onAppPositionChanged(newPosition: Int, appItem: AppItem) {
+        appGridViewModel.saveAppOrder(newPosition, appItem)
+    }
+
+    override fun onItemLongPressed(longPressed: Boolean) {
+        // after the user long presses the app icon, scrolling should be disabled until long press
+        // is canceled as to allow MotionEvent to be interpreted as attempt to drag the app icon.
+        appGridRecyclerView.suppressLayout(longPressed)
+    }
+
+    override fun onItemSelected(gridPositionFrom: Int) {
+        isCurrentlyDragging = true
+        layoutManager.setShouldLayoutChildren(false)
+        adapter.setDragStartPoint(gridPositionFrom)
+        dismissShortcutPopup()
+    }
+
+    override fun onItemDragged() {
+        appGridDragController.cancelDelayedPageFling()
+    }
+
+    override fun onDragExited(gridPosition: Int, exitDirection: Int) {
+        if (adapter.getOffsetBoundDirection(gridPosition) == exitDirection) {
+            appGridDragController.postDelayedPageFling(exitDirection)
+        }
+    }
+
+    override fun onItemDropped(gridPositionFrom: Int, gridPositionTo: Int) {
+        layoutManager.setShouldLayoutChildren(true)
+        adapter.moveAppItem(gridPositionFrom, gridPositionTo)
+    }
+
+    private inner class AppGridDragController() {
+        // TODO: (b/271320404) move DragController to separate directory called dragndrop and
+        // migrate logic this class and AppItemViewHolder there.
+        private val handler: Handler = Handler(getMainLooper())
+
+        fun cancelDelayedPageFling() {
+            handler.removeCallbacksAndMessages(null)
+        }
+
+        fun postDelayedPageFling(@AppItemBoundDirection exitDirection: Int) {
+            val scrollToNextPage =
+                if (isHorizontal(pageOrientation)) {
+                    exitDirection == AppItemBoundDirection.RIGHT
+                } else {
+                    exitDirection == AppItemBoundDirection.BOTTOM
+                }
+            handler.removeCallbacksAndMessages(null)
+            handler.postDelayed({
+                if (currentScrollState == RecyclerView.SCROLL_STATE_IDLE) {
+                    adapter.updatePageScrollDestination(scrollToNextPage)
+                    nextScrollDestination = snapCallback.snapPosition
+                    layoutManager.setShouldLayoutChildren(true)
+                    appGridRecyclerView.smoothScrollToPosition(nextScrollDestination)
+                }
+                // another delayed scroll will be queued to enable the user to input multiple
+                // page scrolls by holding the recyclerview at the app grid margin
+                postDelayedPageFling(exitDirection)
+            }, offPageHoverBeforeScrollMs)
+        }
+    }
+
+    /**
+     * Private onDragListener for handling dispatching off page scroll event when user holds the app
+     * icon at the page margin.
+     */
+    private inner class AppGridDragListener : View.OnDragListener {
+        override fun onDrag(v: View, event: DragEvent): Boolean {
+            val action = event.action
+            if (action == DragEvent.ACTION_DROP || action == DragEvent.ACTION_DRAG_ENDED) {
+                isCurrentlyDragging = false
+                appGridDragController.cancelDelayedPageFling()
+                dragCallback.resetCallbackState()
+                layoutManager.setShouldLayoutChildren(true)
+                if (action == DragEvent.ACTION_DROP) {
+                    return false
+                } else {
+                    animateDropEnded(HiddenApiAccess.getDragSurface(event))
+                }
+            }
+            return true
+        }
+    }
+
+    annotation class AppTypes {
+        companion object {
+            const val APP_TYPE_LAUNCHABLES = 1
+            const val APP_TYPE_MEDIA_SERVICES = 2
+        }
+    }
+
+    enum class Mode(
+        @field:StringRes @param:StringRes val titleStringId: Int,
+        @field:AppTypes @param:AppTypes val appTypes: Int,
+        val openMediaCenter: Boolean
+    ) {
+        ALL_APPS(
+            R.string.app_launcher_title_all_apps,
+            APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
+            true
+        ),
+        MEDIA_ONLY(
+            R.string.app_launcher_title_media_only,
+            APP_TYPE_MEDIA_SERVICES,
+            true
+        ),
+        MEDIA_POPUP(
+            R.string.app_launcher_title_media_only,
+            APP_TYPE_MEDIA_SERVICES,
+            false
+        )
+    }
+
+    companion object {
+        const val TAG = "AppGridFragment"
+        const val DEBUG_BUILD = false
+        const val MODE_INTENT_EXTRA = "com.android.car.carlauncher.mode"
+
+        @JvmStatic
+        fun newInstance(mode: Mode): AppGridFragment {
+            return AppGridFragment().apply {
+                arguments = Bundle().apply {
+                    putString(MODE_INTENT_EXTRA, mode.name)
+                }
+            }
+        }
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridViewModel.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridViewModel.kt
new file mode 100644
index 0000000..3787111
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppGridViewModel.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2024 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.carlauncher
+
+import android.app.Application
+import android.os.Bundle
+import android.os.SystemClock
+import androidx.lifecycle.AbstractSavedStateViewModelFactory
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.SavedStateHandle
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import androidx.preference.PreferenceManager
+import androidx.savedstate.SavedStateRegistryOwner
+import com.android.car.carlauncher.AppGridFragment.AppTypes.Companion.APP_TYPE_LAUNCHABLES
+import com.android.car.carlauncher.AppGridFragment.Mode
+import com.android.car.carlauncher.repositories.AppGridRepository
+import java.time.Clock
+import java.util.concurrent.TimeUnit
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.mapLatest
+import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.transformLatest
+import kotlinx.coroutines.launch
+
+/**
+ * This ViewModel manages the main application grid within the car launcher. It provides
+ * methods to retrieve app lists, handle app reordering, determine distraction
+ * optimization requirements, and manage the Terms of Service (TOS) banner display.
+ */
+class AppGridViewModel(
+    private val appGridRepository: AppGridRepository,
+    private val application: Application
+) : AndroidViewModel(application) {
+
+    /**
+     * A Kotlin Flow containing a complete list of applications obtained from the repository.
+     * This Flow is shared for efficiency within the ViewModel.
+     */
+    private val allAppsItemList = appGridRepository.getAllAppsList()
+        .shareIn(viewModelScope, SharingStarted.WhileSubscribed(STOP_TIME_OUT_FLOW_SUBSCRIPTION), 1)
+
+    /**
+     * A Kotlin Flow containing a list of media-focused applications obtained from the repository,
+     * shared for efficiency within the ViewModel.
+     */
+    private val mediaOnlyList = appGridRepository.getMediaAppsList()
+        .shareIn(viewModelScope, SharingStarted.WhileSubscribed(STOP_TIME_OUT_FLOW_SUBSCRIPTION), 1)
+
+    /**
+     * A MutableStateFlow indicating the current application display mode in the app grid.
+     */
+    private val appMode: MutableStateFlow<Mode> = MutableStateFlow(Mode.ALL_APPS)
+
+    /**
+     * Provides a Flow of application lists (AppItem). The returned Flow dynamically switches
+     * between the complete app list (`allAppsItemList`) and a filtered list
+     * of media apps (`mediaOnlyList`) based on the current `appMode`.
+     *
+     * @return A Flow of AppItem lists
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    fun getAppList(): Flow<List<AppItem>> {
+        return appMode.transformLatest {
+            val sourceList = if (it.appTypes and APP_TYPE_LAUNCHABLES == 1) {
+                allAppsItemList
+            } else {
+                mediaOnlyList
+            }
+            emitAll(sourceList)
+        }.distinctUntilChanged()
+    }
+
+    /**
+     * Updates the application order in the repository.
+     *
+     * @param newPosition The intended new index position for the app.
+     * @param appItem The AppItem to be repositioned.
+     */
+    fun saveAppOrder(newPosition: Int, appItem: AppItem) {
+        viewModelScope.launch {
+            allAppsItemList.replayCache.lastOrNull()?.toMutableList()?.apply {
+                // Remove original occurrence
+                remove(appItem)
+                // Add to new position
+                add(newPosition, appItem)
+            }?.let {
+                appGridRepository.saveAppOrder(it)
+            }
+        }
+    }
+
+    /**
+     * Provides a flow indicating whether distraction optimization should be applied
+     * in the car launcher UI.
+     *
+     * @return A Flow emitting Boolean values where 'true' signifies a need for distraction optimization.
+     */
+    fun requiresDistractionOptimization(): Flow<Boolean> {
+        return appGridRepository.requiresDistractionOptimization()
+    }
+
+    /**
+     * Returns a flow that determines whether the Terms of Service (TOS) banner should be displayed.
+     * The logic considers if the TOS requires acceptance and the banner resurfacing interval.
+     *
+     * @return A Flow emitting Boolean values where 'true' indicates the banner should be displayed.
+     */
+    @OptIn(ExperimentalCoroutinesApi::class)
+    fun getShouldShowTosBanner(): Flow<Boolean> {
+        return appGridRepository.getTosState().mapLatest {
+            if (!it.shouldBlockTosApps) {
+                return@mapLatest false
+            }
+            return@mapLatest shouldShowTos()
+        }
+    }
+
+    /**
+     * Checks if we need to show the Banner based when it was previously dismissed.
+     */
+    private fun shouldShowTos(): Boolean {
+        // Convert days to seconds
+        val bannerResurfaceTimeInSeconds = TimeUnit.DAYS.toSeconds(
+            application.resources
+                .getInteger(R.integer.config_tos_banner_resurface_time_days).toLong()
+        )
+        val bannerDismissTime = PreferenceManager.getDefaultSharedPreferences(application)
+            .getLong(TOS_BANNER_DISMISS_TIME_KEY, 0)
+
+        val systemBootTime = Clock.systemUTC()
+            .instant().epochSecond - TimeUnit.MILLISECONDS.toSeconds(SystemClock.elapsedRealtime())
+        // Show on next drive / reboot, when banner has not been dismissed in current session
+        return if (bannerResurfaceTimeInSeconds == 0L) {
+            // If banner is dismissed in current drive session, it will have a timestamp greater
+            // than the system boot time timestamp.
+            bannerDismissTime < systemBootTime
+        } else {
+            Clock.systemUTC()
+            .instant().epochSecond - bannerDismissTime > bannerResurfaceTimeInSeconds
+        }
+    }
+
+    /**
+     * Saves the current timestamp to Preferences, marking the time when the Terms of Service (TOS)
+     * banner was dismissed by the user.
+     */
+    fun saveTosBannerDismissalTime() {
+        val dismissTime: Long = Clock.systemUTC().instant().epochSecond
+        PreferenceManager.getDefaultSharedPreferences(application)
+            .edit().putLong(TOS_BANNER_DISMISS_TIME_KEY, dismissTime).apply()
+    }
+
+    /**
+     * Updates the current application display mode. This triggers UI updates in the app grid.
+     * @param mode The new Mode to set for the application grid.
+     */
+    fun updateMode(mode: Mode) {
+        appMode.value = mode
+    }
+
+    companion object {
+        const val TOS_BANNER_DISMISS_TIME_KEY = "TOS_BANNER_DISMISS_TIME"
+        const val STOP_TIME_OUT_FLOW_SUBSCRIPTION = 5_000L
+        fun provideFactory(
+            myRepository: AppGridRepository,
+            application: Application,
+            owner: SavedStateRegistryOwner,
+            defaultArgs: Bundle? = null,
+        ): AbstractSavedStateViewModelFactory =
+            object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
+                override fun <T : ViewModel> create(
+                    key: String,
+                    modelClass: Class<T>,
+                    handle: SavedStateHandle
+                ): T {
+                    return AppGridViewModel(myRepository, application) as T
+                }
+            }
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppItemDragShadowBuilder.java b/libs/appgrid/lib/src/com/android/car/carlauncher/AppItemDragShadowBuilder.java
index 1ea40e2..8f2a4e4 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/AppItemDragShadowBuilder.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppItemDragShadowBuilder.java
@@ -18,24 +18,28 @@
 
 import android.graphics.Canvas;
 import android.graphics.Point;
+import android.graphics.drawable.Drawable;
 import android.view.View;
 
+import androidx.annotation.NonNull;
+
 /**
  * Custom View.DragShadowBuilder that handles the drawing and deploying of drag shadow when an
  * app icon is long pressed and dragged.
  */
 public class AppItemDragShadowBuilder extends View.DragShadowBuilder{
-    private final View mAppIcon;
-    private final int mSize;
+    private final Drawable mIcon;
     private final int mScaledSize;
     private final float mTouchPointX;
     private final float mTouchPointY;
 
-    public AppItemDragShadowBuilder(View view, float touchPointX, float touchPointY,
-            int size, int scaledSize) {
-        super(view);
-        mAppIcon = view;
-        mSize = size;
+    /**
+     * @param icon Drawable to be drawn as the drag shadow to represent the view being dragged.
+     */
+    public AppItemDragShadowBuilder(Drawable icon, float touchPointX, float touchPointY,
+            int scaledSize) {
+        super();
+        mIcon = icon;
         mScaledSize = scaledSize;
         mTouchPointX = touchPointX;
         mTouchPointY = touchPointY;
@@ -43,16 +47,13 @@
 
     @Override
     public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
-        if (mAppIcon != null) {
-            outShadowSize.set(mScaledSize, mScaledSize);
-            outShadowTouchPoint.set((int) mTouchPointX, (int) mTouchPointY);
-        }
+        outShadowSize.set(mScaledSize, mScaledSize);
+        outShadowTouchPoint.set((int) mTouchPointX, (int) mTouchPointY);
     }
 
     @Override
-    public void onDrawShadow(Canvas canvas) {
-        canvas.scale(/* scaleX */ mScaledSize / (float) mSize,
-                /* scaleY */  mScaledSize / (float) mSize);
-        getView().draw(canvas);
+    public void onDrawShadow(@NonNull Canvas canvas) {
+        mIcon.setBounds(0, 0, mScaledSize, mScaledSize);
+        mIcon.draw(canvas);
     }
 }
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java b/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
index dcf7c93..a597ce0 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -16,104 +16,41 @@
 
 package com.android.car.carlauncher;
 
-import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
 import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
 import static android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED;
 
-import static com.android.car.carlauncher.hidden.HiddenApiAccess.hasBaseUserRestriction;
+import static com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.TOS_DISABLED_APPS_SEPARATOR;
+import static com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.TOS_NOT_ACCEPTED;
+import static com.android.car.carlauncher.datasources.restricted.TosDataSourceImpl.TOS_UNINITIALIZED;
 
-import static java.lang.annotation.RetentionPolicy.SOURCE;
-
-import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.admin.DevicePolicyManager;
-import android.car.Car;
-import android.car.CarNotConnectedException;
-import android.car.content.pm.CarPackageManager;
-import android.car.media.CarMediaManager;
-import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
 import android.os.Process;
 import android.os.UserHandle;
-import android.os.UserManager;
 import android.provider.Settings;
-import android.service.media.MediaBrowserService;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.Pair;
-import android.view.View;
 
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
-import com.android.car.dockutil.events.DockEventSenderHelper;
-import com.android.car.dockutil.shortcuts.PinShortcutItem;
-import com.android.car.media.common.source.MediaSourceUtil;
-import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup;
-
-import com.google.common.collect.Sets;
-
-import java.lang.annotation.Retention;
 import java.net.URISyntaxException;
-import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Consumer;
 
 /**
  * Util class that contains helper method used by app launcher classes.
  */
 public class AppLauncherUtils {
     private static final String TAG = "AppLauncherUtils";
-    private static final String ANDROIDX_CAR_APP_LAUNCHABLE = "androidx.car.app.launchable";
-
-    @Retention(SOURCE)
-    @IntDef({APP_TYPE_LAUNCHABLES, APP_TYPE_MEDIA_SERVICES})
-    @interface AppTypes {}
-
-    static final int APP_TYPE_LAUNCHABLES = 1;
-    static final int APP_TYPE_MEDIA_SERVICES = 2;
-
-    // This value indicates if TOS has not been accepted by the user
-    private static final String TOS_NOT_ACCEPTED = "1";
-    // This value indicates if TOS is in uninitialized state
-    private static final String TOS_UNINITIALIZED = "0";
-    static final String TOS_DISABLED_APPS_SEPARATOR = ",";
-    static final String PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR = ";";
-
-    // Max no. of uses tags in automotiveApp XML. This is an arbitrary limit to be defensive
-    // to bad input.
-    private static final int MAX_APP_TYPES = 64;
-    private static final String PACKAGE_URI_PREFIX = "package:";
 
     private AppLauncherUtils() {
     }
 
     /**
-     * Comparator for {@link AppMetaData} that sorts the list
-     * by the "displayName" property in ascending order.
-     */
-    static final Comparator<AppMetaData> ALPHABETICAL_COMPARATOR = Comparator
-            .comparing(AppMetaData::getDisplayName, String::compareToIgnoreCase);
-
-    /**
      * Helper method that launches the app given the app's AppMetaData.
      */
     public static void launchApp(Context context, Intent intent) {
@@ -122,285 +59,6 @@
         context.startActivity(intent, options.toBundle());
     }
 
-    /** Bundles application and services info. */
-    static class LauncherAppsInfo {
-        /*
-         * Map of all car launcher components' (including launcher activities and media services)
-         * metadata keyed by ComponentName.
-         */
-        private final Map<ComponentName, AppMetaData> mLaunchables;
-
-        /** Map of all the media services keyed by ComponentName. */
-        private final Map<ComponentName, ResolveInfo> mMediaServices;
-
-        LauncherAppsInfo(@NonNull Map<ComponentName, AppMetaData> launchablesMap,
-                @NonNull Map<ComponentName, ResolveInfo> mediaServices) {
-            mLaunchables = launchablesMap;
-            mMediaServices = mediaServices;
-        }
-
-        /** Returns true if all maps are empty. */
-        boolean isEmpty() {
-            return mLaunchables.isEmpty() && mMediaServices.isEmpty();
-        }
-
-        /**
-         * Returns whether the given componentName is a media service.
-         */
-        boolean isMediaService(ComponentName componentName) {
-            return mMediaServices.containsKey(componentName);
-        }
-
-        /** Returns the {@link AppMetaData} for the given componentName. */
-        @Nullable
-        AppMetaData getAppMetaData(ComponentName componentName) {
-            return mLaunchables.get(componentName);
-        }
-
-        /** Returns a new list of all launchable components' {@link AppMetaData}. */
-        @NonNull
-        List<AppMetaData> getLaunchableComponentsList() {
-            return new ArrayList<>(mLaunchables.values());
-        }
-
-        /** Returns list of Media Services for the launcher **/
-        @NonNull
-        Map<ComponentName, ResolveInfo> getMediaServices() {
-            return mMediaServices;
-        }
-    }
-
-    private static final LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
-            Collections.emptyMap(), Collections.emptyMap());
-
-    /*
-     * Gets the media source in a given package. If there are multiple sources in the package,
-     * returns the first one.
-     */
-    static ComponentName getMediaSource(@NonNull PackageManager packageManager,
-            @NonNull String packageName) {
-        Intent mediaIntent = new Intent();
-        mediaIntent.setPackage(packageName);
-        mediaIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
-        List<ResolveInfo> mediaServices = packageManager.queryIntentServices(mediaIntent,
-                PackageManager.GET_RESOLVED_FILTER);
-
-        if (mediaServices == null || mediaServices.isEmpty()) {
-            return null;
-        }
-        String defaultService = mediaServices.get(0).serviceInfo.name;
-        if (!TextUtils.isEmpty(defaultService)) {
-            return new ComponentName(packageName, defaultService);
-        }
-        return null;
-    }
-
-    /**
-     * Gets all the components that we want to see in the launcher in unsorted order, including
-     * launcher activities and media services.
-     *
-     * @param appsToHide            A (possibly empty) list of apps (package names) to hide
-     * @param appTypes              Types of apps to show (e.g.: all, or media sources only)
-     * @param openMediaCenter       Whether launcher should navigate to media center when the
-     *                              user selects a media source.
-     * @param launcherApps          The {@link LauncherApps} system service
-     * @param carPackageManager     The {@link CarPackageManager} system service
-     * @param packageManager        The {@link PackageManager} system service
-     *                              of such apps are always excluded.
-     * @param carMediaManager       The {@link CarMediaManager} system service
-     * @return a new {@link LauncherAppsInfo}
-     */
-    @NonNull
-    static LauncherAppsInfo getLauncherApps(
-            Context context,
-            @NonNull Set<String> appsToHide,
-            @AppTypes int appTypes,
-            boolean openMediaCenter,
-            LauncherApps launcherApps,
-            CarPackageManager carPackageManager,
-            PackageManager packageManager,
-            CarMediaManager carMediaManager,
-            ShortcutsListener shortcutsListener,
-            String mirroringAppPkgName,
-            Intent mirroringAppRedirect) {
-
-        if (launcherApps == null || carPackageManager == null || packageManager == null
-                || carMediaManager == null) {
-            return EMPTY_APPS_INFO;
-        }
-
-        boolean isDockEnabled = context.getResources().getBoolean(R.bool.config_enableDock);
-
-        // Using new list since we require a mutable list to do removeIf.
-        List<ResolveInfo> mediaServices = new ArrayList<>();
-        mediaServices.addAll(
-                packageManager.queryIntentServices(
-                        new Intent(MediaBrowserService.SERVICE_INTERFACE),
-                        PackageManager.GET_RESOLVED_FILTER));
-
-        List<LauncherActivityInfo> availableActivities =
-                launcherApps.getActivityList(null, Process.myUserHandle());
-
-        int launchablesSize = mediaServices.size() + availableActivities.size();
-        Map<ComponentName, AppMetaData> launchablesMap = new HashMap<>(launchablesSize);
-        Map<ComponentName, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
-        Set<String> mEnabledPackages = new ArraySet<>(launchablesSize);
-        Set<String> tosDisabledPackages = getTosDisabledPackages(context);
-
-        Set<String> customMediaComponents = Sets.newHashSet(
-                context.getResources().getStringArray(
-                        com.android.car.media.common.R.array.custom_media_packages));
-
-        // Process media services
-        if ((appTypes & APP_TYPE_MEDIA_SERVICES) != 0) {
-            for (ResolveInfo info : mediaServices) {
-                String packageName = info.serviceInfo.packageName;
-                String className = info.serviceInfo.name;
-                ComponentName componentName = new ComponentName(packageName, className);
-                mediaServicesMap.put(componentName, info);
-                mEnabledPackages.add(packageName);
-                if (shouldAddToLaunchables(context, componentName, appsToHide,
-                        customMediaComponents, appTypes, APP_TYPE_MEDIA_SERVICES)) {
-                    CharSequence displayName = info.serviceInfo.loadLabel(packageManager);
-                    AppMetaData appMetaData = new AppMetaData(
-                            displayName,
-                            componentName,
-                            info.serviceInfo.loadIcon(packageManager),
-                            /* isDistractionOptimized= */ true,
-                            /* isMirroring = */ false,
-                            /* isDisabledByTos= */ tosDisabledPackages.contains(packageName),
-                            contextArg -> {
-                                if (openMediaCenter) {
-                                    AppLauncherUtils.launchApp(contextArg,
-                                            createMediaLaunchIntent(componentName));
-                                } else {
-                                    selectMediaSourceAndFinish(contextArg, componentName,
-                                            carMediaManager);
-                                }
-                            },
-                            buildShortcuts(componentName, displayName, shortcutsListener,
-                                    isDockEnabled));
-                    launchablesMap.put(componentName, appMetaData);
-                }
-            }
-        }
-
-        // Process activities
-        if ((appTypes & APP_TYPE_LAUNCHABLES) != 0) {
-            for (LauncherActivityInfo info : availableActivities) {
-                ComponentName componentName = info.getComponentName();
-                mEnabledPackages.add(componentName.getPackageName());
-                if (shouldAddToLaunchables(context, componentName, appsToHide,
-                        customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
-                    boolean isDistractionOptimized =
-                            isActivityDistractionOptimized(carPackageManager,
-                                    componentName.getPackageName(), info.getName());
-                    boolean isDisabledByTos = tosDisabledPackages
-                            .contains(componentName.getPackageName());
-
-                    CharSequence displayName = info.getLabel();
-                    boolean isMirroring = componentName.getPackageName()
-                            .equals(mirroringAppPkgName);
-                    AppMetaData appMetaData = new AppMetaData(
-                            displayName,
-                            componentName,
-                            info.getBadgedIcon(0),
-                            isDistractionOptimized,
-                            isMirroring,
-                            isDisabledByTos,
-                            contextArg -> {
-                                if (componentName.getPackageName().equals(mirroringAppPkgName)) {
-                                    Log.d(TAG, "non-media service package name "
-                                            + "equals mirroring pkg name");
-                                }
-                                AppLauncherUtils.launchApp(contextArg,
-                                        isMirroring ? mirroringAppRedirect :
-                                                createAppLaunchIntent(componentName));
-                            },
-                            buildShortcuts(componentName, displayName, shortcutsListener,
-                                    isDockEnabled));
-                    launchablesMap.put(componentName, appMetaData);
-                }
-            }
-
-            List<ResolveInfo> disabledActivities = getDisabledActivities(context, packageManager,
-                    mEnabledPackages);
-            for (ResolveInfo info : disabledActivities) {
-                String packageName = info.activityInfo.packageName;
-                String className = info.activityInfo.name;
-                ComponentName componentName = new ComponentName(packageName, className);
-                if (!shouldAddToLaunchables(context, componentName, appsToHide,
-                        customMediaComponents, appTypes, APP_TYPE_LAUNCHABLES)) {
-                    continue;
-                }
-                boolean isDistractionOptimized =
-                        isActivityDistractionOptimized(carPackageManager, packageName, className);
-                boolean isDisabledByTos = tosDisabledPackages.contains(packageName);
-
-                CharSequence displayName = info.activityInfo.loadLabel(packageManager);
-                AppMetaData appMetaData = new AppMetaData(
-                        displayName,
-                        componentName,
-                        info.activityInfo.loadIcon(packageManager),
-                        isDistractionOptimized,
-                        /* isMirroring = */ false,
-                        isDisabledByTos,
-                        contextArg -> {
-                            packageManager.setApplicationEnabledSetting(packageName,
-                                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0);
-                            // Fetch the current enabled setting to make sure the setting is synced
-                            // before launching the activity. Otherwise, the activity may not
-                            // launch.
-                            if (packageManager.getApplicationEnabledSetting(packageName)
-                                    != PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
-                                throw new IllegalStateException(
-                                        "Failed to enable the disabled package [" + packageName
-                                                + "]");
-                            }
-                            Log.i(TAG, "Successfully enabled package [" + packageName + "]");
-                            AppLauncherUtils.launchApp(contextArg,
-                                    createAppLaunchIntent(componentName));
-                        },
-                        buildShortcuts(componentName, displayName, shortcutsListener,
-                                isDockEnabled));
-                launchablesMap.put(componentName, appMetaData);
-            }
-
-            List<ResolveInfo> restrictedActivities = getTosDisabledActivities(
-                    context,
-                    packageManager,
-                    mEnabledPackages
-            );
-            for (ResolveInfo info: restrictedActivities) {
-                String packageName = info.activityInfo.packageName;
-                String className = info.activityInfo.name;
-                ComponentName componentName = new ComponentName(packageName, className);
-
-                boolean isDistractionOptimized =
-                        isActivityDistractionOptimized(carPackageManager, packageName, className);
-                boolean isDisabledByTos = tosDisabledPackages.contains(packageName);
-
-                AppMetaData appMetaData = new AppMetaData(
-                        info.activityInfo.loadLabel(packageManager),
-                        componentName,
-                        info.activityInfo.loadIcon(packageManager),
-                        isDistractionOptimized,
-                        /* isMirroring = */ false,
-                        isDisabledByTos,
-                        contextArg -> {
-                            Intent tosIntent = getIntentForTosAcceptanceFlow(contextArg);
-                            launchApp(contextArg, tosIntent);
-                        },
-                        null
-                );
-                launchablesMap.put(componentName, appMetaData);
-            }
-        }
-
-        return new LauncherAppsInfo(launchablesMap, mediaServicesMap);
-    }
-
     /**
      * Gets the intent for launching the TOS acceptance flow
      *
@@ -419,357 +77,6 @@
         }
     }
 
-    private static Consumer<Pair<Context, View>> buildShortcuts(
-            ComponentName componentName, CharSequence displayName,
-            ShortcutsListener shortcutsListener, boolean isDockEnabled) {
-        return pair -> {
-            CarUiShortcutsPopup.Builder carUiShortcutsPopupBuilder =
-                    new CarUiShortcutsPopup.Builder()
-                            .addShortcut(buildForceStopShortcut(componentName.getPackageName(),
-                                    displayName, pair.first, shortcutsListener))
-                            .addShortcut(buildAppInfoShortcut(componentName.getPackageName(),
-                                    pair.first));
-            if (isDockEnabled) {
-                carUiShortcutsPopupBuilder
-                        .addShortcut(buildPinToDockShortcut(componentName, pair.first));
-            }
-            CarUiShortcutsPopup carUiShortcutsPopup = carUiShortcutsPopupBuilder
-                    .build(pair.first, pair.second);
-
-            carUiShortcutsPopup.show();
-            shortcutsListener.onShortcutsShow(carUiShortcutsPopup);
-        };
-    }
-
-    private static CarUiShortcutsPopup.ShortcutItem buildForceStopShortcut(String packageName,
-            CharSequence displayName,
-            Context context,
-            ShortcutsListener shortcutsListener) {
-        return new CarUiShortcutsPopup.ShortcutItem() {
-            @Override
-            public CarUiShortcutsPopup.ItemData data() {
-                return new CarUiShortcutsPopup.ItemData(
-                        R.drawable.ic_force_stop_caution_icon,
-                        context.getResources().getString(
-                                R.string.app_launcher_stop_app_action));
-            }
-
-            @Override
-            public boolean onClick() {
-                shortcutsListener.onShortcutsItemClick(packageName, displayName,
-                        /* allowStopApp= */ true);
-                return true;
-            }
-
-            @Override
-            public boolean isEnabled() {
-                return shouldAllowStopApp(packageName, context);
-            }
-        };
-    }
-
-    private static CarUiShortcutsPopup.ShortcutItem buildAppInfoShortcut(String packageName,
-            Context context) {
-        return new CarUiShortcutsPopup.ShortcutItem() {
-            @Override
-            public CarUiShortcutsPopup.ItemData data() {
-                return new CarUiShortcutsPopup.ItemData(
-                        R.drawable.ic_app_info,
-                        context.getResources().getString(
-                                R.string.app_launcher_app_info_action));
-            }
-
-            @Override
-            public boolean onClick() {
-                Uri packageURI = Uri.parse(PACKAGE_URI_PREFIX + packageName);
-                Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
-                        packageURI);
-                context.startActivity(intent);
-                return true;
-            }
-
-            @Override
-            public boolean isEnabled() {
-                return true;
-            }
-        };
-    }
-
-    private static CarUiShortcutsPopup.ShortcutItem buildPinToDockShortcut(
-            ComponentName componentName, Context context) {
-        DockEventSenderHelper mHelper = new DockEventSenderHelper(context);
-        return new PinShortcutItem(context.getResources(), /* isItemPinned= */ false,
-                /* pinItemClickDelegate= */ () -> mHelper.sendPinEvent(componentName),
-                /* unpinItemClickDelegate= */ () -> mHelper.sendUnpinEvent(componentName)
-        );
-    }
-
-    /**
-     * Force stops an app
-     * <p>Note: Uses hidden apis<p/>
-     */
-    public static void forceStop(String packageName, Context context, CharSequence displayName,
-            CarMediaManager carMediaManager, Map<ComponentName, ResolveInfo> mediaServices,
-            ShortcutsListener listener) {
-        ActivityManager activityManager = context.getSystemService(ActivityManager.class);
-        if (activityManager != null) {
-            maybeReplaceMediaSource(carMediaManager, packageName, mediaServices,
-                    CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
-            maybeReplaceMediaSource(carMediaManager, packageName, mediaServices,
-                    CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK);
-            activityManager.forceStopPackage(packageName);
-            String message = context.getResources()
-                    .getString(R.string.app_launcher_stop_app_success_toast_text, displayName);
-            listener.onStopAppSuccess(message);
-        }
-    }
-
-    private static boolean isCurrentMediaSource(CarMediaManager carMediaManager,
-            String packageName, int mode) {
-        ComponentName componentName = carMediaManager.getMediaSource(mode);
-        if (componentName == null) {
-            //There is no current media source.
-            return false;
-        }
-        return Objects.equals(componentName.getPackageName(), packageName);
-    }
-
-    /***
-     * Updates the MediaSource to second most recent if {@code  packageName} is current media source
-     * Sets to MediaSource to null if no previous MediaSource exists.
-     */
-    private static void maybeReplaceMediaSource(CarMediaManager carMediaManager, String packageName,
-            Map<ComponentName, ResolveInfo> allMediaServices,
-            int mode) {
-        if (!isCurrentMediaSource(carMediaManager, packageName, mode)) {
-            return;
-        }
-        //find the most recent source from history not equal to force-stopping package.
-        List<ComponentName> mediaSources = carMediaManager.getLastMediaSources(mode);
-        ComponentName componentName = mediaSources.stream().filter(c-> (!c.getPackageName()
-                .equals(packageName))).findFirst().orElse(null);
-        if (componentName == null) {
-            //no recent package found, find from all available media services.
-            componentName = allMediaServices.keySet().stream().filter(
-                    c -> (!c.getPackageName().equals(packageName))).findFirst().orElse(null);
-            if (componentName == null) {
-                Log.e(TAG, "Stop-app, no alternative media service found");
-            }
-        }
-        carMediaManager.setMediaSource(componentName, mode);
-    }
-
-    /**
-     * <p>Note: Uses hidden apis<p/>
-     * @return true if the user has restrictions to force stop an app with {@code appInfo}
-     */
-    private static boolean hasUserRestriction(ApplicationInfo appInfo, Context context) {
-        String restriction = UserManager.DISALLOW_APPS_CONTROL;
-        UserManager userManager = context.getSystemService(UserManager.class);
-        if (userManager == null) {
-            Log.e(TAG, " Disabled because , UserManager is null");
-            return true;
-        }
-        if (!userManager.hasUserRestriction(restriction)) {
-            return false;
-        }
-        UserHandle user = UserHandle.getUserHandleForUid(appInfo.uid);
-        if (hasBaseUserRestriction(userManager, restriction, user)) {
-            Log.d(TAG, " Disabled because " + user + " has " + restriction
-                    + " restriction");
-            return true;
-        }
-        // Not disabled for this User
-        return false;
-    }
-
-    /**
-     * <p>Note: uses hidden apis</p>
-     *
-     * @param packageName name of the package to stop the app
-     * @param context     app context
-     * @return true if an app should show the Stop app action
-     */
-    private static boolean shouldAllowStopApp(String packageName, Context context) {
-        DevicePolicyManager dm = context.getSystemService(DevicePolicyManager.class);
-        if (dm == null || dm.packageHasActiveAdmins(packageName)) {
-            return false;
-        }
-        try {
-            ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(packageName,
-                    PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA));
-            // Show only if the User has no restrictions to force stop this app
-            if (hasUserRestriction(appInfo, context)) {
-                return false;
-            }
-            // Show only if the app is running
-            if ((appInfo.flags & ApplicationInfo.FLAG_STOPPED) == 0) {
-                return true;
-            }
-        } catch (PackageManager.NameNotFoundException e) {
-            Log.d(TAG, "shouldAllowStopApp() Package " + packageName + " was not found");
-        }
-        return false;
-    }
-
-    private static List<ResolveInfo> getDisabledActivities(Context context,
-            PackageManager packageManager, Set<String> enabledPackages) {
-        return getActivitiesFromSystemPreferences(
-                context,
-                packageManager,
-                enabledPackages,
-                KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
-                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
-                PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR);
-    }
-
-    private static List<ResolveInfo> getTosDisabledActivities(
-            Context context,
-            PackageManager packageManager,
-            Set<String> enabledPackages) {
-        return getActivitiesFromSystemPreferences(
-                context,
-                packageManager,
-                enabledPackages,
-                KEY_UNACCEPTED_TOS_DISABLED_APPS,
-                PackageManager.MATCH_DISABLED_COMPONENTS,
-                TOS_DISABLED_APPS_SEPARATOR);
-    }
-
-    /**
-     * Get a list of activities from packages in system preferences by key
-     * @param context the app context
-     * @param packageManager The PackageManager
-     * @param enabledPackages Set of packages enabled by system
-     * @param settingsKey Key to read from system preferences
-     * @param sep Separator
-     *
-     * @return List of activities read from system preferences
-     */
-    private static List<ResolveInfo> getActivitiesFromSystemPreferences(
-            Context context,
-            PackageManager packageManager,
-            Set<String> enabledPackages,
-            String settingsKey,
-            int filter,
-            String sep) {
-        ContentResolver contentResolverForUser = context.createContextAsUser(
-                        UserHandle.getUserHandleForUid(Process.myUid()), /* flags= */ 0)
-                .getContentResolver();
-        String settingsValue = Settings.Secure.getString(contentResolverForUser, settingsKey);
-        Set<String> packages = TextUtils.isEmpty(settingsValue) ? new ArraySet<>()
-                : new ArraySet<>(Arrays.asList(settingsValue.split(
-                        sep)));
-
-        if (packages.isEmpty()) {
-            return Collections.emptyList();
-        }
-
-        List<ResolveInfo> allActivities = packageManager.queryIntentActivities(
-                new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
-                PackageManager.ResolveInfoFlags.of(PackageManager.GET_RESOLVED_FILTER
-                        | filter));
-
-        List<ResolveInfo> activities = new ArrayList<>();
-        for (int i = 0; i < allActivities.size(); ++i) {
-            ResolveInfo info = allActivities.get(i);
-            if (!enabledPackages.contains(info.activityInfo.packageName)
-                    && packages.contains(info.activityInfo.packageName)) {
-                activities.add(info);
-            }
-        }
-        return activities;
-    }
-
-    private static boolean shouldAddToLaunchables(Context context,
-            @NonNull ComponentName componentName,
-            @NonNull Set<String> appsToHide,
-            @NonNull Set<String> customMediaComponents,
-            @AppTypes int appTypesToShow,
-            @AppTypes int componentAppType) {
-        if (appsToHide.contains(componentName.getPackageName())) {
-            return false;
-        }
-        switch (componentAppType) {
-            // Process media services
-            case APP_TYPE_MEDIA_SERVICES:
-                // For a media service in customMediaComponents, if its application's launcher
-                // activity will be shown in the Launcher, don't show the service's icon in the
-                // Launcher.
-                if (customMediaComponents.contains(componentName.flattenToString())) {
-                    if ((appTypesToShow & APP_TYPE_LAUNCHABLES) != 0) {
-                        if (Log.isLoggable(TAG, Log.DEBUG)) {
-                            Log.d(TAG, "MBS for custom media app " + componentName
-                                    + " is skipped in app launcher");
-                        }
-                        return false;
-                    }
-                    // Media switcher use case should still show
-                    if (Log.isLoggable(TAG, Log.DEBUG)) {
-                        Log.d(TAG, "MBS for custom media app " + componentName
-                                + " is included in media switcher");
-                    }
-                    return true;
-                }
-                // Only Keep MBS that is a media template
-                return new MediaSourceUtil(context).isMediaTemplate(componentName);
-            // Process activities
-            case APP_TYPE_LAUNCHABLES:
-                return true;
-            default:
-                Log.e(TAG, "Invalid componentAppType : " + componentAppType);
-                return false;
-        }
-    }
-
-    private static void selectMediaSourceAndFinish(Context context, ComponentName componentName,
-            CarMediaManager carMediaManager) {
-        try {
-            carMediaManager.setMediaSource(componentName, CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
-            if (context instanceof Activity) {
-                ((Activity) context).finish();
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Car not connected", e);
-        }
-    }
-
-    /**
-     * Gets if an activity is distraction optimized.
-     *
-     * @param carPackageManager The {@link CarPackageManager} system service
-     * @param packageName       The package name of the app
-     * @param activityName      The requested activity name
-     * @return true if the supplied activity is distraction optimized
-     */
-    static boolean isActivityDistractionOptimized(
-            CarPackageManager carPackageManager, String packageName, String activityName) {
-        boolean isDistractionOptimized = false;
-        // try getting distraction optimization info
-        try {
-            if (carPackageManager != null) {
-                isDistractionOptimized =
-                        carPackageManager.isActivityDistractionOptimized(packageName, activityName);
-            }
-        } catch (CarNotConnectedException e) {
-            Log.e(TAG, "Car not connected when getting DO info", e);
-        }
-        return isDistractionOptimized;
-    }
-
-    /**
-     * Callback when a ShortcutsPopup View is shown
-     */
-    protected interface ShortcutsListener {
-
-        void onShortcutsShow(CarUiShortcutsPopup carUiShortcutsPopup);
-
-        void onShortcutsItemClick(String packageName, CharSequence displayName,
-                boolean allowStopApp);
-
-        void onStopAppSuccess(String message);
-    }
-
     /**
      * Returns a set of packages that are disabled by tos
      *
@@ -787,28 +94,6 @@
                         TOS_DISABLED_APPS_SEPARATOR)));
     }
 
-    private static Intent createMediaLaunchIntent(ComponentName componentName) {
-        return new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE)
-                .putExtra(Car.CAR_EXTRA_MEDIA_COMPONENT, componentName.flattenToString());
-    }
-
-    private static Intent createAppLaunchIntent(ComponentName componentName) {
-        return new Intent(Intent.ACTION_MAIN)
-                .setComponent(componentName)
-                .addCategory(Intent.CATEGORY_LAUNCHER)
-                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-    }
-
-    /**
-     * Check if the tos banner has to be displayed
-     * @param context The application context
-     * @return true if the banner needs to be displayed, false otherwise
-     */
-    static boolean showTosBanner(Context context) {
-        // TODO (b/277235742): Add backoff strategy to dismiss banner
-        return !tosAccepted(context);
-    }
-
     /**
      * Check if a user has accepted TOS
      *
@@ -829,7 +114,6 @@
      * Check if TOS status is uninitialized
      *
      * @param context The application context
-     *
      * @return true if tos is uninitialized, false otherwise
      */
     static boolean tosStatusUninitialized(Context context) {
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/AppMetaData.java b/libs/appgrid/lib/src/com/android/car/carlauncher/AppMetaData.java
index bd57b4e..1e65e59 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/AppMetaData.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/AppMetaData.java
@@ -127,7 +127,9 @@
             return false;
         } else {
             return ((AppMetaData) o).getComponentName().equals(mComponentName)
-                    && ((AppMetaData) o).getIsMirroring() == mIsMirroring;
+                    && ((AppMetaData) o).getIsMirroring() == mIsMirroring
+                    && ((AppMetaData) o).getIsDisabledByTos() == mIsDisabledByTos
+                    && ((AppMetaData) o).getIsDistractionOptimized() == mIsDistractionOptimized;
         }
     }
 
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/BackgroundAnimationHelper.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/BackgroundAnimationHelper.kt
new file mode 100644
index 0000000..23c834c
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/BackgroundAnimationHelper.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2024 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.carlauncher
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.AnimatorSet
+import android.animation.ObjectAnimator
+import android.animation.ValueAnimator
+import android.view.View
+import android.widget.LinearLayout
+
+/**
+ * Animation Helper for animating elements on the appGrid
+ */
+class BackgroundAnimationHelper(
+        private val appsGridBackground: LinearLayout,
+        private val banner: Banner
+) {
+    private var bannerAnimatorSet = AnimatorSet()
+
+    fun showBanner() {
+        banner.visibility = View.VISIBLE
+        val bannerTranslateAnimator = ValueAnimator.ofInt(-banner.measuredHeight, 0)
+        val bannerAlphaAnimator = ObjectAnimator.ofFloat(banner, "alpha", 1f)
+        val appsGridTranslateAnimator = ValueAnimator.ofInt(0, banner.measuredHeight)
+
+        animateBanner(bannerTranslateAnimator, bannerAlphaAnimator, appsGridTranslateAnimator)
+    }
+    fun hideBanner() {
+        val bannerTranslateAnimator = ValueAnimator.ofInt(0, -banner.measuredHeight)
+        val bannerAlphaAnimator = ObjectAnimator.ofFloat(banner, "alpha", 0f)
+        val appsGridTranslateAnimator = ValueAnimator.ofInt(banner.measuredHeight, 0)
+
+        bannerTranslateAnimator.addListener(object : AnimatorListenerAdapter() {
+            override fun onAnimationEnd(animation: Animator) {
+                super.onAnimationEnd(animation)
+                banner.visibility = View.GONE
+            }
+        })
+
+        animateBanner(bannerTranslateAnimator, bannerAlphaAnimator, appsGridTranslateAnimator)
+    }
+
+    private fun animateBanner(
+            bannerTranslateAnimator: ValueAnimator,
+            bannerAlphaAnimator: ObjectAnimator,
+            appsGridTranslateAnimator: ValueAnimator
+    ) {
+        bannerTranslateAnimator.addUpdateListener { banner.y = (it.animatedValue as Int).toFloat() }
+        appsGridTranslateAnimator
+            .addUpdateListener { appsGridBackground.y = (it.animatedValue as Int).toFloat() }
+
+        bannerAnimatorSet.cancel()
+        bannerAnimatorSet = AnimatorSet().apply {
+            duration = BANNER_ANIMATION_DURATION_MS
+            startDelay = BANNER_ANIMATION_START_DELAY_MS
+            playTogether(
+                bannerTranslateAnimator,
+                bannerAlphaAnimator,
+                appsGridTranslateAnimator
+            )
+        }
+        bannerAnimatorSet.start()
+    }
+
+    private companion object {
+        const val BANNER_ANIMATION_DURATION_MS = 300L
+        const val BANNER_ANIMATION_START_DELAY_MS = 400L
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/Banner.java b/libs/appgrid/lib/src/com/android/car/carlauncher/Banner.java
index ebfd7f2..3382bf4 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/Banner.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/Banner.java
@@ -58,8 +58,8 @@
         LayoutInflater inflater = LayoutInflater.from(getContext());
         inflater.inflate(R.layout.banner, this);
 
-        mFirstButton = requireViewById(R.id.first_button);
-        mSecondButton = requireViewById(R.id.second_button);
+        mFirstButton = requireViewById(R.id.banner_first_button);
+        mSecondButton = requireViewById(R.id.banner_second_button);
         mTitleTextView = requireViewById(R.id.banner_title);
 
         TypedArray attrArray = context.getTheme().obtainStyledAttributes(
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherItemMessageHelper.java b/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherItemMessageHelper.java
deleted file mode 100644
index 6548725..0000000
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherItemMessageHelper.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.car.carlauncher.LauncherItemProto.LauncherItemListMessage;
-import com.android.car.carlauncher.LauncherItemProto.LauncherItemMessage;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
-
-/**
- * Helper class that provides method used by LauncherModel
- */
-public class LauncherItemMessageHelper {
-    /**
-     * Convert a List of {@link LauncherItemMessage} to a single {@link LauncherItemListMessage}.
-     */
-    @Nullable
-    public LauncherItemListMessage convertToMessage(List<LauncherItemMessage> msgList) {
-        if (msgList == null) {
-            return null;
-        }
-        LauncherItemListMessage.Builder builder =
-                LauncherItemListMessage.newBuilder().addAllLauncherItemMessage(msgList);
-        return builder.build();
-    }
-
-    /**
-     * Converts {@link LauncherItemListMessage} to a List of {@link LauncherItemMessage},
-     * sorts the LauncherItemList based on their relative order in the file, then return the list.
-     */
-    @NonNull
-    public List<LauncherItemMessage> getSortedList(@Nullable LauncherItemListMessage protoLstMsg) {
-        if (protoLstMsg == null) {
-            return new ArrayList<>();
-        }
-        List<LauncherItemMessage> itemMsgList = protoLstMsg.getLauncherItemMessageList();
-        List<LauncherItemMessage> sortedItemMsgList = new ArrayList<>();
-        if (!itemMsgList.isEmpty() && itemMsgList.size() > 0) {
-            // need to create a new list for sorting purposes since ProtobufArrayList is not mutable
-            sortedItemMsgList.addAll(itemMsgList);
-            Collections.sort(sortedItemMsgList,
-                    Comparator.comparingInt(LauncherItemMessage::getRelativePosition));
-        }
-        return sortedItemMsgList;
-    }
-}
-
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModel.java b/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModel.java
deleted file mode 100644
index 80602ab..0000000
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModel.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import android.content.ComponentName;
-import android.content.Intent;
-
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.ViewModel;
-
-import com.android.car.carlauncher.apporder.AppOrderController;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A launcher model decides how the apps are displayed.
- */
-public class LauncherViewModel extends ViewModel {
-    private final AppOrderController mAppOrderController;
-
-    public LauncherViewModel(File launcherFileDir) {
-        mAppOrderController = new AppOrderController(launcherFileDir);
-    }
-
-    public static final Comparator<LauncherItem> ALPHABETICAL_COMPARATOR = Comparator.comparing(
-            LauncherItem::getDisplayName, String::compareToIgnoreCase);
-
-    public LiveData<List<LauncherItem>> getCurrentLauncher() {
-        return mAppOrderController.getAppOrderObservable();
-    }
-
-    /**
-     * Read in apps order from file if exists, then publish app order to UI if valid.
-     */
-    public void loadAppsOrderFromFile() {
-        mAppOrderController.loadAppOrderFromFile();
-    }
-
-    /**
-     * Populate the apps based on alphabetical order and create mapping from packageName to
-     * LauncherItem. Each item in the current launcher is AppItem.
-     */
-    public void processAppsInfoFromPlatform(AppLauncherUtils.LauncherAppsInfo launcherAppsInfo) {
-        Map<ComponentName, LauncherItem> launcherItemsMap = new HashMap<>();
-        List<LauncherItem> launcherItems = new ArrayList<>();
-        List<AppMetaData> appMetaDataList = launcherAppsInfo.getLaunchableComponentsList();
-        for (AppMetaData appMetaData : appMetaDataList) {
-            LauncherItem nextItem = new AppItem(appMetaData);
-            launcherItems.add(nextItem);
-            launcherItemsMap.put(appMetaData.getComponentName(), nextItem);
-        }
-        Collections.sort(launcherItems, LauncherViewModel.ALPHABETICAL_COMPARATOR);
-        mAppOrderController.loadAppListFromPlatform(launcherItemsMap, launcherItems);
-    }
-
-    /**
-     * Notifies the controller that a change in the data model has been observed by the user
-     * interface (e.g. platform apps list has been updated, user has updated the app order.)
-     *
-     * The controller should ONLY handle writing to disk in this method. This will ensure that all
-     * changes to the data model is consistent with the user interface.
-     */
-    public void handleAppListChange() {
-        mAppOrderController.handleAppListChange();
-    }
-
-    /**
-     * Notifies the controller to move the given AppItem to a new position in the data model.
-     */
-    public void setAppPosition(int position, AppMetaData app) {
-        mAppOrderController.setAppPosition(position, app);
-    }
-
-    /**
-     * Updates the launcher data model when app mirroring intent is received.
-     */
-    public void updateMirroringItem(String packageName, Intent mirroringIntent) {
-        mAppOrderController.updateMirroringItem(packageName, mirroringIntent);
-    }
-}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModelFactory.java b/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModelFactory.java
deleted file mode 100644
index 0abd622..0000000
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/LauncherViewModelFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import androidx.lifecycle.ViewModel;
-import androidx.lifecycle.ViewModelProvider;
-
-import java.io.File;
-
-/** A factory class to allow creation of LauncherViewModel by ViewModelProvider. */
-public class LauncherViewModelFactory implements ViewModelProvider.Factory{
-    private File mLauncherFileDir;
-
-    public LauncherViewModelFactory(File launcherFileDir) {
-        mLauncherFileDir = launcherFileDir;
-    }
-
-    @Override
-    public <T extends ViewModel> T create(Class<T> modelClass) {
-        return (T) new LauncherViewModel(mLauncherFileDir);
-    }
-}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/MediaSessionUtils.java b/libs/appgrid/lib/src/com/android/car/carlauncher/MediaSessionUtils.java
new file mode 100644
index 0000000..b296f62
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/MediaSessionUtils.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 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.carlauncher;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+import com.android.car.media.common.source.MediaModels;
+import com.android.car.media.common.source.MediaSessionHelper;
+
+/** Utility class that handles common MediaSession related logic*/
+public class MediaSessionUtils {
+    private static final String TAG = "MediaSessionUtils";
+
+    private MediaSessionUtils() {}
+
+    /** Create a MediaModels object */
+    public static MediaModels getMediaModels(Context context) {
+        return new MediaModels(context.getApplicationContext(),
+                createNotificationProvider(context));
+    }
+
+    /** Create a MediaSessionHelper object */
+    public static MediaSessionHelper getMediaSessionHelper(Context context) {
+        return new MediaSessionHelper(context.getApplicationContext(),
+                createNotificationProvider(context));
+    }
+
+    private static MediaSessionHelper.NotificationProvider createNotificationProvider(
+            Context context) {
+        return new MediaSessionHelper.NotificationProvider() {
+            @Override
+            public StatusBarNotification[] getActiveNotifications() {
+                try {
+                    return NotificationManager.getService()
+                            .getActiveNotificationsWithAttribution(
+                                    context.getPackageName(), null);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Exception trying to get active notifications " + e);
+                    return new StatusBarNotification[0];
+                }
+            }
+
+            @Override
+            public boolean isMediaNotification(Notification notification) {
+                return notification.isMediaNotification();
+            }
+        };
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java b/libs/appgrid/lib/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
index e5a1c9d..c1f7776 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
@@ -88,4 +88,3 @@
 
     }
 }
-
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/ResetLauncherActivity.java b/libs/appgrid/lib/src/com/android/car/carlauncher/ResetLauncherActivity.java
index efd098a..75c5157 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/ResetLauncherActivity.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/ResetLauncherActivity.java
@@ -18,7 +18,6 @@
 import android.app.AlertDialog;
 import android.os.Bundle;
 
-import com.android.car.carlauncher.apporder.AppOrderController;
 import com.android.car.ui.AlertDialogBuilder;
 
 import java.io.File;
@@ -39,7 +38,7 @@
                 .setTitle(getString(R.string.reset_appgrid_title))
                 .setMessage(getString(R.string.reset_appgrid_dialogue_message))
                 .setPositiveButton(getString(android.R.string.ok), (dialogInterface, which) -> {
-                    File order = new File(filesDir, AppOrderController.ORDER_FILE_NAME);
+                    File order = new File(filesDir, "order.data");
                     order.delete();
                     finish();
                 })
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/apporder/AppOrderController.java b/libs/appgrid/lib/src/com/android/car/carlauncher/apporder/AppOrderController.java
deleted file mode 100644
index 2bc0750..0000000
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/apporder/AppOrderController.java
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher.apporder;
-
-import android.content.ComponentName;
-import android.content.Intent;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.lifecycle.MutableLiveData;
-
-import com.android.car.carlauncher.AppItem;
-import com.android.car.carlauncher.AppLauncherUtils;
-import com.android.car.carlauncher.AppMetaData;
-import com.android.car.carlauncher.LauncherItem;
-import com.android.car.carlauncher.LauncherItemMessageHelper;
-import com.android.car.carlauncher.LauncherItemProto.LauncherItemListMessage;
-import com.android.car.carlauncher.LauncherItemProto.LauncherItemMessage;
-import com.android.car.carlauncher.datastore.DataSourceController;
-import com.android.car.carlauncher.datastore.launcheritem.LauncherItemListSource;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-/**
- * Controller that manages the ordering of the app items in app grid.
- */
-public class AppOrderController implements DataSourceController {
-    // file name holding the user customized app order
-    public static final String ORDER_FILE_NAME = "order.data";
-    private final LauncherItemMessageHelper mItemHelper = new LauncherItemMessageHelper();
-    // The app order of launcher items displayed to users
-    private final MutableLiveData<List<LauncherItem>> mCurrentAppList;
-    private final Map<ComponentName, LauncherItem> mLauncherItemMap = new HashMap<>();
-    private final List<ComponentName> mProtoComponentNames = new ArrayList<>();
-    private final List<LauncherItem> mDefaultOrder;
-    private final List<LauncherItem> mCustomizedOrder;
-    private final LauncherItemListSource mDataSource;
-    private boolean mPlatformAppListLoaded;
-    private boolean mCustomAppOrderFetched;
-    private boolean mIsUserCustomized;
-
-    public AppOrderController(File dataFileDirectory) {
-        this(/* dataSource */ new LauncherItemListSource(dataFileDirectory, ORDER_FILE_NAME),
-                /* appList */ new MutableLiveData<>(new ArrayList<>()),
-                /* defaultOrder */ new ArrayList<>(),
-                /* customizedOrder*/ new ArrayList<>());
-    }
-
-    public AppOrderController(LauncherItemListSource dataSource,
-            MutableLiveData<List<LauncherItem>> appList, List<LauncherItem> defaultOrder,
-            List<LauncherItem> customizedOrder) {
-        mDataSource = dataSource;
-        mCurrentAppList = appList;
-        mDefaultOrder = defaultOrder;
-        mCustomizedOrder = customizedOrder;
-    }
-
-    @Override
-    public boolean checkDataSourceExists() {
-        return mDataSource.exists();
-    }
-
-    public MutableLiveData<List<LauncherItem>> getAppOrderObservable() {
-        return mCurrentAppList;
-    }
-
-    /**
-     * Loads the full app list to be displayed in the app grid.
-     */
-    public void loadAppListFromPlatform(Map<ComponentName, LauncherItem> launcherItemsMap,
-            List<LauncherItem> defaultItemOrder) {
-        mDefaultOrder.clear();
-        mDefaultOrder.addAll(defaultItemOrder);
-        mLauncherItemMap.clear();
-        mLauncherItemMap.putAll(launcherItemsMap);
-        mPlatformAppListLoaded = true;
-        maybePublishAppList();
-    }
-
-    /**
-     * Loads any preexisting app order from the proto datastore on disk.
-     */
-    public void loadAppOrderFromFile() {
-        // handle the app order reset case, where the proto file is removed from file system
-        maybeHandleAppOrderReset();
-        mProtoComponentNames.clear();
-        List<LauncherItemMessage> protoItemMessage = mItemHelper.getSortedList(
-                mDataSource.readFromFile());
-        if (!protoItemMessage.isEmpty()) {
-            mIsUserCustomized = true;
-            for (LauncherItemMessage itemMessage : protoItemMessage) {
-                ComponentName itemComponent = new ComponentName(
-                        itemMessage.getPackageName(), itemMessage.getClassName());
-                mProtoComponentNames.add(itemComponent);
-            }
-        }
-        mCustomAppOrderFetched = true;
-        maybePublishAppList();
-    }
-
-    @VisibleForTesting
-    void maybeHandleAppOrderReset() {
-        if (!checkDataSourceExists()) {
-            mIsUserCustomized = false;
-            mCustomizedOrder.clear();
-        }
-    }
-
-    /**
-     * Combine the proto order read from proto with any additional apps read from the platform, then
-     * publish the new list to user interface.
-     *
-     * Prior to publishing the app list to the LiveData (and subsequently to the UI), both (1) the
-     * default platform mapping and (2) user customized order must be read into memory. These
-     * pre-fetch methods may be executed on different threads, so we should only publish the final
-     * ordering when both steps have completed.
-     */
-    @VisibleForTesting
-    void maybePublishAppList() {
-        if (!appsDataLoadingCompleted()) {
-            return;
-        }
-        // app names found in order proto file will be displayed first
-        mCustomizedOrder.clear();
-        List<LauncherItem> customOrder = new ArrayList<>();
-        Set<ComponentName> namesFoundInProto = new HashSet<>();
-        for (ComponentName name: mProtoComponentNames) {
-            if (mLauncherItemMap.containsKey(name)) {
-                customOrder.add(mLauncherItemMap.get(name));
-                namesFoundInProto.add(name);
-            }
-        }
-        mCustomizedOrder.addAll(customOrder);
-        if (shouldUseCustomOrder()) {
-            // new apps from platform not found in proto will be added to the end
-            mCustomizedOrder.clear();
-            List<ComponentName> newPlatformApps = mLauncherItemMap.keySet()
-                    .stream()
-                    .filter(element -> !namesFoundInProto.contains(element))
-                    .collect(Collectors.toList());
-            if (!newPlatformApps.isEmpty()) {
-                Collections.sort(newPlatformApps);
-                for (ComponentName newAppName: newPlatformApps) {
-                    customOrder.add(mLauncherItemMap.get(newAppName));
-                }
-            }
-            mCustomizedOrder.addAll(customOrder);
-            mCurrentAppList.postValue(customOrder);
-        } else {
-            mCurrentAppList.postValue(mDefaultOrder);
-            mCustomizedOrder.clear();
-        }
-        // reset apps data loading flags
-        mPlatformAppListLoaded = mCustomAppOrderFetched = false;
-    }
-
-    @VisibleForTesting
-    boolean appsDataLoadingCompleted() {
-        return mPlatformAppListLoaded && mCustomAppOrderFetched;
-    }
-
-    @VisibleForTesting
-    boolean shouldUseCustomOrder() {
-        return mIsUserCustomized && mCustomizedOrder.size() != 0;
-    }
-
-    /**
-     * Persistently writes the current in memory app order into disk.
-     */
-    public void handleAppListChange() {
-        if (mIsUserCustomized) {
-            List<LauncherItem> currentItems = mCurrentAppList.getValue();
-            List<LauncherItemMessage> msgList = new ArrayList<LauncherItemMessage>();
-            for (int i = 0; i < currentItems.size(); i++) {
-                msgList.add(currentItems.get(i).convertToMessage(i, -1));
-            }
-            LauncherItemListMessage appOrderListMessage = mItemHelper.convertToMessage(msgList);
-            mDataSource.writeToFile(appOrderListMessage);
-        }
-    }
-
-    /**
-     * Move an app to a specified index and post the value to LiveData.
-     */
-    public void setAppPosition(int position, AppMetaData app) {
-        List<LauncherItem> current = mCurrentAppList.getValue();
-        LauncherItem item = mLauncherItemMap.get(app.getComponentName());
-        if (current != null && current.size() != 0 && position < current.size() && item != null) {
-            mIsUserCustomized = true;
-            current.remove(item);
-            current.add(position, item);
-            mCurrentAppList.postValue(current);
-        }
-    }
-
-    /**
-     * Handles the incoming mirroring intent from ViewModel.
-     *
-     * Update an AppItem's AppMetaData isMirroring state and its launch callback then post the
-     * updated to LiveData.
-     */
-    public void updateMirroringItem(String packageName, Intent mirroringIntent) {
-        List<LauncherItem> launcherList = mCurrentAppList.getValue();
-        if (launcherList == null) {
-            return;
-        }
-        List<LauncherItem> launcherListCopy = new ArrayList<>();
-        for (LauncherItem item : launcherList) {
-            if (item instanceof AppItem) {
-                // TODO (b/272796126): move deep copying to inside DiffUtil
-                AppMetaData metaData = ((AppItem) item).getAppMetaData();
-                if (item.getPackageName().equals(packageName)) {
-                    launcherListCopy.add(new AppItem(item.getPackageName(), item.getClassName(),
-                            item.getDisplayName(), new AppMetaData(metaData.getDisplayName(),
-                            metaData.getComponentName(), metaData.getIcon(),
-                            metaData.getIsDistractionOptimized(), /* isMirroring= */ true,
-                            metaData.getIsDisabledByTos(),
-                                    contextArg ->
-                                            AppLauncherUtils.launchApp(contextArg, mirroringIntent),
-                            metaData.getAlternateLaunchCallback())));
-                } else if (metaData.getIsMirroring()) {
-                    Intent intent = new Intent(Intent.ACTION_MAIN)
-                            .setComponent(metaData.getComponentName())
-                            .addCategory(Intent.CATEGORY_LAUNCHER)
-                            .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    launcherListCopy.add(new AppItem(item.getPackageName(), item.getClassName(),
-                            item.getDisplayName(), new AppMetaData(metaData.getDisplayName(),
-                            metaData.getComponentName(), metaData.getIcon(),
-                            metaData.getIsDistractionOptimized(), /* isMirroring= */ false,
-                            metaData.getIsDisabledByTos(),
-                                    contextArg ->
-                                            AppLauncherUtils.launchApp(contextArg, intent),
-                            metaData.getAlternateLaunchCallback())));
-                } else {
-                    launcherListCopy.add(item);
-                }
-            } else {
-                launcherListCopy.add(item);
-            }
-        }
-        mCurrentAppList.postValue(launcherListCopy);
-    }
-}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/AppOrderDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/AppOrderDataSource.kt
new file mode 100644
index 0000000..2ad0902
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/AppOrderDataSource.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.util.Log
+import com.android.car.carlauncher.LauncherItemProto
+import com.android.car.carlauncher.LauncherItemProto.LauncherItemMessage
+import com.android.car.carlauncher.datasources.AppOrderDataSource.AppOrderInfo
+import com.android.car.carlauncher.datastore.launcheritem.LauncherItemListSource
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.emitAll
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.withContext
+
+/**
+ * DataSource for managing the persisted order of apps. This class encapsulates all
+ * interactions with the persistent storage (e.g., Files, Proto, Database), acting as
+ * the single source of truth.
+ *
+ * Important: To ensure consistency, avoid modifying the persistent storage directly.
+ *            Use the methods provided by this DataSource.
+ */
+interface AppOrderDataSource {
+
+    /**
+     * Saves the provided app order to persistent storage.
+     *
+     * @param appOrderInfoList The new order of apps to be saved, represented as a list of
+     * LauncherItemMessage objects.
+     */
+    suspend fun saveAppOrder(appOrderInfoList: List<AppOrderInfo>)
+
+    /**
+     * Returns a Flow of the saved app order. The Flow will emit the latest saved order
+     * and any subsequent updates.
+     *
+     * @return A Flow of [AppOrderInfo] lists, representing the saved app order.
+     */
+    fun getSavedAppOrder(): Flow<List<AppOrderInfo>>
+
+    /**
+     * Returns a Flow of comparators for sorting app lists. The comparators will prioritize the
+     * saved app order, and may fall back to other sorting logic if necessary.
+     *
+     * @return A Flow of Comparator objects, used to sort [AppOrderInfo] lists.
+     */
+    fun getSavedAppOrderComparator(): Flow<Comparator<AppOrderInfo>>
+
+    /**
+     * Clears the saved app order from persistent storage.
+     *
+     * @return `true` if the operation was successful, `false` otherwise.
+     */
+    suspend fun clearAppOrder(): Boolean
+
+    data class AppOrderInfo(val packageName: String, val className: String, val displayName: String)
+}
+
+/**
+ * Implementation of the [AppOrderDataSource] interface, responsible for managing app order
+ * persistence using a Proto file storage mechanism.
+ *
+ * @property launcherItemListSource The source for accessing and updating the raw Proto data.
+ * @property bgDispatcher (Optional) A CoroutineDispatcher specifying the thread pool for background
+ *                      operations (defaults to Dispatchers.IO for I/O-bound tasks).
+ */
+class AppOrderProtoDataSourceImpl(
+    private val launcherItemListSource: LauncherItemListSource,
+    private val bgDispatcher: CoroutineDispatcher = Dispatchers.IO,
+) : AppOrderDataSource {
+
+    private val appOrderFlow = MutableStateFlow(emptyList<AppOrderInfo>())
+
+    /**
+     * Saves the current app order to a Proto file for persistent storage.
+     * * Performs the save operation on the background dispatcher ([bgDispatcher]).
+     * * Updates all collectors of [getSavedAppOrderComparator] and [getSavedAppOrder] immediately,
+     *   even before the write operation has completed.
+     * * In case of a write failure, the operation fails silently. This might lead to a
+     *   temporarily inconsistent app order for the current session (until the app restarts).
+     */
+    override suspend fun saveAppOrder(appOrderInfoList: List<AppOrderInfo>) {
+        // Immediately update the cache.
+        appOrderFlow.value = appOrderInfoList
+        // Store the app order persistently.
+        withContext(bgDispatcher) {
+            // If it fails to write, it fails silently.
+            if (!launcherItemListSource.writeToFile(
+                    LauncherItemProto.LauncherItemListMessage.newBuilder()
+                        .addAllLauncherItemMessage(appOrderInfoList.mapIndexed { index, item ->
+                            convertToMessage(item, index)
+                        }).build()
+                )
+            ) {
+                Log.i(TAG, "saveAppOrder failed to writeToFile")
+            }
+        }
+    }
+
+    /**
+     * Gets the latest know saved order to sort the apps.
+     * Also check [getSavedAppOrderComparator] if you need comparator to sort the list of apps.
+     *
+     * * Emits a new list to all collectors whenever the app order is updated using the
+     *   [saveAppOrder] function or when [clearAppOrder] is called.
+     *
+     * __Handling Apps with Unknown Positions:__
+     * The client should implement logic to handle apps whose positions are not
+     * specified in the saved order. A common strategy is to append them to the end of the list.
+     *
+     * __Handling Unavailable Apps:__
+     * The client can choose to exclude apps that are unavailable (e.g., uninstalled or disabled)
+     * from the sorted list.
+    */
+    override fun getSavedAppOrder(): Flow<List<AppOrderInfo>> = flow {
+        withContext(bgDispatcher) {
+            val appOrderFromFiles = launcherItemListSource.readFromFile()?.launcherItemMessageList
+            // Read from the persistent storage for pre-existing order.
+            // If no pre-existing order exists it initially returns an emptyList.
+            if (!appOrderFromFiles.isNullOrEmpty()) {
+                appOrderFlow.value =
+                    appOrderFromFiles.sortedBy { it.relativePosition }
+                        .map { AppOrderInfo(it.packageName, it.className, it.displayName) }
+            }
+        }
+        emitAll(appOrderFlow)
+    }.flowOn(bgDispatcher)
+
+    /**
+     * Provides a Flow of comparators to sort a list of apps.
+     *
+     * * Sorts apps based on a pre-defined order. If an app is not found in the pre-defined
+     *   order, it falls back to alphabetical sorting with [AppOrderInfo.displayName].
+     * * Emits a new comparator to all collectors whenever the app order is updated using the
+     *   [saveAppOrder] function or when [clearAppOrder] is called.
+     *
+     * @see getSavedAppOrder
+     */
+    override fun getSavedAppOrderComparator(): Flow<Comparator<AppOrderInfo>> {
+        return getSavedAppOrder().map { appOrderInfoList ->
+            val appOrderMap = appOrderInfoList.withIndex().associateBy({it.value}, {it.index})
+            Comparator<AppOrderInfo> { app1, app2 ->
+                when {
+                    // Both present in predefined list.
+                    appOrderMap.contains(app1) && appOrderMap.contains(app2) -> {
+                        // Kotlin compiler complains for nullability, although this should not be.
+                        appOrderMap[app1]!! - appOrderMap[app2]!!
+                    }
+                    // Prioritize predefined names.
+                    appOrderMap.contains(app1) -> -1
+                    appOrderMap.contains(app2) -> 1
+                    // Fallback to alphabetical.
+                    else -> app1.displayName.compareTo(app2.displayName)
+                }
+            }
+        }.flowOn(bgDispatcher)
+    }
+
+    /**
+     * Deletes the persisted app order data. Performs the file deletion operation on the
+     * background dispatcher ([bgDispatcher]).
+     *
+     * * Successful deletion will report empty/default order [emptyList] to collectors of
+     *   [getSavedAppOrder] amd [getSavedAppOrderComparator]
+     *
+     * @return `true` if the deletion was successful, `false` otherwise.
+     */
+    override suspend fun clearAppOrder(): Boolean {
+        return withContext(bgDispatcher) {
+            launcherItemListSource.deleteFile()
+        }.also {
+            if (it) {
+                // If delete is successful report empty app order.
+                appOrderFlow.value = emptyList()
+            }
+        }
+    }
+
+    private fun convertToMessage(
+        appOrderInfo: AppOrderInfo,
+        relativePosition: Int
+    ): LauncherItemMessage? {
+        val builder = LauncherItemMessage.newBuilder().setPackageName(appOrderInfo.packageName)
+            .setClassName(appOrderInfo.className).setDisplayName(appOrderInfo.displayName)
+            .setRelativePosition(relativePosition).setContainerID(DOES_NOT_SUPPORT_CONTAINER)
+        return builder.build()
+    }
+
+    companion object {
+        val TAG: String = AppOrderDataSource::class.java.simpleName
+        private const val DOES_NOT_SUPPORT_CONTAINER = -1
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSource.kt
new file mode 100644
index 0000000..6d8f15b
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/ControlCenterMirroringDataSource.kt
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import android.os.RemoteException
+import android.util.Log
+import com.android.car.carlauncher.R
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSource.MirroringPackageData
+import java.net.URISyntaxException
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.channels.ProducerScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * DataSource interface tells if there is an active Mirroring-Session.
+ */
+interface ControlCenterMirroringDataSource {
+
+    /**
+     * @return Flow of [MirroringPackageData] which sends the active Mirroring packageName and
+     *  redirect launchIntent which launches the application in the
+     *  MirroringActivity
+     */
+    fun getAppMirroringSession(): Flow<MirroringPackageData>
+
+    data class MirroringPackageData(
+        val packageName: String,
+        val launchIntent: Intent
+    ) {
+        companion object {
+            // signifies active mirroring session
+            val NO_MIRRORING = MirroringPackageData("", Intent())
+        }
+    }
+}
+
+/**
+ * Impl of [ControlCenterMirroringDataSource] to surface all the control center mirroring session
+ * All the operations in this class are non blocking.
+ *
+ *  The Application using this Datasource is expected to define the following configs
+ *  in its resources. The implementation uses [resources] to fetch these configs.
+ *  These configs should be bound application's lifecycle and are not expected to change with
+ *  Activity's lifecycle events.
+ *
+ *  __config_msg_mirroring_service_pkg_name__: String value stating the package name of the
+ *   mirroring service.
+ *
+ *  __config_msg_mirroring_service_class_name__: String value stating the class name of the
+ *   mirroring service.
+ *
+ *  __config_msg_register_mirroring_pkg_code__: Integer unique key to register the service.
+ *
+ *  __config_msg_unregister_mirroring_pkg_code__: Integer unique key to unregister the service.
+ *
+ *  __config_msg_send_mirroring_pkg_code__: Integer unique key to send mirroring packet across the
+ *   service.
+ *
+ *  __config_msg_mirroring_pkg_name_key__: String unique key to send packageName of the active
+ *   mirroring session.
+ *
+ *  __config_msg_mirroring_redirect_uri_key__: String unique key to send the redirect uri of the
+ *   mirroring activity.
+ *
+ * @property [resources] Application resources, not bound to activity's configuration changes.
+ * @property [bindService] Function to register service.
+ *  Should be provided by an Android Component owning the [Context].
+ * @property [unBindService] Function to unregister the broadcast receiver.
+ *  Should be provided by the Android Component owning the [Context].
+ * @property [packageManager] Used to resolve the bounded Service.
+ * @property [bgDispatcher] Executes all the operations on this background coroutine dispatcher.
+ *
+ */
+class ControlCenterMirroringDataSourceImpl(
+    private val resources: Resources,
+    private val bindService: (Intent, MirroringServiceConnection, flags: Int) -> Unit,
+    private val unBindService: (MirroringServiceConnection) -> Unit,
+    private val packageManager: PackageManager,
+    private val bgDispatcher: CoroutineDispatcher = Dispatchers.IO
+) : ControlCenterMirroringDataSource {
+
+    /**
+     * @return Flow of [MirroringPackageData] reporting current active mirroring session.
+     *
+     * Note: The producer sends an [MirroringPackageData.NO_MIRRORING] initially.
+     * This immediately tells the collector that there are no changes as of now with packages.
+     *
+     * When the scope in which this flow is collected is closed/canceled
+     * [unBindService] is triggered.
+     */
+    override fun getAppMirroringSession(): Flow<MirroringPackageData> {
+        return callbackFlow {
+            // Send empty mirroring packet to signify that no mirroring is ongoing
+            trySend(MirroringPackageData.NO_MIRRORING)
+            val looper = Looper.getMainLooper()
+            val clientMessenger = getReceiverMessenger(looper, this)
+            val serviceConnection = getMirroringConnectionService(clientMessenger, this)
+            registerReceiver(serviceConnection, this)
+
+            awaitClose {
+                unregisterReceiver(serviceConnection)
+            }
+        }.flowOn(bgDispatcher).conflate()
+    }
+
+    private fun getReceiverMessenger(
+        looper: Looper,
+        producerScope: ProducerScope<MirroringPackageData>
+    ): Messenger {
+        return Messenger(object : Handler(looper) {
+            private val senderMirroringPkgCode =
+                resources.getInteger(R.integer.config_msg_send_mirroring_pkg_code)
+            private val mirroringPkgNameKey =
+                resources.getString(R.string.config_msg_mirroring_pkg_name_key)
+            private val mirroringRedirectUriKey =
+                resources.getString(R.string.config_msg_mirroring_redirect_uri_key)
+
+            override fun handleMessage(msg: Message) {
+                if (msg.what != senderMirroringPkgCode) {
+                    super.handleMessage(msg)
+                    return
+                }
+                val bundle = msg.obj as Bundle
+                val mirroringPackageName = bundle.getString(mirroringPkgNameKey)
+                if (mirroringPackageName.isNullOrEmpty()) {
+                    producerScope.trySend(MirroringPackageData.NO_MIRRORING)
+                    return
+                }
+                try {
+                    val mirroringIntentRedirect = Intent.parseUri(
+                        bundle.getString(mirroringRedirectUriKey),
+                        Intent.URI_INTENT_SCHEME
+                    )
+                    producerScope.trySend(
+                        MirroringPackageData(
+                            mirroringPackageName,
+                            mirroringIntentRedirect
+                        )
+                    )
+                } catch (e: URISyntaxException) {
+                    Log.d(TAG, "Error parsing mirroring redirect intent $e")
+                }
+            }
+        })
+    }
+
+    abstract class MirroringServiceConnection : ServiceConnection {
+        var mServiceMessenger: Messenger? = null
+        var mClientMessenger: Messenger? = null
+    }
+
+    private fun getMirroringConnectionService(
+        clientMessenger: Messenger,
+        producerScope: ProducerScope<MirroringPackageData>
+    ): MirroringServiceConnection {
+        return object : MirroringServiceConnection() {
+            init {
+                mClientMessenger = clientMessenger
+            }
+
+            override fun onServiceConnected(name: ComponentName, service: IBinder) {
+                mServiceMessenger = Messenger(service)
+                val msg: Message = Message.obtain(
+                    null,
+                    resources.getInteger(R.integer.config_msg_register_mirroring_pkg_code)
+                )
+                msg.replyTo = mClientMessenger
+                try {
+                    mServiceMessenger?.send(msg)
+                } catch (e: RemoteException) {
+                    Log.d(TAG, "Exception sending message to mirroring service: $e")
+                }
+            }
+
+            override fun onServiceDisconnected(name: ComponentName) {
+                producerScope.cancel("Mirroring Service disconnected")
+            }
+        }
+    }
+
+    private fun registerReceiver(
+        mirroringConnectionService: MirroringServiceConnection,
+        producerScope: ProducerScope<MirroringPackageData>
+    ) {
+        try {
+            val intent = Intent()
+            intent.component = ComponentName(
+                resources.getString(R.string.config_msg_mirroring_service_pkg_name),
+                resources.getString(R.string.config_msg_mirroring_service_class_name)
+            )
+            if (packageManager.resolveService(intent, 0) != null) {
+                bindService(
+                    intent,
+                    mirroringConnectionService,
+                    Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT
+                )
+            }
+        } catch (e: SecurityException) {
+            Log.e(TAG, "Error binding to mirroring service: $e")
+            producerScope.close(e)
+        }
+    }
+
+    private fun unregisterReceiver(mirroringConnectionService: MirroringServiceConnection) {
+        val msg = Message.obtain(
+            null,
+            resources.getInteger(R.integer.config_msg_unregister_mirroring_pkg_code)
+        )
+        msg.replyTo = mirroringConnectionService.mClientMessenger
+        try {
+            mirroringConnectionService.mServiceMessenger?.send(msg)
+        } catch (e: RemoteException) {
+            Log.d(TAG, "Exception unregistering mirroring service $e")
+        }
+        if (mirroringConnectionService.mServiceMessenger != null) {
+            unBindService(mirroringConnectionService)
+        }
+    }
+
+    companion object {
+        val TAG: String = ControlCenterMirroringDataSourceImpl::class.java.simpleName
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSource.kt
new file mode 100644
index 0000000..d0613c5
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/LauncherActivitiesDataSource.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.content.pm.LauncherActivityInfo
+import android.content.pm.LauncherApps
+import android.content.res.Resources
+import android.os.UserHandle
+import com.android.car.carlauncher.R
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.withContext
+
+interface LauncherActivitiesDataSource {
+
+    /**
+     * Gets all the Launchable activities for the user.
+     */
+    suspend fun getAllLauncherActivities(): List<LauncherActivityInfo>
+
+    /**
+     * Flow notifying changes if packages are changed.
+     */
+    fun getOnPackagesChanged(): Flow<String>
+
+    /**
+     * Get packages to hide explicitly
+     */
+    fun getAppsToHide(): List<String>
+
+    companion object {
+        val TAG: String = LauncherActivitiesDataSource::class.java.simpleName
+    }
+}
+
+/**
+ * Impl of [LauncherActivitiesDataSource] to surface all the launcher activities apis.
+ * All the operations in this class are non blocking.
+ *
+ * @property [launcherApps] Used to fetch launcher activities.
+ * @property [registerReceiverFunction] Function to register the broadcast receiver.
+ * Should be provided by the Android Component owning the [Context]
+ * @property [unregisterReceiverFunction] Function to unregister the broadcast receiver.
+ * Should be provided by the Android Component owning the [Context]
+ * @property [userHandle] Specified user's handle to fetch launcher activities.
+ * @param [resources] Application resources, not bound to activity's configuration changes.
+ * @property [bgDispatcher] Executes all the operations on this background coroutine dispatcher.
+ */
+class LauncherActivitiesDataSourceImpl(
+    private val launcherApps: LauncherApps,
+    private val registerReceiverFunction: (BroadcastReceiver, IntentFilter) -> Unit,
+    private val unregisterReceiverFunction: (BroadcastReceiver) -> Unit,
+    private val userHandle: UserHandle,
+    val resources: Resources,
+    private val bgDispatcher: CoroutineDispatcher = Dispatchers.Default
+) : LauncherActivitiesDataSource {
+
+    private val listOfApps = resources.getStringArray(R.array.hidden_apps).toList()
+
+    /**
+     * Gets all launcherActivities for a user with [userHandle]
+     */
+    override suspend fun getAllLauncherActivities(): List<LauncherActivityInfo> {
+        return withContext(bgDispatcher) {
+            launcherApps.getActivityList(
+                /* packageName = */
+                null,
+                userHandle
+            )
+        }
+    }
+
+    /**
+     * Gets a flow Producer which report changes in the packages with following actions:
+     * [Intent.ACTION_PACKAGE_ADDED], [Intent.ACTION_PACKAGE_CHANGED],
+     * [Intent.ACTION_PACKAGE_REPLACED] or [Intent.ACTION_PACKAGE_REMOVED].
+     *
+     * Note: The producer sends an `Empty String` initially. This immediately tells the collector
+     * that there are no changes as of now with packages.
+     *
+     * When the scope in which this flow is collected is closed/canceled
+     * [unregisterReceiverFunction] is triggered.
+     */
+    override fun getOnPackagesChanged(): Flow<String> {
+        return callbackFlow {
+            trySend("")
+            val filter = IntentFilter()
+            filter.addAction(Intent.ACTION_PACKAGE_ADDED)
+            filter.addAction(Intent.ACTION_PACKAGE_CHANGED)
+            filter.addAction(Intent.ACTION_PACKAGE_REPLACED)
+            filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
+            filter.addDataScheme("package")
+            val receiver = object : BroadcastReceiver() {
+                override fun onReceive(context: Context?, intent: Intent?) {
+                    val packageName = intent?.data?.schemeSpecificPart
+                    if (packageName.isNullOrBlank()) {
+                        return
+                    }
+                    trySend(packageName)
+                }
+            }
+            registerReceiverFunction(receiver, filter)
+            awaitClose {
+                unregisterReceiverFunction(receiver)
+            }
+        }.flowOn(bgDispatcher).conflate()
+    }
+
+    /**
+     * Gets packages that are explicitly required to be hidden.
+     *
+     * * Note: This packages are defined in [Resources] by name __hidden_apps__
+     */
+    override fun getAppsToHide(): List<String> {
+        return listOfApps
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSource.kt
new file mode 100644
index 0000000..c4f49bc
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/MediaTemplateAppsDataSource.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+import android.service.media.MediaBrowserService
+import com.android.car.media.common.source.MediaSource.isAudioMediaSource
+import com.android.car.media.common.source.MediaSource.isMediaTemplate
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * DataSource interface for MediaTemplate apps
+ */
+interface MediaTemplateAppsDataSource {
+
+    /**
+     * Get all media services on the device
+     * @param includeCustomMediaPackages
+     */
+    suspend fun getAllMediaServices(includeCustomMediaPackages: Boolean): List<ResolveInfo>
+}
+
+/**
+ * Impl of [MediaTemplateAppsDataSource], surfaces all media template apps related queries
+ *
+ * @property packageManager to query MediaServices.
+ * @param appContext application context, not bound to activity's configuration changes.
+ * @property [bgDispatcher] executes all the operations on this background coroutine dispatcher.
+ */
+class MediaTemplateAppsDataSourceImpl(
+    private val packageManager: PackageManager,
+    private val appContext: Context,
+    private val bgDispatcher: CoroutineDispatcher
+) : MediaTemplateAppsDataSource {
+
+    /**
+     * Gets all media services for MediaTemplateApps.
+     *
+     * @param includeCustomMediaPackages if false, only gets MediaTemplateApps.
+     *                                   if true, in addition to MediaTemplateApps also include
+     *                                   custom media components.
+     */
+    override suspend fun getAllMediaServices(
+        includeCustomMediaPackages: Boolean
+    ): List<ResolveInfo> {
+        val filterFunction = if (includeCustomMediaPackages) {
+            ::isAudioMediaSource
+        } else {
+            ::isMediaTemplate
+        }
+        return withContext(bgDispatcher) {
+            packageManager.queryIntentServices(
+                Intent(MediaBrowserService.SERVICE_INTERFACE),
+                PackageManager.GET_RESOLVED_FILTER
+            ).filter {
+                val componentName = ComponentName(it.serviceInfo.packageName, it.serviceInfo.name)
+                filterFunction(appContext, componentName)
+            }
+        }
+    }
+
+    companion object {
+        val TAG: String = MediaTemplateAppsDataSourceImpl::class.java.simpleName
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/UXRestrictionDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/UXRestrictionDataSource.kt
new file mode 100644
index 0000000..afb2ff7
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/UXRestrictionDataSource.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources
+
+import android.car.content.pm.CarPackageManager
+import android.car.drivingstate.CarUxRestrictionsManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.res.Resources
+import android.media.session.MediaSessionManager
+import android.util.Log
+import androidx.lifecycle.asFlow
+import com.android.car.carlauncher.Flags
+import com.android.car.carlauncher.MediaSessionUtils
+import com.android.car.carlauncher.R
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/**
+ * DataSource interface for providing ux restriction state
+ */
+interface UXRestrictionDataSource {
+
+    /**
+     * Flow notifying if distraction optimization is required
+     */
+    fun requiresDistractionOptimization(): Flow<Boolean>
+
+    fun isDistractionOptimized(): Flow<(componentName: ComponentName, isMedia: Boolean) -> Boolean>
+}
+
+/**
+ * Impl of [UXRestrictionDataSource]
+ *
+ * @property [uxRestrictionsManager] Used to listen for distraction optimization changes.
+ * @property [carPackageManager]
+ * @property [mediaSessionManager]
+ * @property [resources] Application resources, not bound to activity's configuration changes.
+ * @property [bgDispatcher] Executes all the operations on this background coroutine dispatcher.
+ */
+class UXRestrictionDataSourceImpl(
+    private val context: Context,
+    private val uxRestrictionsManager: CarUxRestrictionsManager,
+    private val carPackageManager: CarPackageManager,
+    private val mediaSessionManager: MediaSessionManager,
+    private val resources: Resources,
+    private val bgDispatcher: CoroutineDispatcher = Dispatchers.Default,
+) : UXRestrictionDataSource {
+
+    /**
+     * Gets a flow producer which provides updates if distraction optimization is currently required
+     * This conveys if the foreground activity needs to be distraction optimized.
+     *
+     * When the scope in which this flow is collected is closed/canceled
+     * [CarUxRestrictionsManager.unregisterListener] is triggered.
+     */
+    override fun requiresDistractionOptimization(): Flow<Boolean> {
+        return callbackFlow {
+            val currentRestrictions = uxRestrictionsManager.currentCarUxRestrictions
+            if (currentRestrictions == null) {
+                Log.e(TAG, "CurrentCarUXRestrictions is not initialized")
+                trySend(false)
+            } else {
+                trySend(currentRestrictions.isRequiresDistractionOptimization)
+            }
+            uxRestrictionsManager.registerListener {
+                trySend(it.isRequiresDistractionOptimization)
+            }
+            awaitClose {
+                uxRestrictionsManager.unregisterListener()
+            }
+        }.flowOn(bgDispatcher).conflate()
+    }
+
+    override fun isDistractionOptimized():
+            Flow<(componentName: ComponentName, isMedia: Boolean) -> Boolean> {
+        if (!(Flags.mediaSessionCard() &&
+                    resources.getBoolean(R.bool.config_enableMediaSessionAppsWhileDriving))
+        ) {
+            return flowOf(fun(componentName: ComponentName, isMedia: Boolean): Boolean {
+                return isMedia || (carPackageManager.isActivityDistractionOptimized(
+                    componentName.packageName,
+                    componentName.className
+                ))
+            })
+        }
+        return getActiveMediaPlaybackSessions().map {
+            fun(componentName: ComponentName, isMedia: Boolean): Boolean {
+                if (it.contains(componentName.packageName)) {
+                    return true
+                }
+                return isMedia || (carPackageManager.isActivityDistractionOptimized(
+                    componentName.packageName,
+                    componentName.className
+                ))
+            }
+        }.distinctUntilChanged()
+    }
+
+    private fun getActiveMediaPlaybackSessions(): Flow<List<String>> {
+        return MediaSessionUtils.getMediaSessionHelper(context).activeOrPausedMediaSources.asFlow()
+            .map { mediaSources ->
+                mediaSources.mapNotNull {
+                    it.packageName
+                }
+            }
+    }
+
+    companion object {
+        val TAG: String = UXRestrictionDataSourceImpl::class.java.simpleName
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSource.kt
new file mode 100644
index 0000000..7149b50
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/DisabledAppsDataSource.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources.restricted
+
+import android.car.settings.CarSettings
+import android.content.ContentResolver
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
+import android.content.pm.ResolveInfo
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import com.android.car.carlauncher.datasources.restricted.RestrictedAppsUtils.getLauncherActivitiesForRestrictedApps
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * DataSource exposes a flow which tracks list of disabled apps.
+ */
+interface DisabledAppsDataSource {
+    fun getDisabledApps(): Flow<List<ResolveInfo>>
+}
+
+/**
+ * Impl of [DisabledAppsDataSource], to surface all DisabledApps apis.
+ *
+ * All the operations in this class are non blocking.
+ */
+class DisabledAppsDataSourceImpl(
+    private val contentResolver: ContentResolver,
+    private val packageManager: PackageManager,
+    private val bgDispatcher: CoroutineDispatcher
+) : DisabledAppsDataSource {
+
+    /**
+     * Gets a Flow producer which gets the current list of disabled apps installed for this user
+     * and found at [Settings.Secure.getString]
+     * for Key [CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE].
+     *
+     * The Flow also pushes new list if there is any updates in the list of disabled apps.
+     */
+    override fun getDisabledApps(): Flow<List<ResolveInfo>> {
+        return callbackFlow {
+            trySend(
+                getLauncherActivitiesForRestrictedApps(
+                    packageManager,
+                    contentResolver,
+                    CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
+                    PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR,
+                    MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                )
+            )
+            if (Looper.myLooper() == null) {
+                Looper.prepare()
+            }
+
+            val looper: Looper =
+                Looper.myLooper().takeIf { it != null } ?: Looper.getMainLooper().also {
+                    Log.d(TAG, "Current thread looper is null, fallback to MainLooper")
+                }
+
+            val disabledAppsObserver = object : ContentObserver(Handler(looper)) {
+                override fun onChange(selfChange: Boolean) {
+                    super.onChange(selfChange)
+                    trySend(
+                        getLauncherActivitiesForRestrictedApps(
+                            packageManager,
+                            contentResolver,
+                            CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE,
+                            PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR,
+                            MATCH_DISABLED_UNTIL_USED_COMPONENTS
+                        )
+                    )
+                }
+            }
+
+            contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(
+                    CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE
+                ),
+                false,
+                disabledAppsObserver
+            )
+
+            awaitClose {
+                // If MainLooper do not quit it. MainLooper always stays alive.
+                if (looper != Looper.getMainLooper()) {
+                    looper.quitSafely()
+                }
+                contentResolver.unregisterContentObserver(disabledAppsObserver)
+            }
+        }.flowOn(bgDispatcher).conflate()
+    }
+
+    companion object {
+        val TAG: String = DisabledAppsDataSourceImpl::class.java.simpleName
+        const val PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR = ";"
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/RestrictedAppsUtils.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/RestrictedAppsUtils.kt
new file mode 100644
index 0000000..8ed4a3c
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/RestrictedAppsUtils.kt
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources.restricted
+
+import android.content.ContentResolver
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.GET_RESOLVED_FILTER
+import android.content.pm.ResolveInfo
+import android.provider.Settings
+import android.text.TextUtils
+import android.util.ArraySet
+
+/**
+ * Helper class for Restricted category of launcher apps.
+ */
+internal object RestrictedAppsUtils {
+
+    /**
+     * @param contentResolver required to retrieve secure strings from Settings.
+     * @param secureKey key used to store the list of packages.
+     * @param separator separator used for packages in the stored string.
+     *
+     * @return Set of packages stored in [Settings.Secure] with key [secureKey]
+     */
+    fun getRestrictedPackages(
+        contentResolver: ContentResolver,
+        secureKey: String,
+        separator: String
+    ): Set<String> {
+        val settingsValue = Settings.Secure.getString(
+            contentResolver,
+            secureKey
+        )
+
+        return if (TextUtils.isEmpty(settingsValue)) {
+            ArraySet()
+        } else {
+            ArraySet(
+            listOf(
+                *settingsValue.split(
+                    separator.toRegex()
+                ).dropLastWhile { it.isEmpty() }.toTypedArray()
+            )
+        )
+        }
+    }
+
+    /**
+     * @param packageManager required to queryIntentActivities category [Intent.CATEGORY_LAUNCHER].
+     * @param contentResolver required to retrieve secure string from [Settings.Secure].
+     * @param secureKey key used to store the list of packages.
+     * @param separator separator used for packages in the stored string.
+     *
+     * @return List of ResolveInfo for restricted launcher activities filtered by packages found at
+     *  [Settings.Secure] with key [secureKey].
+     */
+    fun getLauncherActivitiesForRestrictedApps(
+        packageManager: PackageManager,
+        contentResolver: ContentResolver,
+        secureKey: String,
+        separator: String,
+        filter: Int
+    ): List<ResolveInfo> {
+        return packageManager.queryIntentActivities(
+            Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER),
+            PackageManager.ResolveInfoFlags.of(
+                (GET_RESOLVED_FILTER or filter).toLong()
+            )
+        ).filter {
+            getRestrictedPackages(
+                contentResolver,
+                secureKey,
+                separator
+            ).contains(it.activityInfo.packageName)
+        }
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/TosDataSource.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/TosDataSource.kt
new file mode 100644
index 0000000..9111758
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datasources/restricted/TosDataSource.kt
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.datasources.restricted
+
+import android.car.settings.CarSettings
+import android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS
+import android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED
+import android.content.ContentResolver
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS
+import android.content.pm.ResolveInfo
+import android.database.ContentObserver
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import com.android.car.carlauncher.datasources.restricted.RestrictedAppsUtils.getLauncherActivitiesForRestrictedApps
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
+
+/**
+ * DataSource exposes a flow which tracks list of tos(Terms of Service) disabled apps.
+ */
+interface TosDataSource {
+    fun getTosState(): Flow<TosState>
+}
+
+data class TosState(
+    val shouldBlockTosApps: Boolean,
+    val restrictedApps: List<ResolveInfo> = emptyList()
+)
+
+/**
+ * Impl of [TosDataSource], to surface all DisabledApps apis.
+ *
+ * All the operations in this class are non blocking.
+ */
+class TosDataSourceImpl(
+    private val contentResolver: ContentResolver,
+    private val packageManager: PackageManager,
+    private val bgDispatcher: CoroutineDispatcher,
+) : TosDataSource {
+
+    /**
+     * Gets a Flow producer which gets if TOS is accepted by the user and
+     * the current list of tos disabled apps installed for this user and found at
+     * [Settings.Secure.getString] for Key [CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS]
+     * __only if__ [TosState.hasAccepted] is false which is reflected by
+     * [CarSettings.Secure.KEY_USER_TOS_ACCEPTED]
+     *
+     * The Flow also pushes new list if there is any updates in the list of disabled apps.
+     * @return Flow of [TosState]
+     */
+    override fun getTosState(): Flow<TosState> {
+        if (isTosAccepted()) {
+            // Tos is accepted, so we do not need to block the apps.
+            // We can assume the list of blocked apps as empty in this case.
+            return flowOf(
+                TosState(
+                    false,
+                    emptyList()
+                )
+            )
+        }
+
+        return callbackFlow {
+            trySend(
+                TosState(
+                    shouldBlockTosApps(),
+                    getLauncherActivitiesForRestrictedApps(
+                        packageManager,
+                        contentResolver,
+                        KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                        TOS_DISABLED_APPS_SEPARATOR,
+                        MATCH_DISABLED_COMPONENTS
+                    )
+                )
+            )
+            if (Looper.myLooper() == null) {
+                Looper.prepare()
+            }
+
+            val looper: Looper =
+                Looper.myLooper().takeIf { it != null } ?: Looper.getMainLooper().also {
+                    Log.d(TAG, "Current thread looper is null, fallback to MainLooper")
+                }
+            // Tos Accepted Observer
+            val tosStateObserver = object : ContentObserver(Handler(looper)) {
+                override fun onChange(selfChange: Boolean) {
+                    super.onChange(selfChange)
+                    val isAccepted = isTosAccepted()
+                    val restrictedApps: List<ResolveInfo> = if (isAccepted) {
+                        // We don't need to observe the changes once TOS is accepted
+                        contentResolver.unregisterContentObserver(this)
+                        // if TOS is accepted, we can assume that TOS disabled apps will be empty
+                        emptyList()
+                    } else {
+                        getLauncherActivitiesForRestrictedApps(
+                            packageManager,
+                            contentResolver,
+                            KEY_UNACCEPTED_TOS_DISABLED_APPS,
+                            TOS_DISABLED_APPS_SEPARATOR,
+                            MATCH_DISABLED_COMPONENTS
+                        )
+                    }
+                    trySend(TosState(shouldBlockTosApps(), restrictedApps))
+                }
+            }
+
+            contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(KEY_UNACCEPTED_TOS_DISABLED_APPS),
+                false,
+                tosStateObserver
+            )
+
+            contentResolver.registerContentObserver(
+                Settings.Secure.getUriFor(KEY_USER_TOS_ACCEPTED),
+                false,
+                tosStateObserver
+            )
+
+            awaitClose {
+                // If MainLooper do not quit it. MainLooper always stays alive.
+                if (looper != Looper.getMainLooper()) {
+                    looper.quitSafely()
+                }
+                contentResolver.unregisterContentObserver(tosStateObserver)
+            }
+        }.flowOn(bgDispatcher).conflate()
+    }
+
+    /**
+     * Check if a user has accepted TOS
+     * @return true if the user has accepted Tos, false otherwise
+     */
+    private fun isTosAccepted(): Boolean {
+        val settingsValue = Settings.Secure.getString(
+            contentResolver,
+            KEY_USER_TOS_ACCEPTED
+        )
+        return settingsValue == TOS_ACCEPTED
+    }
+
+    /**
+     * Only block the apps to the user when the TOS state is [TOS_NOT_ACCEPTED]
+     *
+     * Note: Even when TOS state is [TOS_UNINITIALIZED] we do not want to block tos apps.
+     */
+    private fun shouldBlockTosApps(): Boolean {
+        val settingsValue = Settings.Secure.getString(
+            contentResolver,
+            KEY_USER_TOS_ACCEPTED
+        )
+        return settingsValue == TOS_NOT_ACCEPTED
+    }
+
+    companion object {
+        // This value indicates if TOS is in uninitialized state
+        const val TOS_UNINITIALIZED = "0"
+
+        // This value indicates if TOS has not been accepted by the user
+        const val TOS_NOT_ACCEPTED = "1"
+
+        // This value indicates if TOS has been accepted by the user
+        const val TOS_ACCEPTED = "2"
+        const val TOS_DISABLED_APPS_SEPARATOR = ","
+
+        private val TAG = TosDataSourceImpl::class.java.simpleName
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/datastore/ProtoDataSource.java b/libs/appgrid/lib/src/com/android/car/carlauncher/datastore/ProtoDataSource.java
index bb1f348..16ea685 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/datastore/ProtoDataSource.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/datastore/ProtoDataSource.java
@@ -64,35 +64,46 @@
     }
 
     /**
-     * Writes the {@link MessageLite} subclass T to the file represented by this object.
+     * Writes the {@link MessageLite} subclass T to the file represented by this object in the
+     * background thread.
      */
-    public void writeToFile(T data) {
+    public void writeToFileInBackgroundThread(T data) {
         ExecutorService executorService = Executors.newSingleThreadExecutor();
         executorService.execute(() -> {
-            try {
-                if (mOutputStream == null) {
-                    mOutputStream = new FileOutputStream(getDataFile(), false);
-                }
-                writeDelimitedTo(data, mOutputStream);
-            } catch (IOException e) {
-                Log.e(TAG, "Launcher item list not written to file successfully.");
-            } finally {
-                try {
-                    if (mOutputStream != null) {
-                        mOutputStream.flush();
-                        mOutputStream.getFD().sync();
-                        mOutputStream.close();
-                        mOutputStream = null;
-                    }
-                } catch (IOException e) {
-                    Log.e(TAG, "Unable to close output stream. ");
-                }
-            }
+            writeToFile(data);
             executorService.shutdown();
         });
     }
 
     /**
+     * Writes the {@link MessageLite} subclass T to the file represented by this object.
+     */
+    public boolean writeToFile(T data) {
+        boolean success = true;
+        try {
+            if (mOutputStream == null) {
+                mOutputStream = new FileOutputStream(getDataFile(), false);
+            }
+            writeDelimitedTo(data, mOutputStream);
+        } catch (IOException e) {
+            Log.e(TAG, "Launcher item list not written to file successfully.");
+            success = false;
+        } finally {
+            try {
+                if (mOutputStream != null) {
+                    mOutputStream.flush();
+                    mOutputStream.getFD().sync();
+                    mOutputStream.close();
+                    mOutputStream = null;
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to close output stream. ");
+            }
+        }
+        return success;
+    }
+
+    /**
      * Reads the {@link MessageLite} subclass T from the file represented by this object.
      */
     @Nullable
@@ -123,6 +134,21 @@
     }
 
     /**
+     * @return True if delete file was successful, false otherwise
+     */
+    public boolean deleteFile() {
+        boolean success = false;
+        try {
+            if (mFile.exists()) {
+                success = mFile.delete();
+            }
+        } catch (SecurityException ex) {
+            Log.e(TAG, "deleteFile - " + ex);
+        }
+        return success;
+    }
+
+    /**
      * This method will be called by {@link ProtoDataSource#readFromFile}.
      *
      * Implementation is left to subclass since {@link MessageLite.parseDelimitedFrom(InputStream)}
@@ -137,7 +163,8 @@
     protected abstract T parseDelimitedFrom(InputStream inputStream) throws IOException;
 
     /**
-     * This method will be called by {@link ProtoDataSource#writeToFile(MessageLite)}.
+     * This method will be called by
+     * {@link ProtoDataSource#writeToFileInBackgroundThread(MessageLite)}.
      *
      * Implementation is left to subclass since {@link MessageLite#writeDelimitedTo(OutputStream)}
      * requires a defined class at compile time. Subclasses should implement this method by directly
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/pagination/PaginationController.java b/libs/appgrid/lib/src/com/android/car/carlauncher/pagination/PaginationController.java
index f12bf93..e144da5 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/pagination/PaginationController.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/pagination/PaginationController.java
@@ -19,8 +19,6 @@
 import android.view.View;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 
-import com.android.car.carlauncher.Banner;
-import com.android.car.carlauncher.R;
 import com.android.car.carlauncher.pagination.PageMeasurementHelper.GridDimensions;
 import com.android.car.carlauncher.pagination.PageMeasurementHelper.PageDimensions;
 
@@ -33,20 +31,15 @@
 public class PaginationController {
     private final PageMeasurementHelper mPageMeasurementHelper;
     private final DimensionUpdateCallback mCallback;
-    // Terms of Service Banner
-    private final Banner mTosBanner;
 
     public PaginationController(View windowBackground, DimensionUpdateCallback callback) {
         mCallback = callback;
         mPageMeasurementHelper = new PageMeasurementHelper(windowBackground);
-        mTosBanner = windowBackground.findViewById(R.id.tos_banner);
         windowBackground.getViewTreeObserver().addOnGlobalLayoutListener(
                 new OnGlobalLayoutListener() {
                     @Override
                     public void onGlobalLayout() {
-                        // We need to subtract the banner height from the available window height
-                        // available to the app_grid
-                        int windowHeight = windowBackground.getMeasuredHeight() - getBannerHeight();
+                        int windowHeight = windowBackground.getMeasuredHeight();
                         int windowWidth = windowBackground.getMeasuredWidth();
                         maybeHandleWindowResize(windowWidth, windowHeight);
                     }
@@ -61,13 +54,6 @@
         }
     }
 
-    private int getBannerHeight() {
-        if (mTosBanner.getVisibility() == View.VISIBLE) {
-            return mTosBanner.getMeasuredHeight();
-        }
-        return 0;
-    }
-
     /**
      * Callback contract between this controller and its {@link DimensionUpdateListener} classes.
      *
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppGridAdapter.java b/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppGridAdapter.java
index a7d0c89..6640570 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppGridAdapter.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppGridAdapter.java
@@ -29,12 +29,11 @@
 import androidx.recyclerview.widget.DiffUtil;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.car.carlauncher.AppGridActivity.Mode;
+import com.android.car.carlauncher.AppGridFragment.Mode;
 import com.android.car.carlauncher.AppGridPageSnapper;
 import com.android.car.carlauncher.AppItem;
 import com.android.car.carlauncher.LauncherItem;
 import com.android.car.carlauncher.LauncherItemDiffCallback;
-import com.android.car.carlauncher.LauncherViewModel;
 import com.android.car.carlauncher.R;
 import com.android.car.carlauncher.RecentAppsRowViewHolder;
 import com.android.car.carlauncher.pagination.PageIndexingHelper;
@@ -59,7 +58,6 @@
     private final int mNumOfRows;
     private int mAppItemWidth;
     private int mAppItemHeight;
-    private final LauncherViewModel mLauncherViewModel;
     // grid order of the mLauncherItems used by DiffUtils in dispatchUpdates to animate UI updates
     private final List<LauncherItem> mGridOrderedLauncherItems;
 
@@ -70,40 +68,25 @@
     private Rect mPageBound;
     private Mode mAppGridMode;
 
-    public AppGridAdapter(Context context, int numOfCols, int numOfRows,
-            LauncherViewModel launcherViewModel, AppItemViewHolder.AppItemDragCallback dragCallback,
-            AppGridPageSnapper.AppGridPageSnapCallback snapCallback) {
-        this(context, numOfCols, numOfRows,
-                context.getResources().getBoolean(R.bool.use_vertical_app_grid)
-                        ? PageOrientation.VERTICAL : PageOrientation.HORIZONTAL,
-                LayoutInflater.from(context), launcherViewModel, dragCallback, snapCallback);
-    }
+    private AppGridAdapterListener mAppGridAdapterListener;
 
     public AppGridAdapter(Context context, int numOfCols, int numOfRows,
-            @PageOrientation int pageOrientation,
-            LayoutInflater layoutInflater, LauncherViewModel launcherViewModel,
             AppItemViewHolder.AppItemDragCallback dragCallback,
-            AppGridPageSnapper.AppGridPageSnapCallback snapCallback) {
-        this(context, numOfCols, numOfRows, pageOrientation, layoutInflater,
-                launcherViewModel, dragCallback, snapCallback, Mode.ALL_APPS);
-    }
-
-    public AppGridAdapter(Context context, int numOfCols, int numOfRows,
-            @PageOrientation int pageOrientation,
-            LayoutInflater layoutInflater, LauncherViewModel launcherViewModel,
-            AppItemViewHolder.AppItemDragCallback dragCallback,
-            AppGridPageSnapper.AppGridPageSnapCallback snapCallback, Mode mode) {
+            AppGridPageSnapper.AppGridPageSnapCallback snapCallback,
+            AppGridAdapterListener appGridAdapterListener,
+            Mode mode) {
         mContext = context;
-        mInflater = layoutInflater;
+        mInflater = LayoutInflater.from(context);
         mNumOfCols = numOfCols;
         mNumOfRows = numOfRows;
         mDragCallback = dragCallback;
         mSnapCallback = snapCallback;
-
+        int pageOrientation =  context.getResources().getBoolean(R.bool.use_vertical_app_grid)
+                ? PageOrientation.VERTICAL : PageOrientation.HORIZONTAL;
         mIndexingHelper = new PageIndexingHelper(numOfCols, numOfRows, pageOrientation);
         mGridOrderedLauncherItems = new ArrayList<>();
-        mLauncherViewModel = launcherViewModel;
         mAppGridMode = mode;
+        mAppGridAdapterListener = appGridAdapterListener;
     }
 
     /**
@@ -142,8 +125,8 @@
      * This should only be called by onChanged() in the observer as a response to data change in the
      * adapter's LauncherViewModel.
      */
-    public void setLauncherItems(List<LauncherItem> launcherItems) {
-        mLauncherItems = launcherItems;
+    public void setLauncherItems(List<? extends LauncherItem> launcherItems) {
+        mLauncherItems = (List<LauncherItem>) launcherItems;
         int newSnapPosition = mSnapCallback.getSnapPosition();
         if (newSnapPosition != 0 && newSnapPosition >= getItemCount()) {
             // in case user deletes the only app item on the last page, the page should snap to the
@@ -273,7 +256,7 @@
         // we need to move package to target index even if the from and to index are the same to
         // ensure dispatchLayout gets called to re-anchor the recyclerview to current page.
         AppItem selectedApp = (AppItem) mLauncherItems.get(adaptorIndexFrom);
-        mLauncherViewModel.setAppPosition(adaptorIndexTo, selectedApp.getAppMetaData());
+        mAppGridAdapterListener.onAppPositionChanged(adaptorIndexTo, selectedApp);
     }
 
 
@@ -343,4 +326,8 @@
         }
         return mIndexingHelper.adaptorIndexToGridPosition(targetAdapterIndex);
     }
+
+    public interface AppGridAdapterListener {
+        void onAppPositionChanged(int newPosition, AppItem appItem);
+    }
 }
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppItemViewHolder.java b/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppItemViewHolder.java
index b7a40bb..92dc941 100644
--- a/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppItemViewHolder.java
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/recyclerview/AppItemViewHolder.java
@@ -22,7 +22,7 @@
 import static com.android.car.carlauncher.AppGridConstants.AppItemBoundDirection;
 import static com.android.car.carlauncher.AppGridConstants.PageOrientation;
 import static com.android.car.carlauncher.AppGridConstants.isHorizontal;
-import static com.android.car.carlauncher.hidden.HiddenApiAccess.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION;
+import static com.android.car.hidden.apis.HiddenApiAccess.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION;
 
 import android.content.ClipData;
 import android.content.ComponentName;
@@ -42,17 +42,17 @@
 import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.recyclerview.widget.RecyclerView;
 
-import com.android.car.carlauncher.AppGridActivity;
+import com.android.car.carlauncher.AppGridFragment;
 import com.android.car.carlauncher.AppGridPageSnapper.AppGridPageSnapCallback;
 import com.android.car.carlauncher.AppItemDragShadowBuilder;
 import com.android.car.carlauncher.AppMetaData;
 import com.android.car.carlauncher.R;
+import com.android.car.carlaunchercommon.toasts.NonDrivingOptimizedLaunchFailedToast;
 
 /**
  * App item view holder that contains the app icon and name.
@@ -98,18 +98,18 @@
     public static class BindInfo {
         private final boolean mIsDistractionOptimizationRequired;
         private final Rect mPageBound;
-        private final AppGridActivity.Mode mMode;
+        private final AppGridFragment.Mode mMode;
 
         public BindInfo(boolean isDistractionOptimizationRequired,
                 Rect pageBound,
-                AppGridActivity.Mode mode) {
+                AppGridFragment.Mode mode) {
             this.mIsDistractionOptimizationRequired = isDistractionOptimizationRequired;
             this.mPageBound = pageBound;
             this.mMode = mode;
         }
 
         public BindInfo(boolean isDistractionOptimizationRequired, Rect pageBound) {
-            this(isDistractionOptimizationRequired, pageBound, AppGridActivity.Mode.ALL_APPS);
+            this(isDistractionOptimizationRequired, pageBound, AppGridFragment.Mode.ALL_APPS);
         }
     }
 
@@ -160,7 +160,7 @@
         }
         boolean isDistractionOptimizationRequired = bindInfo.mIsDistractionOptimizationRequired;
         mPageBound = bindInfo.mPageBound;
-        AppGridActivity.Mode mode = bindInfo.mMode;
+        AppGridFragment.Mode mode = bindInfo.mMode;
 
         mHasAppMetadata = true;
         mAppItemView.setFocusable(true);
@@ -182,8 +182,8 @@
         // previous page, so we need to rebind the app with the correct visibility.
         setStateSelected(mComponentName.equals(mDragCallback.mSelectedComponent));
 
-        boolean isLaunchableDistractionOptimized =
-                !isDistractionOptimizationRequired || app.getIsDistractionOptimized();
+        boolean isLaunchableDistractionOptimized = !isDistractionOptimizationRequired
+                || app.getIsDistractionOptimized();
         boolean isDisabledByTos = app.getIsDisabledByTos();
         boolean isLaunchable = isLaunchableDistractionOptimized || isDisabledByTos;
 
@@ -238,7 +238,7 @@
                                 mActionDownX,
                                 mActionDownY,
                                 mode)) {
-                            startDragAndDrop(app.getComponentName(), event.getX(), event.getY());
+                            startDragAndDrop(app, event.getX(), event.getY());
                             mCanStartDragAction = false;
                         } else if (action == MotionEvent.ACTION_UP
                                 || action == MotionEvent.ACTION_CANCEL) {
@@ -253,14 +253,9 @@
                 });
             }
         } else {
-            String warningText = mContext.getResources()
-                    .getString(R.string.driving_toast_text, app.getDisplayName());
-            View.OnClickListener appLaunchListener = new View.OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    Toast.makeText(mContext, warningText, Toast.LENGTH_LONG).show();
-                }
-            };
+            View.OnClickListener appLaunchListener = v ->
+                    NonDrivingOptimizedLaunchFailedToast.Companion.showToast(
+                            mContext, app.getDisplayName());
             mAppItemView.setOnClickListener(appLaunchListener);
             mAppIcon.setOnClickListener(appLaunchListener);
 
@@ -379,9 +374,9 @@
 
 
     private boolean shouldStartDragAndDrop(MotionEvent event, float actionDownX,
-            float actionDownY, AppGridActivity.Mode mode) {
+            float actionDownY, AppGridFragment.Mode mode) {
         // If App Grid is not in all apps mode, we should not allow drag and drop
-        if (mode != AppGridActivity.Mode.ALL_APPS) {
+        if (mode != AppGridFragment.Mode.ALL_APPS) {
             return false;
         }
         // the move event should be with in the bounds of the app icon
@@ -395,18 +390,21 @@
                 && isDistancePastThreshold;
     }
 
-    private void startDragAndDrop(ComponentName componentName, float eventX, float eventY) {
+    private void startDragAndDrop(AppMetaData app, float eventX, float eventY) {
         ClipData clipData = ClipData.newPlainText(/* label= */ APP_ITEM_DRAG_TAG,
-                /* text= */ componentName.flattenToString());
+                /* text= */ app.getComponentName().flattenToString());
 
         // since the app icon is scaled, the touch point that users should be holding when drag
         // shadow is deployed should also be scaled
         Point dragPoint = new Point(/* x */ (int) (eventX / mIconSize * mIconScaledSize),
                 /* y */ (int) (eventY / mIconSize * mIconScaledSize));
 
-        AppItemDragShadowBuilder dragShadowBuilder = new AppItemDragShadowBuilder(mAppIcon,
+        Drawable appIcon = app.getIcon();
+        if (appIcon.getConstantState() == null) return;
+        AppItemDragShadowBuilder dragShadowBuilder = new AppItemDragShadowBuilder(
+                appIcon.getConstantState().newDrawable().mutate(),
                 /* touchPointX */ dragPoint.x, /* touchPointX */ dragPoint.y,
-                /* size */ mIconSize, /* scaledSize */ mIconScaledSize);
+                /* scaledSize */ mIconScaledSize);
         mAppIcon.startDragAndDrop(clipData, /* dragShadowBuilder */ dragShadowBuilder,
                 /* myLocalState */ null, /* flags */ DRAG_FLAG_OPAQUE | DRAG_FLAG_GLOBAL
                         | DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION);
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/AppGridRepository.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/AppGridRepository.kt
new file mode 100644
index 0000000..f9376a6
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/AppGridRepository.kt
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.repositories
+
+import android.Manifest.permission.MANAGE_OWN_CALLS
+import android.content.ComponentName
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.NameNotFoundException
+import android.content.pm.ResolveInfo
+import android.graphics.drawable.Drawable
+import android.os.UserManager
+import android.util.Log
+import com.android.car.carlauncher.AppItem
+import com.android.car.carlauncher.AppMetaData
+import com.android.car.carlauncher.datasources.AppOrderDataSource
+import com.android.car.carlauncher.datasources.AppOrderDataSource.AppOrderInfo
+import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSource
+import com.android.car.carlauncher.datasources.LauncherActivitiesDataSource
+import com.android.car.carlauncher.datasources.MediaTemplateAppsDataSource
+import com.android.car.carlauncher.datasources.UXRestrictionDataSource
+import com.android.car.carlauncher.datasources.restricted.DisabledAppsDataSource
+import com.android.car.carlauncher.datasources.restricted.TosDataSource
+import com.android.car.carlauncher.datasources.restricted.TosState
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType.DISABLED
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType.LAUNCHER
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType.MEDIA
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType.MIRRORING
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProviderFactory.AppLauncherProviderType.TOS_DISABLED
+import com.android.car.carlauncher.repositories.appactions.AppShortcutsFactory
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+interface AppGridRepository {
+
+    /**
+     * Returns a flow of all applications available in the app grid, including
+     * system apps, media apps, and potentially restricted apps.
+     *
+     * @return A Flow emitting a list of AppItem objects.
+     */
+    fun getAllAppsList(): Flow<List<AppItem>>
+
+    /**
+     * Provides a flow indicating whether distraction optimization is required for the device.
+     * Distraction optimization might limit the features or visibility of apps.
+     *
+     * @return A Flow emitting a Boolean value, where `true` indicates distraction optimization is
+     * needed.
+     */
+    fun requiresDistractionOptimization(): Flow<Boolean>
+
+    /**
+     * Returns a continuous flow representing the Terms of Service (ToS) state for apps.
+     * This state may determine the availability or restrictions of certain apps.
+     *
+     * @return A Flow emitting the current TosState.
+     */
+    fun getTosState(): Flow<TosState>
+
+    /**
+     * Suspends execution to save the provided app order to persistent storage.
+     * Used to maintain the arrangement of apps within the app grid.
+     *
+     * @param currentAppOrder A list of AppItem representing the desired app order.
+     */
+    suspend fun saveAppOrder(currentAppOrder: List<AppItem>)
+
+    /**
+     * Returns a flow of media-related apps installed on the device.
+     *
+     * @return A Flow emitting a list of AppItem representing media applications.
+     */
+    fun getMediaAppsList(): Flow<List<AppItem>>
+}
+
+/**
+ * The core implementation of the AppGridRepository interface.  This class is responsible for:
+ *
+ * *  Fetching and combining app information from various sources (launcher activities,
+ *    media services, disabled apps, etc.)
+ * *  Applying restrictions and filtering app lists based on distraction optimization, ToS status,
+ *    and mirroring state.
+ * *  Managing app order and saving it to persistent storage.
+ * *  Providing real-time updates of the app grid as changes occur.
+ */
+class AppGridRepositoryImpl(
+    private val launcherActivities: LauncherActivitiesDataSource,
+    private val mediaTemplateApps: MediaTemplateAppsDataSource,
+    private val disabledApps: DisabledAppsDataSource,
+    private val tosApps: TosDataSource,
+    private val controlCenterMirroring: ControlCenterMirroringDataSource,
+    private val uxRestriction: UXRestrictionDataSource,
+    private val appOrder: AppOrderDataSource,
+    private val packageManager: PackageManager,
+    private val appLaunchFactory: AppLaunchProviderFactory,
+    private val appShortcutsFactory: AppShortcutsFactory,
+    userManager: UserManager,
+    private val bgDispatcher: CoroutineDispatcher
+) : AppGridRepository {
+
+    private val isVisibleBackgroundUser = !userManager.isUserForeground &&
+        userManager.isUserVisible && !userManager.isProfile
+
+    /**
+     * Provides a flow of all apps in the app grid.
+     * It combines data from multiple sources, filters apps based on restrictions, handles dynamic
+     * updates and returns the list in the last known savedOrder.
+     *
+     * @return A Flow emitting lists of AppItem objects.
+     */
+    override fun getAllAppsList(): Flow<List<AppItem>> {
+        return combine(
+            getAllLauncherAndMediaApps(),
+            getRestrictedApps(),
+            controlCenterMirroring.getAppMirroringSession(),
+            appOrder.getSavedAppOrderComparator(),
+            uxRestriction.isDistractionOptimized()
+        ) { apps, restrictedApps, mirroringSession, order, isDistractionOptimized ->
+            val alreadyAddedComponents = apps.map { it.componentName.packageName }.toSet()
+            return@combine (apps + restrictedApps.filterNot {
+                it.componentName.packageName in alreadyAddedComponents
+            }).sortedWith { a1, a2 ->
+                order.compare(a1.appOrderInfo, a2.appOrderInfo)
+            }.filter {
+                !shouldHideApp(it)
+            }.map {
+                if (mirroringSession.packageName == it.componentName.packageName) {
+                    it.redirectIntent = mirroringSession.launchIntent
+                } else if (it.launchActionType == MIRRORING) {
+                    it.redirectIntent = null
+                }
+                it.toAppItem(isDistractionOptimized(it.componentName, it.launchActionType == MEDIA))
+            }
+        }.flowOn(bgDispatcher).distinctUntilChanged()
+    }
+
+    /**
+     * Emitting distraction optimization status changes.
+     *
+     * @return A Flow of Boolean values, where `true` indicates distraction optimization is
+     * required.
+     */
+    override fun requiresDistractionOptimization(): Flow<Boolean> {
+        return uxRestriction.requiresDistractionOptimization()
+    }
+
+    /**
+     * Provides the Terms of Service state for apps.
+     *
+     * @return A Flow emitting the current TosState.
+     */
+    override fun getTosState(): Flow<TosState> {
+        return tosApps.getTosState()
+    }
+
+    /**
+     *  Suspends saving the given app order to persistent storage.
+     *  Updates to the app order are posted to the subscribers of
+     *  [AppGridRepositoryImpl.getAllAppsList]
+     *
+     * @param currentAppOrder A list of AppItem representing the desired app order.
+     */
+    override suspend fun saveAppOrder(currentAppOrder: List<AppItem>) {
+        appOrder.saveAppOrder(currentAppOrder.toAppOrderInfoList())
+    }
+
+    /**
+     * Providing a flow of media-related apps.
+     * Handles dynamic updates to the list of media apps.
+     *
+     * @return A Flow emitting lists of AppItem objects representing media apps.
+     */
+    override fun getMediaAppsList(): Flow<List<AppItem>> {
+        return launcherActivities.getOnPackagesChanged().map {
+            mediaTemplateApps.getAllMediaServices(true).map {
+                it.toAppInfo(MEDIA).toAppItem(true)
+            }
+        }.flowOn(bgDispatcher).distinctUntilChanged()
+    }
+
+    private fun getAllLauncherAndMediaApps(): Flow<List<AppInfo>> {
+        return launcherActivities.getOnPackagesChanged().map {
+            val launcherApps = launcherActivities.getAllLauncherActivities().map {
+                AppInfo(it.label, it.componentName, it.getBadgedIcon(0), LAUNCHER)
+            }
+            val mediaTemplateApps = mediaTemplateApps.getAllMediaServices(false).map {
+                it.toAppInfo(MEDIA)
+            }
+            launcherApps + mediaTemplateApps
+        }.flowOn(bgDispatcher).distinctUntilChanged()
+    }
+
+    private fun getRestrictedApps(): Flow<List<AppInfo>> {
+        return disabledApps.getDisabledApps()
+            .combine(tosApps.getTosState()) { disabledApps, tosApps ->
+                return@combine disabledApps.map {
+                    it.toAppInfo(DISABLED)
+                } + tosApps.restrictedApps.map {
+                    it.toAppInfo(TOS_DISABLED)
+                }
+            }.flowOn(bgDispatcher).distinctUntilChanged()
+    }
+
+    private data class AppInfo(
+        val displayName: CharSequence,
+        val componentName: ComponentName,
+        val icon: Drawable,
+        private val _launchActionType: AppLauncherProviderType,
+        var redirectIntent: Intent? = null
+    ) {
+        val launchActionType get() = if (redirectIntent == null) {
+            _launchActionType
+        } else {
+            MIRRORING
+        }
+
+        val appOrderInfo =
+            AppOrderInfo(componentName.packageName, componentName.className, displayName.toString())
+    }
+
+    private fun AppInfo.toAppItem(isDistractionOptimized: Boolean): AppItem {
+        val metaData = AppMetaData(
+            displayName,
+            componentName,
+            icon,
+            isDistractionOptimized,
+            launchActionType == MIRRORING,
+            launchActionType == TOS_DISABLED,
+            { context ->
+                appLaunchFactory
+                    .get(launchActionType)
+                    ?.launch(context, componentName, redirectIntent)
+            },
+            { contextViewPair ->
+                appShortcutsFactory.showShortcuts(
+                    componentName,
+                    displayName,
+                    contextViewPair.first,
+                    contextViewPair.second
+                )
+            }
+        )
+        return AppItem(metaData)
+    }
+
+    private fun ResolveInfo.toAppInfo(launchActionType: AppLauncherProviderType): AppInfo {
+        val componentName: ComponentName
+        val icon: Drawable
+        if (launchActionType == MEDIA) {
+            componentName = ComponentName(serviceInfo.packageName, serviceInfo.name)
+            icon = serviceInfo.loadIcon(packageManager)
+        } else {
+            componentName = ComponentName(activityInfo.packageName, activityInfo.name)
+            icon = activityInfo.loadIcon(packageManager)
+        }
+        return AppInfo(
+            loadLabel(packageManager),
+            componentName,
+            icon,
+            launchActionType
+        )
+    }
+
+    private fun List<AppItem>.toAppOrderInfoList(): List<AppOrderInfo> {
+        return map { AppOrderInfo(it.packageName, it.className, it.displayName.toString()) }
+    }
+
+    private fun shouldHideApp(appInfo: AppInfo): Boolean {
+        // Disable telephony apps for MUMD passenger since accepting a call will
+        // drop the driver's call.
+        if (isVisibleBackgroundUser) {
+            return try {
+                packageManager.getPackageInfo(
+                    appInfo.componentName.packageName, PackageManager.GET_PERMISSIONS)
+                    .requestedPermissions?.any {it == MANAGE_OWN_CALLS} ?: false
+            } catch (e: NameNotFoundException) {
+                Log.e(TAG, "Unable to query app permissions for $appInfo $e")
+                false
+            }
+        }
+
+        return false
+    }
+
+    companion object {
+        const val TAG = "AppGridRepository"
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProvider.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProvider.kt
new file mode 100644
index 0000000..7c78b80
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProvider.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.repositories.appactions
+
+import android.app.ActivityOptions
+import android.car.Car
+import android.car.media.CarMediaManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.car.carlauncher.R
+import java.net.URISyntaxException
+
+/**
+ * A sealed class representing various ways to launch applications within the system.
+ * Subclasses define specialized launch behaviors for different app types (launcher
+ * activities, media services, disabled apps, etc.).
+ *
+ * @see LauncherActivityLaunchProvider
+ * @see MediaServiceLaunchProvider
+ * @see DisabledAppLaunchProvider
+ * @see TosDisabledAppLaunchProvider
+ * @see MirroringAppLaunchProvider
+ */
+sealed class AppLaunchProvider {
+
+    /**
+     *  Launches the app associated with the given ComponentName. This is the core
+     *  method that all subclasses must implement.
+     *
+     *  @param context Application context used for launching the activity.
+     *  @param componentName The ComponentName identifying the app to launch.
+     *  @param launchIntent An optional Intent that might contain additional launch instructions.
+     */
+    abstract fun launch(
+        context: Context,
+        componentName: ComponentName,
+        launchIntent: Intent? = null
+    )
+
+    /**
+     * Utility method to launch an Intent with consistent ActivityOptions
+     */
+    protected fun launchApp(context: Context, intent: Intent) {
+        val options = ActivityOptions.makeBasic()
+        options.launchDisplayId = context.display?.displayId ?: 0
+        context.startActivity(intent, options.toBundle())
+    }
+
+    /**
+     * Handles launching standard launcher activities. It constructs an Intent with
+     * the necessary flags for launching a new instance of the given app.
+     */
+    internal object LauncherActivityLaunchProvider : AppLaunchProvider() {
+        override fun launch(context: Context, componentName: ComponentName, launchIntent: Intent?) {
+            launchApp(
+                context,
+                Intent(Intent.ACTION_MAIN).setComponent(componentName)
+                    .addCategory(Intent.CATEGORY_LAUNCHER).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            )
+        }
+    }
+
+    /**
+     * Launches media services, either by directly starting a media center template or
+     * switching active media sources. This provider works in conjunction with the CarMediaManager.
+     *
+     * @param carMediaManager Interface for controlling car media settings.
+     * @param launchMediaCenter If true, launches the media center template directly.
+     * @param closeScreen A callback function, likely used to close a prior UI screen.
+     */
+    internal class MediaServiceLaunchProvider(
+        private val carMediaManager: CarMediaManager,
+        private val launchMediaCenter: Boolean,
+        val closeScreen: () -> Unit
+    ) : AppLaunchProvider() {
+        override fun launch(context: Context, componentName: ComponentName, launchIntent: Intent?) {
+            if (launchMediaCenter) {
+                launchApp(
+                    context,
+                    Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE).putExtra(
+                        Car.CAR_EXTRA_MEDIA_COMPONENT,
+                        componentName.flattenToString()
+                    )
+                )
+                return
+            }
+            carMediaManager.setMediaSource(componentName, CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)
+            closeScreen()
+        }
+    }
+
+    /**
+     * Responsible for enabling and then launching disabled applications. Requires access
+     * to the PackageManager to manage application state.
+     */
+    internal class DisabledAppLaunchProvider(
+        private val packageManager: PackageManager,
+    ) : AppLaunchProvider() {
+        override fun launch(context: Context, componentName: ComponentName, launchIntent: Intent?) {
+            packageManager.setApplicationEnabledSetting(
+                componentName.packageName,
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                0
+            )
+            // Fetch the current enabled setting to make sure the setting is synced
+            // before launching the activity. Otherwise, the activity may not
+            // launch.
+            check(
+                packageManager.getApplicationEnabledSetting(componentName.packageName)
+                        == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+            ) {
+                ("Failed to enable the disabled package [" + componentName.packageName + "]")
+            }
+            Log.i(TAG, "Successfully enabled package [${componentName.packageName}]")
+            launchApp(
+                context,
+                Intent(Intent.ACTION_MAIN).setComponent(componentName)
+                    .addCategory(Intent.CATEGORY_LAUNCHER).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            )
+        }
+
+        companion object {
+            const val TAG = "DisabledAppLaunchProvider"
+        }
+    }
+
+    /**
+     * Handles launching the system activity for Terms of Service (TOS) acceptance. This
+     * provider redirects the user to the TOS flow for apps restricted by TOS.
+     */
+    internal object TosDisabledAppLaunchProvider : AppLaunchProvider() {
+        override fun launch(context: Context, componentName: ComponentName, launchIntent: Intent?) {
+            val tosIntentName = context.resources.getString(R.string.user_tos_activity_intent)
+            try {
+                launchApp(context, Intent.parseUri(tosIntentName, Intent.URI_ANDROID_APP_SCHEME))
+            } catch (se: URISyntaxException) {
+                Log.e(TAG, "Invalid intent URI in user_tos_activity_intent", se)
+            }
+        }
+
+        const val TAG = "TosDisabledAppLaunchProvider"
+    }
+
+    /**
+     * Handles app mirroring scenarios. Assumes that a launchIntent is provided with the
+     * necessary information to launch the mirrored app.
+     */
+    internal object MirroringAppLaunchProvider :
+        AppLaunchProvider() {
+        override fun launch(context: Context, componentName: ComponentName, launchIntent: Intent?) {
+            launchIntent?.let {
+                launchApp(context, it)
+            }
+        }
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProviderFactory.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProviderFactory.kt
new file mode 100644
index 0000000..befd702
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppLaunchProviderFactory.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.repositories.appactions
+
+import android.car.media.CarMediaManager
+import android.content.pm.PackageManager
+import android.util.Log
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProvider.DisabledAppLaunchProvider
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProvider.LauncherActivityLaunchProvider
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProvider.MediaServiceLaunchProvider
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProvider.MirroringAppLaunchProvider
+import com.android.car.carlauncher.repositories.appactions.AppLaunchProvider.TosDisabledAppLaunchProvider
+
+/**
+ * Acts as a central factory for obtaining specialized AppLaunchProvider instances.
+ * It maintains an internal mapping between AppLauncherProviderType enum values and
+ * the corresponding provider objects. The factory is responsible for:
+ *
+ * * Registering available AppLaunchProvider implementations
+ * * Providing access to providers based on the requested AppLauncherProviderType
+ */
+class AppLaunchProviderFactory(
+    carMediaManager: CarMediaManager,
+    launchMediaCenter: Boolean,
+    onMediaSelected: () -> Unit,
+    packageManager: PackageManager
+) {
+
+    private val providerMap = mutableMapOf<AppLauncherProviderType, AppLaunchProvider>()
+
+    /**
+     * Enumerates the supported types of AppLaunchProvider implementations. Used
+     * internally by the factory to differentiate and retrieve providers.
+     */
+    enum class AppLauncherProviderType {
+        LAUNCHER, MEDIA, DISABLED, TOS_DISABLED, MIRRORING
+    }
+
+    init {
+        // Add all providers.
+        addProvider(LauncherActivityLaunchProvider)
+        addProvider(MediaServiceLaunchProvider(carMediaManager, launchMediaCenter, onMediaSelected))
+        addProvider(DisabledAppLaunchProvider(packageManager))
+        addProvider(TosDisabledAppLaunchProvider)
+        addProvider(MirroringAppLaunchProvider)
+    }
+
+    /**
+     * Retrieves the AppLaunchProvider associated with the specified AppLauncherProviderType.
+     *
+     * @param providerType The type of AppLaunchProvider to retrieve.
+     * @return The corresponding AppLaunchProvider if registered, otherwise null.
+     */
+    fun get(providerType: AppLauncherProviderType): AppLaunchProvider? {
+        return providerMap[providerType].also {
+            if (it == null) {
+                Log.i(TAG, "Launch provider for ${providerType.name} missing")
+            }
+        }
+    }
+
+    /**
+     * Registers an AppLaunchProvider within the factory. Providers are associated with
+     * their corresponding AppLauncherProviderType.
+     *
+     * @param provider The AppLaunchProvider instance to register.
+     */
+    private fun addProvider(provider: AppLaunchProvider) {
+        when (provider) {
+            is DisabledAppLaunchProvider -> {
+                providerMap[AppLauncherProviderType.DISABLED] = provider
+            }
+
+            LauncherActivityLaunchProvider -> {
+                providerMap[AppLauncherProviderType.LAUNCHER] = provider
+            }
+
+            is MediaServiceLaunchProvider -> {
+                providerMap[AppLauncherProviderType.MEDIA] = provider
+            }
+
+            is MirroringAppLaunchProvider -> {
+                providerMap[AppLauncherProviderType.MIRRORING] = provider
+            }
+
+            is TosDisabledAppLaunchProvider -> {
+                providerMap[AppLauncherProviderType.TOS_DISABLED] = provider
+            }
+        }
+    }
+
+    companion object {
+        const val TAG = "AppLaunchProviderFactory"
+    }
+}
diff --git a/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppShortcutsFactory.kt b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppShortcutsFactory.kt
new file mode 100644
index 0000000..37f0c0e
--- /dev/null
+++ b/libs/appgrid/lib/src/com/android/car/carlauncher/repositories/appactions/AppShortcutsFactory.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2024 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.carlauncher.repositories.appactions
+
+import android.car.media.CarMediaManager
+import android.content.ComponentName
+import android.content.Context
+import android.os.Process
+import android.os.UserHandle
+import android.view.View
+import com.android.car.carlaunchercommon.shortcuts.AppInfoShortcutItem
+import com.android.car.carlaunchercommon.shortcuts.ForceStopShortcutItem
+import com.android.car.carlaunchercommon.shortcuts.PinShortcutItem
+import com.android.car.dockutil.Flags
+import com.android.car.dockutil.events.DockEventSenderHelper
+import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
+
+/**
+ * This class is responsible for creating and displaying app shortcuts popups within the
+ * car UI. It generates shortcuts for actions like "Stop App," "App Info," and potentially
+ * "Pin to Dock." The class interacts with CarMediaManager and relies on a ShortcutsListener
+ * to track interactions with the shortcuts popup.
+ *
+ * @param carMediaManager For controlling car media settings.
+ * @param mediaServiceComponents A set of ComponentNames identifying installed media services.
+ * @param shortcutsListener Listener for handling events triggered by the shortcuts popup.
+ */
+class AppShortcutsFactory(
+    private val carMediaManager: CarMediaManager,
+    private val mediaServiceComponents: Set<ComponentName>,
+    private val shortcutsListener: ShortcutsListener
+) {
+
+    /**
+     * Displays a car UI shortcuts popup anchored to the provided view.  The popup includes
+     * shortcuts for "Force Stop," "App Info," and potentially "Pin to Dock" (if the feature
+     * is enabled).
+     *
+     * @param componentName The ComponentName of the app for which shortcuts are generated.
+     * @param displayName The display name of the app.
+     * @param context Application context.
+     * @param anchorView The UI view to anchor the shortcuts popup.
+     */
+    fun showShortcuts(
+        componentName: ComponentName,
+        displayName: CharSequence,
+        context: Context,
+        anchorView: View
+    ) {
+        val carUiShortcutsPopupBuilder =
+            CarUiShortcutsPopup.Builder()
+                .addShortcut(
+                    ForceStopShortcutItem(
+                        context,
+                        componentName.packageName,
+                        displayName,
+                        carMediaManager,
+                        mediaServiceComponents
+                    )
+                )
+                .addShortcut(
+                    AppInfoShortcutItem(
+                        context,
+                        componentName.packageName,
+                        UserHandle.getUserHandleForUid(Process.myUid())
+                    )
+                )
+        if (Flags.dockFeature()) {
+            carUiShortcutsPopupBuilder
+                .addShortcut(buildPinToDockShortcut(componentName, context))
+        }
+        val carUiShortcutsPopup =
+            carUiShortcutsPopupBuilder
+                .build(context, anchorView)
+        carUiShortcutsPopup.show()
+        shortcutsListener.onShortcutsShow(carUiShortcutsPopup)
+    }
+
+    /**
+     * Helper function to construct a shortcut item for the "Pin to Dock" action
+     * within the shortcuts popup.
+     *
+     * @param componentName ComponentName of the app to be pinned.
+     * @param context Application context.
+     * @return A CarUiShortcutsPopup.ShortcutItem for the "Pin to Dock" action, or null
+     *         if the feature is not enabled.
+     */
+    private fun buildPinToDockShortcut(
+        componentName: ComponentName,
+        context: Context
+    ): CarUiShortcutsPopup.ShortcutItem? {
+        val helper = DockEventSenderHelper(context)
+        return PinShortcutItem(
+            context.resources,
+            false,
+            { helper.sendPinEvent(componentName) }
+        )
+        { helper.sendUnpinEvent(componentName) }
+    }
+
+    /**
+     *  Simple callback interface for notifying clients when a car UI shortcuts
+     *  popup is displayed.
+     */
+    interface ShortcutsListener {
+        /**
+         *  Called when a CarUiShortcutsPopup view becomes visible.
+         *
+         *  @param carUiShortcutsPopup The displayed popup view.
+         */
+        fun onShortcutsShow(carUiShortcutsPopup: CarUiShortcutsPopup)
+    }
+}
diff --git a/libs/appgrid/lib/tests/Android.bp b/libs/appgrid/lib/tests/Android.bp
index 1e8c043..4314dfd 100644
--- a/libs/appgrid/lib/tests/Android.bp
+++ b/libs/appgrid/lib/tests/Android.bp
@@ -15,64 +15,67 @@
 
 package {
     default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
 }
 
 android_test {
-  name: "CarAppGridTests",
+    name: "CarAppGridTests",
 
-  srcs: ["src/**/*.java"],
+    srcs: ["src/**/*.java"],
 
-  resource_dirs: ["res"],
+    resource_dirs: ["res"],
 
-  libs: [
-      "android.car",
-      "android.test.base",
-      "android.car-system-stubs",
-  ],
+    libs: [
+        "android.car",
+        "android.test.base.stubs.system",
+        "android.car-system-stubs",
+    ],
 
-  optimize: {
-      enabled: false,
-  },
+    optimize: {
+        enabled: false,
+    },
 
-  static_libs: [
-      "android.car.testapi",
-      "android.car.test.utils",
-      "androidx.test.core",
-      "androidx.test.runner",
-      "androidx.test.rules",
-      "androidx.test.espresso.core",
-      "androidx.test.espresso.contrib",
-      "androidx.test.espresso.intents",
-      "androidx.test.ext.junit",
-      "hamcrest-library",
-      "mockito-target-extended",
-      "truth",
-      "testables",
-      "CarAppGrid-lib"
-  ],
+    static_libs: [
+        "android.car.testapi",
+        "android.car.test.utils",
+        "androidx.test.core",
+        "androidx.test.runner",
+        "androidx.test.rules",
+        "androidx.test.espresso.core",
+        "androidx.test.espresso.contrib",
+        "androidx.test.espresso.intents",
+        "androidx.test.ext.junit",
+        "hamcrest-library",
+        "mockito-target-extended",
+        "truth",
+        "testables",
+        "CarAppGrid-lib",
+    ],
 
-  platform_apis: true,
+    platform_apis: true,
 
-  certificate: "platform",
+    certificate: "platform",
 
-  privileged: true,
+    privileged: true,
 
-  manifest: "AndroidManifest.xml",
+    manifest: "AndroidManifest.xml",
 
-  instrumentation_for: "CarAppGrid-lib",
+    instrumentation_for: "CarAppGrid-lib",
 
-  dex_preopt: {
-      enabled: false,
-  },
+    dex_preopt: {
+        enabled: false,
+    },
 
-  jni_libs: [
-      // For mockito extended
-      "libdexmakerjvmtiagent",
-      "libstaticjvmtiagent",
-  ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
 
-  test_suites: [
-      "automotive-tests",
-      "device-tests",
-  ],
+    test_suites: [
+        "automotive-tests",
+        "device-tests",
+    ],
+    // TODO(b/319708040): re-enable use_resource_processor
+    use_resource_processor: false,
 }
diff --git a/docklib-util/res/values/strings.xml b/libs/appgrid/lib/tests/res/values-en-rCA/strings.xml
similarity index 70%
copy from docklib-util/res/values/strings.xml
copy to libs/appgrid/lib/tests/res/values-en-rCA/strings.xml
index 2c24df7..f7fcedc 100644
--- a/docklib-util/res/values/strings.xml
+++ b/libs/appgrid/lib/tests/res/values-en-rCA/strings.xml
@@ -1,5 +1,5 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!--
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
   ~ Copyright (C) 2023 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
@@ -13,10 +13,9 @@
   ~ 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.
-  -->
+   -->
 
-<resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_test_title" msgid="4167394338298199728">"AppGridTests"</string>
 </resources>
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridActivityTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridActivityTest.java
deleted file mode 100644
index d67191b..0000000
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridActivityTest.java
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
-import static android.car.settings.CarSettings.Secure.KEY_USER_TOS_ACCEPTED;
-
-import static androidx.test.espresso.Espresso.onView;
-import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static androidx.test.espresso.matcher.ViewMatchers.withId;
-
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.Mockito.atLeast;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-
-import android.car.drivingstate.CarUxRestrictionsManager;
-import android.content.Intent;
-import android.provider.Settings;
-import android.testing.TestableContext;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.core.app.ActivityScenario;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-
-/**
- * Programmatic tests for AppGridActivity and AppGridScrollBar
- */
-@RunWith(AndroidJUnit4.class)
-public class AppGridActivityTest {
-    private ActivityScenario<AppGridActivity> mActivityScenario;
-    private CarUxRestrictionsManager mCarUxRestrictionsManager;
-    private PageIndicator mPageIndicator;
-
-    @After
-    public void tearDown() {
-        if (mActivityScenario != null) {
-            mActivityScenario.close();
-        }
-    }
-
-    @Test
-    public void onCreate_appGridRecyclerView_isVisible() {
-        mActivityScenario = ActivityScenario.launch(AppGridActivity.class);
-        onView(withId(R.id.apps_grid)).check(matches(isDisplayed()));
-        onView(withId(R.id.page_indicator_container)).check(matches(isDisplayed()));
-    }
-
-    @Test
-    public void onResume_ScrollStateIsUpdated() {
-        mActivityScenario = ActivityScenario.launch(AppGridActivity.class);
-        mActivityScenario.onActivity(activity -> {
-            mPageIndicator = mock(PageIndicator.class);
-            activity.setPageIndicator(mPageIndicator);
-        });
-        // The activity needs to be reset to go to the RESUMED state after the
-        // mock object is set up
-        mActivityScenario.moveToState(Lifecycle.State.CREATED);
-        mActivityScenario.moveToState(Lifecycle.State.RESUMED);
-        onView(withId(R.id.apps_grid)).check(matches(isDisplayed()));
-        onView(withId(R.id.page_indicator_container)).check(matches(isDisplayed()));
-        // onResumed will trigger LauncherViewModel.onChanged when the activity first started
-        // which triggers this call again
-        verify(mPageIndicator, atLeast(1)).updatePageCount(anyInt());
-    }
-
-    @Test
-    public void onStop_CarUxRestrictionsManager_unregisterListener() {
-        mActivityScenario = ActivityScenario.launch(AppGridActivity.class);
-        mActivityScenario.onActivity(activity -> {
-            mCarUxRestrictionsManager = mock(CarUxRestrictionsManager.class);
-            activity.setCarUxRestrictionsManager(mCarUxRestrictionsManager);
-        });
-        mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
-        verify(mCarUxRestrictionsManager, times(1)).unregisterListener();
-    }
-
-    @Test
-    public void onCreate_tosIsAccepted_tosContentObserversAreNull() {
-        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
-        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 2);
-
-
-        mActivityScenario = ActivityScenario.launch(new Intent(mContext, AppGridActivity.class));
-
-        mActivityScenario.onActivity(activity -> {
-            assertNull(activity.mTosContentObserver); // Content observer not setup
-            assertNull(activity.mTosDisabledAppsContentObserver); // Content observer not setup
-        });
-    }
-
-    @Test
-    public void afterTosIsAccepted_unregisterTosContentObservers() {
-        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
-        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
-
-        mActivityScenario = ActivityScenario.launch(new Intent(mContext, AppGridActivity.class));
-
-        mActivityScenario.onActivity(activity -> {
-            assertNotNull(activity.mTosContentObserver); // Content observer is setup
-            assertNotNull(activity.mTosDisabledAppsContentObserver); // Content observer is setup
-
-            // Accept TOS
-            Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 2);
-            activity.mTosContentObserver.onChange(true);
-        });
-
-        // Content observer is null after tos is accepted
-        mActivityScenario.onActivity(activity -> {
-            assertNull(activity.mTosContentObserver);
-            assertNull(activity.mTosDisabledAppsContentObserver);
-        });
-    }
-
-    @Test
-    public void tosUninitialized_changesToTosUnaccepted_doNotUnregisterTosContentObservers() {
-        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
-        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 0);
-
-        mActivityScenario = ActivityScenario.launch(new Intent(mContext, AppGridActivity.class));
-
-        mActivityScenario.onActivity(activity -> {
-            assertNotNull(activity.mTosContentObserver); // Content observer is setup
-            assertNotNull(activity.mTosDisabledAppsContentObserver); // Content observer is setup
-
-            // TOS changed to unaccepted
-            Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
-            activity.mTosContentObserver.onChange(true);
-        });
-
-        // Content observer is not null after tos is unaccepted
-        mActivityScenario.onActivity(activity -> {
-            assertNotNull(activity.mTosContentObserver);
-            assertNotNull(activity.mTosDisabledAppsContentObserver);
-        });
-    }
-
-    @Test
-    public void
-            tosNotAccepted_tosDisabledAppsUpdate_doNotUnregisterTosDisabledAppsContentObserver() {
-        TestableContext mContext = new TestableContext(InstrumentationRegistry.getContext());
-        Settings.Secure.putInt(mContext.getContentResolver(), KEY_USER_TOS_ACCEPTED, 1);
-        Settings.Secure.putString(
-                mContext.getContentResolver(),
-                KEY_UNACCEPTED_TOS_DISABLED_APPS,
-                "tos_disabled_app_one,tos_disabled_app_2");
-
-        mActivityScenario = ActivityScenario.launch(new Intent(mContext, AppGridActivity.class));
-
-        mActivityScenario.onActivity(activity -> {
-            assertNotNull(activity.mTosContentObserver); // Content observer is setup
-            assertNotNull(activity.mTosDisabledAppsContentObserver); // Content observer is setup
-
-            // TOS changed to unaccepted
-            Settings.Secure.putString(mContext.getContentResolver(),
-                    KEY_UNACCEPTED_TOS_DISABLED_APPS,
-                    "tos_disabled_app_one");
-            activity.mTosContentObserver.onChange(true);
-        });
-
-        // Content observer is not null after tos is unaccepted
-        mActivityScenario.onActivity(activity -> {
-            assertNotNull(activity.mTosContentObserver);
-            assertNotNull(activity.mTosDisabledAppsContentObserver);
-        });
-    }
-}
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridAdapterTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridAdapterTest.java
index ab357b0..b22e9e8 100644
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridAdapterTest.java
+++ b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppGridAdapterTest.java
@@ -25,9 +25,10 @@
 
 import android.content.Context;
 import android.graphics.Rect;
-import android.view.LayoutInflater;
 import android.view.View;
 
+import androidx.test.platform.app.InstrumentationRegistry;
+
 import com.android.car.carlauncher.pagination.PageIndexingHelper;
 import com.android.car.carlauncher.recyclerview.AppGridAdapter;
 import com.android.car.carlauncher.recyclerview.AppItemViewHolder;
@@ -37,18 +38,18 @@
 import org.mockito.Mock;
 
 public class AppGridAdapterTest {
-
-    @Mock public Context mMockContext;
-    @Mock public LayoutInflater mMockLayoutInflater;
-    @Mock public LauncherViewModel mMockLauncherModel;
-    @Mock public AppItemViewHolder.AppItemDragCallback mMockDragCallback;
-    @Mock public AppGridPageSnapper.AppGridPageSnapCallback mMockSnapCallback;
-    @Mock public Rect mMockPageBound;
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    @Mock
+    public AppItemViewHolder.AppItemDragCallback mMockDragCallback;
+    @Mock
+    public AppGridPageSnapper.AppGridPageSnapCallback mMockSnapCallback;
+    @Mock
+    public Rect mMockPageBound;
     public AppGridAdapter mTestAppGridAdapter;
 
     @Before
     public void setUp() throws Exception {
-        mMockLauncherModel = mock(LauncherViewModel.class);
         mMockDragCallback = mock(AppItemViewHolder.AppItemDragCallback.class);
         mMockSnapCallback = mock(AppGridPageSnapper.AppGridPageSnapCallback.class);
     }
@@ -57,9 +58,9 @@
     public void testPageRounding_getItemCount_getPageCount() {
         int numOfCols = 5;
         int numOfRows = 3;
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
@@ -85,9 +86,9 @@
         numOfCols = 4;
         numOfRows = 6;
 
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
@@ -114,9 +115,9 @@
         // an adapter with 45 items
         int numOfCols = 5;
         int numOfRows = 3;
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
@@ -149,9 +150,9 @@
         // an adapter with 45 items
         int numOfRows = 5;
         int numOfCols = 3;
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                /* pageOrientation */ PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
@@ -192,9 +193,9 @@
         // an adapter with 40 items, 3 page, and 5 padded empty items
         int numOfCols = 5;
         int numOfRows = 3;
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
@@ -245,9 +246,9 @@
         // an adapter with 44 items, 3 page, and 16 padded empty items
         int numOfCols = 4;
         int numOfRows = 5;
-        mTestAppGridAdapter = new AppGridAdapter(mMockContext, numOfCols, numOfRows,
-                PageOrientation.HORIZONTAL,
-                mMockLayoutInflater, mMockLauncherModel, mMockDragCallback, mMockSnapCallback);
+        mTestAppGridAdapter = new AppGridAdapter(mContext, numOfCols, numOfRows,
+                mMockDragCallback, mMockSnapCallback,
+                mock(AppGridAdapter.AppGridAdapterListener.class), AppGridFragment.Mode.ALL_APPS);
         mTestAppGridAdapter.updateViewHolderDimensions(mMockPageBound,
                 /* appItemWidth */ 260, /* appItemHeight */ 200);
         mTestAppGridAdapter = spy(mTestAppGridAdapter);
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppItemViewHolderTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppItemViewHolderTest.java
deleted file mode 100644
index 9642c21..0000000
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppItemViewHolderTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.view.View;
-import android.view.ViewTreeObserver;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import androidx.test.core.app.ApplicationProvider;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.car.carlauncher.recyclerview.AppItemViewHolder;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-@RunWith(AndroidJUnit4.class)
-public class AppItemViewHolderTest {
-
-    private static final String TEST_APP_PACKAGE_NAME = "com.android.car.test";
-    private static final String TEST_TOS_DISABLED_APP_CLASS_NAME = "TosDisabledApp";
-
-    @Mock private View mView;
-    @Mock private Context mContext;
-    @Mock private AppItemViewHolder.AppItemDragCallback mDragCallback;
-    @Mock private AppGridPageSnapper.AppGridPageSnapCallback mSnapCallback;
-    @Mock private ImageView mAppIcon;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mContext = ApplicationProvider.getApplicationContext();
-    }
-
-    @Test
-    public void testTosDisabledAppsOpacity() {
-        setupMocksTosDisabledApps();
-
-        AppItemViewHolder appItemViewHolder =
-                new AppItemViewHolder(mView, mContext, mDragCallback, mSnapCallback);
-
-        ComponentName componentName =
-                new ComponentName(TEST_APP_PACKAGE_NAME, TEST_TOS_DISABLED_APP_CLASS_NAME);
-
-        AppMetaData metaData =
-                new AppMetaData(
-                        null,
-                        componentName,
-                        null,
-                        true,
-                        false,
-                        true,
-                        null,
-                        null);
-
-        appItemViewHolder.bind(metaData, new AppItemViewHolder.BindInfo(false, null));
-
-        verify(mAppIcon).setAlpha(0.46f);
-    }
-
-    private void setupMocksTosDisabledApps() {
-        LinearLayout appItemView = mock(LinearLayout.class);
-        ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
-        TextView appName = mock(TextView.class);
-
-        when(mView.findViewById(R.id.app_item)).thenReturn(appItemView);
-        when(appItemView.findViewById(R.id.app_icon)).thenReturn(mAppIcon);
-        when(appItemView.findViewById(R.id.app_name)).thenReturn(appName);
-        when(mAppIcon.getViewTreeObserver()).thenReturn(viewTreeObserver);
-    }
-}
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java
deleted file mode 100644
index c027a72..0000000
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/AppLauncherUtilsTest.java
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import static android.car.settings.CarSettings.Secure.KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE;
-import static android.car.settings.CarSettings.Secure.KEY_UNACCEPTED_TOS_DISABLED_APPS;
-import static android.content.pm.ApplicationInfo.CATEGORY_AUDIO;
-import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
-import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
-import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
-
-import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_LAUNCHABLES;
-import static com.android.car.carlauncher.AppLauncherUtils.APP_TYPE_MEDIA_SERVICES;
-import static com.android.car.carlauncher.AppLauncherUtils.PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR;
-import static com.android.car.carlauncher.AppLauncherUtils.TOS_DISABLED_APPS_SEPARATOR;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.car.Car;
-import android.car.content.pm.CarPackageManager;
-import android.car.media.CarMediaManager;
-import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.LauncherActivityInfo;
-import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.res.Resources;
-import android.os.Bundle;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.service.media.MediaBrowserService;
-import android.util.ArraySet;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.ArgumentMatchers;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public final class AppLauncherUtilsTest extends AbstractExtendedMockitoTestCase {
-    private static final String TEST_DISABLED_APP_1 = "com.android.car.test.disabled1";
-    private static final String TEST_DISABLED_APP_2 = "com.android.car.test.disabled2";
-    private static final String TEST_ENABLED_APP = "com.android.car.test.enabled";
-    private static final String TEST_TOS_DISABLED_APP_1 = "com.android.car.test.tosdisabled1";
-    private static final String TEST_TOS_DISABLED_APP_2 = "com.android.car.test.tosdisabled2";
-    private static final String TEST_VIDEO_APP = "com.android.car.test.video";
-    // Default media app
-    private static final String TEST_MEDIA_TEMPLATE_MBS = "com.android.car.test.mbs";
-    // Video app that has a MBS defined but has its own launch activity
-    private static final String TEST_VIDEO_MBS = "com.android.car.test.video.mbs";
-    // NDO App that has opted in its MBS to launch in car
-    private static final String TEST_NDO_MBS_LAUNCHABLE = "com.android.car.test.mbs.launchable";
-    // NDO App that has opted out its MBS to launch in car
-    private static final String TEST_NDO_MBS_NOT_LAUNCHABLE =
-            "com.android.car.test.mbs.notlaunchable";
-
-    private static final String CUSTOM_MEDIA_PACKAGE = "com.android.car.radio";
-    private static final String CUSTOM_MEDIA_CLASS = "com.android.car.radio.service";
-    private static final String CUSTOM_MEDIA_COMPONENT = CUSTOM_MEDIA_PACKAGE
-            + "/" + CUSTOM_MEDIA_CLASS;
-    private static final String TEST_MIRROR_APP_PKG = "com.android.car.test.mirroring";
-    private static final String TOS_INTENT_NAME = "intent:#Intent;action="
-            + "com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;"
-            + "S.mini_flow_extra=GTOS_GATED_FLOW;end";
-    private static final String TOS_INTENT_VERIFY = "#Intent;action="
-            + "com.android.car.SHOW_USER_TOS_ACTIVITY;B.show_value_prop=true;"
-            + "S.mini_flow_extra=GTOS_GATED_FLOW;end";
-
-
-    @Mock private Context mMockContext;
-    @Mock private LauncherApps mMockLauncherApps;
-    @Mock private PackageManager mMockPackageManager;
-    @Mock private AppLauncherUtils.ShortcutsListener mMockShortcutsListener;
-
-    @Mock private Resources mResources;
-
-    @Mock private LauncherActivityInfo mRadioLauncherActivityInfo;
-
-    private CarMediaManager mCarMediaManager;
-    private CarPackageManager mCarPackageManager;
-    private Car mCar;
-
-    @Before
-    public void setUp() throws Exception {
-        // Need for CarMediaManager to get the user from the context.
-        when(mMockContext.getUser()).thenReturn(UserHandle.of(ActivityManager.getCurrentUser()));
-
-        mCar = Car.createCar(mMockContext, /* handler = */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
-                (car, ready) -> {
-                    if (!ready) {
-                        mCarPackageManager = null;
-                        mCarMediaManager = null;
-                        return;
-                    }
-                    mCarPackageManager = (CarPackageManager) car.getCarManager(Car.PACKAGE_SERVICE);
-                    mCarPackageManager = Mockito.spy(mCarPackageManager);
-                    mCarMediaManager = (CarMediaManager) car.getCarManager(Car.CAR_MEDIA_SERVICE);
-                    when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
-                });
-    }
-
-    @After
-    public void tearDown() throws Exception {
-        if (mCar != null && mCar.isConnected()) {
-            mCar.disconnect();
-            mCar = null;
-        }
-    }
-
-    @Override
-    protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
-        session.spyStatic(Settings.Secure.class);
-    }
-
-    @Test
-    public void testGetLauncherApps_MediaCenterAppSwitcher() {
-        mockSettingsStringCalls();
-        mockPackageManagerQueries();
-
-        when(mMockContext.getResources()).thenReturn(mResources);
-        when(mResources.getStringArray(eq(
-                com.android.car.media.common.R.array.custom_media_packages)))
-                .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
-
-        // Setup custom media component
-        when(mMockLauncherApps.getActivityList(any(), any()))
-                .thenReturn(List.of(mRadioLauncherActivityInfo));
-        when(mRadioLauncherActivityInfo.getComponentName())
-                .thenReturn(new ComponentName(CUSTOM_MEDIA_PACKAGE, CUSTOM_MEDIA_CLASS));
-        when(mRadioLauncherActivityInfo.getName())
-                .thenReturn(CUSTOM_MEDIA_CLASS);
-
-        AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
-                mMockContext, /* appsToHide= */ new ArraySet<>(),
-                /* appTypes= */ APP_TYPE_MEDIA_SERVICES,
-                /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
-                mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
-                TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
-
-        List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
-
-        // Only media apps should be present
-        assertEquals(Set.of(
-                        TEST_MEDIA_TEMPLATE_MBS,
-                        TEST_NDO_MBS_LAUNCHABLE,
-                        CUSTOM_MEDIA_PACKAGE),
-                appMetaData.stream()
-                        .map(am -> am.getComponentName().getPackageName())
-                        .collect(Collectors.toSet()));
-
-        // This should include all MBS discovered
-        assertEquals(5, launcherAppsInfo.getMediaServices().size());
-
-        mockPmGetApplicationEnabledSetting(COMPONENT_ENABLED_STATE_ENABLED, TEST_DISABLED_APP_1,
-                TEST_DISABLED_APP_2);
-
-        launchAllApps(appMetaData);
-
-        // Media apps should do only switching and not launch activity
-        verify(mMockContext, never()).startActivity(any(), any());
-    }
-
-    @Test
-    public void testGetLauncherApps_Launcher() {
-        mockSettingsStringCalls();
-        mockPackageManagerQueries();
-
-        when(mMockContext.getResources()).thenReturn(mResources);
-        when(mResources.getStringArray(eq(
-                com.android.car.media.common.R.array.custom_media_packages)))
-                .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
-
-        // Setup custom media component
-        when(mMockLauncherApps.getActivityList(any(), any()))
-                .thenReturn(List.of(mRadioLauncherActivityInfo));
-        when(mRadioLauncherActivityInfo.getComponentName())
-                .thenReturn(new ComponentName(CUSTOM_MEDIA_PACKAGE, CUSTOM_MEDIA_CLASS));
-        when(mRadioLauncherActivityInfo.getName())
-                .thenReturn(CUSTOM_MEDIA_CLASS);
-
-        AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
-                mMockContext, /* appsToHide= */ new ArraySet<>(),
-                /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
-                /* openMediaCenter= */ true, mMockLauncherApps, mCarPackageManager,
-                mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
-                TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
-
-        List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
-        // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
-
-        assertEquals(Set.of(
-                        TEST_MEDIA_TEMPLATE_MBS,
-                        TEST_NDO_MBS_LAUNCHABLE,
-                        CUSTOM_MEDIA_PACKAGE,
-                        TEST_DISABLED_APP_1,
-                        TEST_DISABLED_APP_2),
-                appMetaData.stream()
-                        .map(am -> am.getComponentName().getPackageName())
-                        .collect(Collectors.toSet()));
-
-
-        // This should include all MBS discovered
-        assertEquals(5, launcherAppsInfo.getMediaServices().size());
-
-        mockPmGetApplicationEnabledSetting(COMPONENT_ENABLED_STATE_ENABLED, TEST_DISABLED_APP_1,
-                TEST_DISABLED_APP_2);
-
-        launchAllApps(appMetaData);
-
-        verify(mMockPackageManager).setApplicationEnabledSetting(
-                eq(TEST_DISABLED_APP_1), eq(COMPONENT_ENABLED_STATE_ENABLED), eq(0));
-
-        verify(mMockPackageManager).setApplicationEnabledSetting(
-                eq(TEST_DISABLED_APP_2), eq(COMPONENT_ENABLED_STATE_ENABLED), eq(0));
-
-        verify(mMockContext, times(5)).startActivity(any(), any());
-
-        verify(mMockPackageManager, never()).setApplicationEnabledSetting(
-                eq(TEST_ENABLED_APP), anyInt(), eq(0));
-    }
-
-
-    @Test
-    public void testGetLauncherAppsWithEnableAndTosDisabledApps() {
-        mockSettingsStringCalls();
-        mockTosPackageManagerQueries();
-
-        when(mMockContext.getResources()).thenReturn(mResources);
-        when(mResources.getStringArray(eq(
-                com.android.car.media.common.R.array.custom_media_packages)))
-                .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
-
-        AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
-                mMockContext, /* appsToHide= */ new ArraySet<>(),
-                /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
-                /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
-                mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
-                TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
-
-        List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
-
-        // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
-        assertEquals(3, appMetaData.size());
-
-        Resources resources = mock(Resources.class);
-        when(mMockContext.getResources()).thenReturn(resources);
-        when(resources.getString(anyInt())).thenReturn(TOS_INTENT_NAME);
-
-        launchAllApps(appMetaData);
-
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mMockContext, times(2)).startActivity(intentCaptor.capture(), any());
-
-        String intentUri = intentCaptor.getAllValues().get(0).toUri(0);
-        assertEquals(TOS_INTENT_VERIFY, intentUri);
-    }
-
-    @Test
-    public void testGetLauncherAppsWithEnableAndTosDisabledDistractionOptimizedApps() {
-        mockSettingsStringCalls();
-        mockTosPackageManagerQueries();
-
-        when(mMockContext.getResources()).thenReturn(mResources);
-        when(mResources.getStringArray(eq(
-                com.android.car.media.common.R.array.custom_media_packages)))
-                .thenReturn(new String[]{CUSTOM_MEDIA_COMPONENT});
-
-        doReturn(true)
-                .when(mCarPackageManager)
-                .isActivityDistractionOptimized(eq(TEST_TOS_DISABLED_APP_1), any());
-        doReturn(true)
-                .when(mCarPackageManager)
-                .isActivityDistractionOptimized(eq(TEST_TOS_DISABLED_APP_2), any());
-
-        AppLauncherUtils.LauncherAppsInfo launcherAppsInfo = AppLauncherUtils.getLauncherApps(
-                mMockContext, /* appsToHide= */ new ArraySet<>(),
-                /* appTypes= */ APP_TYPE_LAUNCHABLES + APP_TYPE_MEDIA_SERVICES,
-                /* openMediaCenter= */ false, mMockLauncherApps, mCarPackageManager,
-                mMockPackageManager, mCarMediaManager, mMockShortcutsListener,
-                TEST_MIRROR_APP_PKG,  /* mirroringAppRedirect= */ null);
-
-        List<AppMetaData> appMetaData = launcherAppsInfo.getLaunchableComponentsList();
-
-        // mMockLauncherApps is never stubbed, only services & disabled activities are expected.
-        assertEquals(3, appMetaData.size());
-
-        Resources resources = mock(Resources.class);
-        when(mMockContext.getResources()).thenReturn(resources);
-        when(resources.getString(anyInt())).thenReturn(TOS_INTENT_NAME);
-
-        launchAllApps(appMetaData);
-
-        ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
-        verify(mMockContext, times(2)).startActivity(intentCaptor.capture(), any());
-
-        String intentUri = intentCaptor.getAllValues().get(0).toUri(0);
-        assertEquals(TOS_INTENT_VERIFY, intentUri);
-    }
-
-    private void forceStopInit(ActivityManager activityManager, CarMediaManager carMediaManager,
-            ComponentName currentMediaComponentName,
-            ComponentName previousMediaComponentName,
-            Map<Integer, Boolean> currentModes, boolean isMedia) {
-        when(mMockContext.getSystemService(
-                ArgumentMatchers.<Class<ActivityManager>>any())).thenReturn(activityManager);
-        when(mMockContext.getResources()).thenReturn(mock(Resources.class));
-        if (isMedia) {
-            currentModes.forEach((mode, current) -> {
-                if (current) {
-                    when(carMediaManager.getMediaSource(mode)).thenReturn(
-                            currentMediaComponentName);
-                } else {
-                    when(carMediaManager.getMediaSource(mode)).thenReturn(
-                            previousMediaComponentName);
-                }
-            });
-            List<ComponentName> lastMediaSources = new ArrayList<>();
-            lastMediaSources.add(currentMediaComponentName);
-            if (previousMediaComponentName != null) {
-                lastMediaSources.add(previousMediaComponentName);
-            }
-            when(carMediaManager.getLastMediaSources(anyInt())).thenReturn(lastMediaSources);
-        } else {
-            when(carMediaManager.getMediaSource(anyInt())).thenReturn(previousMediaComponentName);
-        }
-    }
-
-    @Test
-    public void forceStopNonMediaApp_shouldStopApp() {
-        String packageName = "com.example.app";
-        CharSequence displayName = "App";
-        ActivityManager activityManager = mock(ActivityManager.class);
-        CarMediaManager carMediaManager = mock(CarMediaManager.class);
-        forceStopInit(activityManager, carMediaManager,
-                /* currentMediaComponentName= */null, /* previousMediaComponentName= */null,
-                /* currentModes= */Map.of(), /* isMedia= */false);
-        Map<ComponentName, ResolveInfo> mediaServices = new HashMap<>();
-
-        AppLauncherUtils.forceStop(packageName, mMockContext, displayName, carMediaManager,
-                mediaServices, mMockShortcutsListener);
-
-        verify(activityManager).forceStopPackage(packageName);
-        verify(mMockShortcutsListener).onStopAppSuccess(nullable(String.class));
-        verify(carMediaManager, never()).setMediaSource(nullable(ComponentName.class), anyInt());
-    }
-
-    @Test
-    public void forceStopCurrentPlaybackOnlyMediaApp_shouldSetPlaybackOnlyToPreviousAndStopApp() {
-        String packageName = "com.example.app";
-        CharSequence displayName = "App";
-        ActivityManager activityManager = mock(ActivityManager.class);
-        CarMediaManager carMediaManager = mock(CarMediaManager.class);
-        ComponentName currentMediaComponentName = new ComponentName(packageName,
-                "com.example.service");
-        ComponentName previousMediaComponentName = new ComponentName("test", "test");
-        Map<Integer, Boolean> currentModes = new HashMap<>();
-        currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK, true);
-        currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE, false);
-        forceStopInit(activityManager, carMediaManager, currentMediaComponentName,
-                previousMediaComponentName, /* currentModes= */currentModes, /* isMedia= */true);
-        Map<ComponentName, ResolveInfo> mediaServices = new HashMap<>();
-
-        AppLauncherUtils.forceStop(packageName, mMockContext, displayName, carMediaManager,
-                mediaServices, mMockShortcutsListener);
-
-        verify(activityManager).forceStopPackage(packageName);
-        verify(mMockShortcutsListener).onStopAppSuccess(nullable(String.class));
-        verify(carMediaManager).setMediaSource(previousMediaComponentName,
-                CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK);
-        verify(carMediaManager, never()).setMediaSource(previousMediaComponentName,
-                CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
-
-    }
-
-    @Test
-    public void forceStopCurrentMediaApp_noHistory_shouldSetToOtherMediaServiceAndStopApp() {
-        String packageName = "com.example.app";
-        CharSequence displayName = "App";
-        ActivityManager activityManager = mock(ActivityManager.class);
-        CarMediaManager carMediaManager = mock(CarMediaManager.class);
-        ComponentName currentMediaComponentName = new ComponentName(packageName,
-                "com.example.service");
-        ComponentName otherMediaComponentName = new ComponentName("other.package",
-                "other.test");
-        Map<Integer, Boolean> currentModes = new HashMap<>();
-        currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK, true);
-        currentModes.put(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE, true);
-        forceStopInit(activityManager, carMediaManager, currentMediaComponentName,
-                /* previousMediaComponentName= */null,
-                /* currentModes= */currentModes, /* isMedia= */true);
-        Map<ComponentName, ResolveInfo> mediaServices = new HashMap<>();
-        mediaServices.put(otherMediaComponentName, mock(ResolveInfo.class));
-
-        AppLauncherUtils.forceStop(packageName, mMockContext, displayName, carMediaManager,
-                mediaServices, mMockShortcutsListener);
-
-        verify(activityManager).forceStopPackage(packageName);
-        verify(mMockShortcutsListener).onStopAppSuccess(nullable(String.class));
-        verify(carMediaManager, times(2))
-                .setMediaSource(eq(otherMediaComponentName), anyInt());
-    }
-
-    @Test
-    public void forceStopNonCurrentMediaApp_shouldOnlyStopApp() {
-        String packageName = "com.example.app";
-        CharSequence displayName = "App";
-        ActivityManager activityManager = mock(ActivityManager.class);
-        CarMediaManager carMediaManager = mock(CarMediaManager.class);
-        ComponentName currentMediaComponentName = new ComponentName(packageName,
-                "com.example.service");
-        ComponentName previousMediaComponentName = new ComponentName("test", "test");
-        forceStopInit(activityManager, carMediaManager, currentMediaComponentName,
-                previousMediaComponentName, /* currentModes= */Collections.emptyMap(),
-                /* isMedia= */true);
-        Map<ComponentName, ResolveInfo> mediaServices = new HashMap<>();
-
-        AppLauncherUtils.forceStop(packageName, mMockContext, displayName, carMediaManager,
-                mediaServices, mMockShortcutsListener);
-
-        verify(activityManager).forceStopPackage(packageName);
-        verify(mMockShortcutsListener).onStopAppSuccess(nullable(String.class));
-        verify(carMediaManager, never()).setMediaSource(any(ComponentName.class), anyInt());
-    }
-
-    private void mockPackageManagerQueries() {
-        // setup a media template app that uses media service
-        ApplicationInfo mbsAppInfo = new ApplicationInfo();
-        mbsAppInfo.category = CATEGORY_AUDIO;
-        ResolveInfo mbs = constructServiceResolveInfo(TEST_MEDIA_TEMPLATE_MBS);
-
-        try {
-            Intent mbsIntent = new Intent();
-            mbsIntent.setComponent(mbs.getComponentInfo().getComponentName());
-            mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
-            when(mMockPackageManager.getApplicationInfo(mbs.getComponentInfo().packageName, 0))
-                    .thenReturn(mbsAppInfo);
-
-            doReturn(Arrays.asList(mbs)).when(mMockPackageManager).queryIntentServices(
-                    argThat((Intent i) -> i != null
-                            && mbs.getComponentInfo().getComponentName().equals(i.getComponent())),
-                    eq(PackageManager.GET_META_DATA));
-
-            when(mMockPackageManager.getLaunchIntentForPackage(mbs.getComponentInfo().packageName))
-                    .thenReturn(null);
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
-        // setup a NDO Video app that has MBS but also its own activity, MBS won't be surfaced
-        ApplicationInfo videoAppInfo = new ApplicationInfo();
-        videoAppInfo.category = CATEGORY_VIDEO;
-        ResolveInfo videoApp = constructServiceResolveInfo(TEST_VIDEO_MBS);
-        try {
-            Intent videoMbsIntent = new Intent();
-            videoMbsIntent.setComponent(videoApp.getComponentInfo().getComponentName());
-            videoMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
-            when(mMockPackageManager.getApplicationInfo(videoApp.getComponentInfo().packageName,
-                    0))
-                    .thenReturn(videoAppInfo);
-
-            doReturn(Arrays.asList(videoApp)).when(mMockPackageManager).queryIntentServices(
-                    argThat((Intent i) -> i != null
-                            && videoApp.getComponentInfo().getComponentName()
-                                    .equals(i.getComponent())),
-                    eq(PackageManager.GET_META_DATA));
-
-            when(mMockPackageManager.getLaunchIntentForPackage(
-                    videoApp.getComponentInfo().packageName))
-                    .thenReturn(new Intent());
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
-        // setup a NDO app that has MBS opted out of launch in car
-        ApplicationInfo notlaunchableMBSInfo = new ApplicationInfo();
-        notlaunchableMBSInfo.category = CATEGORY_VIDEO;
-        ResolveInfo notlaunchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_NOT_LAUNCHABLE);
-
-        try {
-            Intent notlaunachableMbsIntent = new Intent();
-            notlaunachableMbsIntent.setComponent(
-                    notlaunchableMBSApp.getComponentInfo().getComponentName());
-            notlaunachableMbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
-            when(mMockPackageManager.getApplicationInfo(
-                    notlaunchableMBSApp.getComponentInfo().packageName, 0))
-                    .thenReturn(notlaunchableMBSInfo);
-
-
-            notlaunchableMBSApp.serviceInfo.metaData = new Bundle();
-            notlaunchableMBSApp.serviceInfo.metaData
-                    .putBoolean("androidx.car.app.launchable", false);
-
-            doReturn(Arrays.asList(notlaunchableMBSApp))
-                    .when(mMockPackageManager).queryIntentServices(
-                    argThat((Intent i) -> i != null
-                            && notlaunchableMBSApp.getComponentInfo().getComponentName()
-                                    .equals(i.getComponent())),
-                    eq(PackageManager.GET_META_DATA));
-
-            when(mMockPackageManager.getLaunchIntentForPackage(
-                    notlaunchableMBSApp.getComponentInfo().packageName))
-                    .thenReturn(new Intent());
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
-
-        // setup a NDO app that has MBS opted in to launch in car
-        ApplicationInfo launchableMBSInfo = new ApplicationInfo();
-        launchableMBSInfo.category = CATEGORY_VIDEO;
-        ResolveInfo launchableMBSApp = constructServiceResolveInfo(TEST_NDO_MBS_LAUNCHABLE);
-        try {
-            Intent mbsIntent = new Intent();
-            mbsIntent.setComponent(launchableMBSApp.getComponentInfo().getComponentName());
-            mbsIntent.setAction(MediaBrowserService.SERVICE_INTERFACE);
-
-            when(mMockPackageManager.getApplicationInfo(
-                    launchableMBSApp.getComponentInfo().packageName,
-                    0))
-                    .thenReturn(launchableMBSInfo);
-
-
-            launchableMBSApp.serviceInfo.metaData = new Bundle();
-            launchableMBSApp.serviceInfo.metaData.putBoolean("androidx.car.app.launchable", true);
-
-            doReturn(Arrays.asList(launchableMBSApp)).when(mMockPackageManager).queryIntentServices(
-                    argThat((Intent i) -> i != null
-                            && launchableMBSApp.getComponentInfo().getComponentName()
-                            .equals(i.getComponent())),
-                    eq(PackageManager.GET_META_DATA));
-
-            when(mMockPackageManager.getLaunchIntentForPackage(
-                    launchableMBSApp.getComponentInfo().packageName))
-                    .thenReturn(new Intent());
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-
-        when(mMockPackageManager.queryIntentServices(any(), eq(PackageManager.GET_RESOLVED_FILTER)))
-                .thenAnswer(args -> {
-            Intent intent = args.getArgument(0);
-            if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
-                return Arrays.asList(mbs, videoApp, notlaunchableMBSApp, launchableMBSApp,
-                        constructServiceResolveInfo(CUSTOM_MEDIA_PACKAGE));
-            }
-            return new ArrayList<>();
-        });
-
-        // setup activities
-        when(mMockPackageManager.queryIntentActivities(any(), any())).thenAnswer(args -> {
-            Intent intent = args.getArgument(0);
-            PackageManager.ResolveInfoFlags flags = args.getArgument(1);
-            List<ResolveInfo> resolveInfoList = new ArrayList<>();
-            if (intent.getAction().equals(Intent.ACTION_MAIN)) {
-                if ((flags.getValue() & MATCH_DISABLED_UNTIL_USED_COMPONENTS) != 0) {
-                    resolveInfoList.add(constructActivityResolveInfo(TEST_DISABLED_APP_1));
-                    resolveInfoList.add(constructActivityResolveInfo(TEST_DISABLED_APP_2));
-                }
-                // Keep custom media component in both MBS and Activity with Launch Intent
-                resolveInfoList.add(constructActivityResolveInfo(CUSTOM_MEDIA_PACKAGE));
-                // Add apps which will have their own Launcher Activity
-                resolveInfoList.add(constructActivityResolveInfo(TEST_VIDEO_MBS));
-                resolveInfoList.add(constructActivityResolveInfo(TEST_NDO_MBS_LAUNCHABLE));
-                resolveInfoList.add(constructActivityResolveInfo(TEST_NDO_MBS_NOT_LAUNCHABLE));
-            }
-
-            return resolveInfoList;
-        });
-    }
-
-    private void mockTosPackageManagerQueries() {
-        ResolveInfo resolveInfo = constructServiceResolveInfo(TEST_ENABLED_APP);
-        try {
-            when(mMockPackageManager.getServiceInfo(
-                    resolveInfo
-                            .getComponentInfo().getComponentName(),
-                    PackageManager.GET_META_DATA))
-                    .thenReturn(new ServiceInfo());
-        } catch (PackageManager.NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-        when(mMockPackageManager.queryIntentServices(any(), anyInt())).thenAnswer(args -> {
-            Intent intent = args.getArgument(0);
-            if (intent.getAction().equals(MediaBrowserService.SERVICE_INTERFACE)) {
-                return Collections.singletonList(resolveInfo);
-            }
-            return new ArrayList<>();
-        });
-        when(mMockPackageManager.queryIntentActivities(any(), any())).thenAnswer(args -> {
-            Intent intent = args.getArgument(0);
-            PackageManager.ResolveInfoFlags flags = args.getArgument(1);
-            List<ResolveInfo> resolveInfoList = new ArrayList<>();
-            if (intent.getAction().equals(Intent.ACTION_MAIN)) {
-                if ((flags.getValue() & MATCH_DISABLED_COMPONENTS) != 0) {
-                    resolveInfoList.add(constructActivityResolveInfo(TEST_TOS_DISABLED_APP_1));
-                    resolveInfoList.add(constructActivityResolveInfo(TEST_TOS_DISABLED_APP_2));
-                }
-                resolveInfoList.add(constructActivityResolveInfo(TEST_ENABLED_APP));
-            }
-            return resolveInfoList;
-        });
-    }
-
-    private void mockPmGetApplicationEnabledSetting(int enabledState, String... packages) {
-        for (String pkg : packages) {
-            when(mMockPackageManager.getApplicationEnabledSetting(pkg)).thenReturn(enabledState);
-        }
-    }
-
-    private void mockSettingsStringCalls() {
-        when(mMockContext.createContextAsUser(any(UserHandle.class), anyInt()))
-                .thenAnswer(args -> {
-                    Context context = mock(Context.class);
-                    ContentResolver contentResolver = mock(ContentResolver.class);
-                    when(context.getContentResolver()).thenReturn(contentResolver);
-                    return context;
-                });
-
-        doReturn(TEST_DISABLED_APP_1 + PACKAGES_DISABLED_ON_RESOURCE_OVERUSE_SEPARATOR
-                + TEST_DISABLED_APP_2)
-                .when(() -> Settings.Secure.getString(any(ContentResolver.class),
-                        eq(KEY_PACKAGES_DISABLED_ON_RESOURCE_OVERUSE)));
-
-        doReturn(TEST_TOS_DISABLED_APP_1 + TOS_DISABLED_APPS_SEPARATOR
-                + TEST_TOS_DISABLED_APP_2)
-                .when(() -> Settings.Secure.getString(any(ContentResolver.class),
-                        eq(KEY_UNACCEPTED_TOS_DISABLED_APPS)));
-    }
-
-    private void launchAllApps(List<AppMetaData> appMetaData) {
-        for (AppMetaData meta : appMetaData) {
-            Consumer<Context> launchCallback = meta.getLaunchCallback();
-            launchCallback.accept(mMockContext);
-        }
-    }
-
-    private static ResolveInfo constructActivityResolveInfo(String packageName) {
-        ResolveInfo info = new ResolveInfo();
-        info.activityInfo = new ActivityInfo();
-        info.activityInfo.packageName = packageName;
-        info.activityInfo.name = packageName + ".activity";
-        info.activityInfo.applicationInfo = new ApplicationInfo();
-        return info;
-    }
-
-    private static ResolveInfo constructServiceResolveInfo(String packageName) {
-        ResolveInfo info = new ResolveInfo();
-        info.serviceInfo = new ServiceInfo();
-        info.serviceInfo.packageName = packageName;
-        info.serviceInfo.name = packageName + ".service";
-        info.serviceInfo.applicationInfo = new ApplicationInfo();
-        return info;
-    }
-}
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/LauncherViewModelTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/LauncherViewModelTest.java
deleted file mode 100644
index 90537c9..0000000
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/LauncherViewModelTest.java
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import android.car.test.mocks.AbstractExtendedMockitoTestCase;
-import android.content.ComponentName;
-import android.graphics.drawable.Drawable;
-
-import androidx.lifecycle.Observer;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-@RunWith(AndroidJUnit4.class)
-public final class LauncherViewModelTest extends AbstractExtendedMockitoTestCase {
-    @Rule
-    public InstantTaskExecutorRule instantTaskExecutorRule =
-            new InstantTaskExecutorRule();
-    private LauncherViewModel mLauncherModel;
-    private AppLauncherUtils.LauncherAppsInfo mLauncherAppsInfo;
-    private Drawable mDrawable = mock(Drawable.class);
-    private Consumer mConsumer = mock(Consumer.class);
-    private List<LauncherItem> mCustomizedApps;
-    private List<LauncherItem> mAlphabetizedApps;
-    private List<AppMetaData> mApps;
-
-    @Before
-    public void setUp() throws Exception {
-        mLauncherModel = new LauncherViewModel(
-                new File("/data/user/10/com.android.car.carlauncher/files"));
-        mCustomizedApps = new ArrayList<>();
-        mAlphabetizedApps = new ArrayList<>();
-        AppMetaData app1 = createTestAppMetaData("App1", "A");
-        AppMetaData app2 = createTestAppMetaData("App2", "B");
-        AppMetaData app3 = createTestAppMetaData("App3", "C");
-        LauncherItem launcherItem1 = new AppItem(app1);
-        LauncherItem launcherItem2 = new AppItem(app2);
-        LauncherItem launcherItem3 = new AppItem(app3);
-        mApps = new ArrayList<>();
-        mApps.add(app1);
-        mApps.add(app2);
-        mApps.add(app3);
-        mAlphabetizedApps = new ArrayList<>();
-        mAlphabetizedApps.add(launcherItem1);
-        mAlphabetizedApps.add(launcherItem2);
-        mAlphabetizedApps.add(launcherItem3);
-        mCustomizedApps = new ArrayList<>();
-        mCustomizedApps.add(launcherItem2);
-        mCustomizedApps.add(launcherItem3);
-        mCustomizedApps.add(launcherItem1);
-
-        mLauncherAppsInfo = mock(AppLauncherUtils.LauncherAppsInfo.class);
-        when(mLauncherAppsInfo.getLaunchableComponentsList()).thenReturn(mApps);
-    }
-
-    private AppMetaData createTestAppMetaData(String displayName, String componentName) {
-        return new AppMetaData(displayName, new ComponentName(componentName, componentName),
-                mDrawable, true, false, true, mConsumer, mConsumer);
-    }
-
-    @Test
-    @Ignore("b/304484141")
-    public void test_concurrentExecution() throws InterruptedException {
-        ExecutorService pool = Executors.newCachedThreadPool();
-        for (int i = 0; i < 100; i++) {
-            pool.execute(() -> {
-                mLauncherModel.loadAppsOrderFromFile();
-            });
-            pool.execute(() -> {
-                mLauncherModel.processAppsInfoFromPlatform(mLauncherAppsInfo);
-            });
-        }
-        pool.shutdown(); // Disable new tasks from being submitted
-        if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
-            pool.shutdownNow(); // Cancel currently executing tasks
-        }
-        mLauncherModel.getCurrentLauncher().observeForever(new Observer<>() {
-            @Override
-            public void onChanged(List<LauncherItem> launcherItems) {
-                assertEquals(3, launcherItems.size());
-                assertEquals("A", launcherItems.get(0).getPackageName());
-                assertEquals("B", launcherItems.get(1).getPackageName());
-                assertEquals("C", launcherItems.get(2).getPackageName());
-                //remove observer after assertion
-                mLauncherModel.getCurrentLauncher().removeObserver(this);
-            }
-        });
-    }
-
-    @Test
-    public void loadAppsOrderFromFile_first_noOrderFile() throws IOException {
-        mLauncherModel.loadAppsOrderFromFile();
-        mLauncherModel.processAppsInfoFromPlatform(mLauncherAppsInfo);
-        mLauncherModel.getCurrentLauncher().observeForever(launcherItems -> {
-            assertEquals(3, launcherItems.size());
-            assertEquals("A", launcherItems.get(0).getPackageName());
-            assertEquals("B", launcherItems.get(1).getPackageName());
-            assertEquals("C", launcherItems.get(2).getPackageName());
-        });
-    }
-
-    @Test
-    public void loadAppsOrderFromFile_first_existsOrderFile() {
-        mLauncherModel.processAppsInfoFromPlatform(mLauncherAppsInfo);
-        mLauncherModel.loadAppsOrderFromFile();
-
-        mLauncherModel.setAppPosition(0, mApps.get(2));
-        // normally, the observer would make this call
-        mLauncherModel.handleAppListChange();
-
-        mLauncherModel.loadAppsOrderFromFile();
-        mLauncherModel.getCurrentLauncher().observeForever(it -> {
-            assertEquals("C", mApps.get(2).getPackageName());
-            assertEquals(3, it.size());
-            assertEquals("C", it.get(0).getPackageName());
-        });
-    }
-
-    @Test
-    public void processAppsInfoFromPlatform_first_noCustomOrderFile() {
-        mLauncherModel.processAppsInfoFromPlatform(mLauncherAppsInfo);
-        mLauncherModel.loadAppsOrderFromFile();
-        mLauncherModel.getCurrentLauncher().observeForever(it -> {
-            assertEquals(3, it.size());
-            assertEquals("A", it.get(0).getPackageName());
-        });
-    }
-}
diff --git a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/apporder/AppOrderControllerTest.java b/libs/appgrid/lib/tests/src/com/android/car/carlauncher/apporder/AppOrderControllerTest.java
deleted file mode 100644
index da153fd..0000000
--- a/libs/appgrid/lib/tests/src/com/android/car/carlauncher/apporder/AppOrderControllerTest.java
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2023 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.carlauncher.apporder;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.content.ComponentName;
-
-import androidx.lifecycle.MutableLiveData;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.car.carlauncher.AppItem;
-import com.android.car.carlauncher.AppMetaData;
-import com.android.car.carlauncher.LauncherItem;
-import com.android.car.carlauncher.LauncherItemMessageHelper;
-import com.android.car.carlauncher.LauncherItemProto.LauncherItemMessage;
-import com.android.car.carlauncher.datastore.launcheritem.LauncherItemListSource;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@RunWith(AndroidJUnit4.class)
-public class AppOrderControllerTest {
-    private AppOrderController mController;
-    private Map<ComponentName, LauncherItem> mLauncherItemsMap;
-    private List<LauncherItem> mDefaultOrder;
-    private List<LauncherItem> mCustomizedOrder;
-    @Mock
-    private LauncherItemListSource mMockDataSource;
-    private MutableLiveData<List<LauncherItem>> mCurrentAppList;
-
-    @Before
-    public void setUp() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        when(mMockDataSource.exists()).thenReturn(true);
-
-        mLauncherItemsMap = new HashMap<>();
-        mDefaultOrder = spy(new ArrayList<>());
-        mCustomizedOrder = spy(new ArrayList<>());
-        mCurrentAppList = spy(new MutableLiveData<>());
-        mCustomizedOrder.add(null);
-
-        mController = spy(new AppOrderController(mMockDataSource, mCurrentAppList, mDefaultOrder,
-                mCustomizedOrder));
-    }
-
-    @Test
-    public void maybePublishAppList_loadAppListFromPlatform_noPublishing() {
-        // tests that multiple platform connection does not publish app list
-        mController.loadAppListFromPlatform(mLauncherItemsMap, mDefaultOrder);
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-
-        mController.loadAppListFromPlatform(mLauncherItemsMap, mDefaultOrder);
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-
-        verify(mController, times(2)).maybePublishAppList();
-        verify(mCurrentAppList, never()).postValue(any());
-    }
-
-    @Test
-    public void maybePublishAppList_loadAppListFromFile_noPublishing() {
-        // tests that multiple file read does not publish app list
-        mController.loadAppOrderFromFile();
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-
-        mController.loadAppOrderFromFile();
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-
-        verify(mController, times(2)).maybePublishAppList();
-        verify(mCurrentAppList, never()).postValue(any());
-    }
-
-    @Test
-    public void maybePublishAppList_publishing_defaultOrder() {
-        when(mController.checkDataSourceExists()).thenReturn(false);
-
-        mController.loadAppOrderFromFile();
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-        assertThat(mController.shouldUseCustomOrder()).isFalse();
-
-        mController.loadAppListFromPlatform(mLauncherItemsMap, mDefaultOrder);
-        verify(mController, times(2)).maybePublishAppList();
-        verify(mCurrentAppList, times(1)).postValue(any());
-    }
-
-    @Test
-    public void maybePublishAppList_publishing_customOrder() {
-        when(mController.checkDataSourceExists()).thenReturn(true);
-        // if the data source exists and the list is non-empty, we expect to use custom oder
-        List<LauncherItemMessage> nonEmptyMessageList = new ArrayList<>();
-        LauncherItemMessage emptyAppItemMessage =
-                (new AppItem("packageName", "className", "displayName", null))
-                        .convertToMessage(1, 1);
-        nonEmptyMessageList.add(emptyAppItemMessage);
-        LauncherItemMessageHelper helper = new LauncherItemMessageHelper();
-        when(mMockDataSource.readFromFile()).thenReturn(
-                helper.convertToMessage(nonEmptyMessageList));
-
-        mController.loadAppOrderFromFile();
-        assertThat(mController.appsDataLoadingCompleted()).isFalse();
-        assertThat(mController.shouldUseCustomOrder()).isTrue();
-
-        mController.loadAppListFromPlatform(mLauncherItemsMap, mDefaultOrder);
-        verify(mController, times(2)).maybePublishAppList();
-        verify(mCurrentAppList, times(1)).postValue(any());
-    }
-
-    @Test
-    public void setAppPosition_postValue() {
-        // simulate platform app loading
-        LauncherItem testItem1 = new AppItem("packageName1", "className1", "displayName1", null);
-        LauncherItem testItem2 = new AppItem("packageName2", "className2", "displayName2", null);
-        String packageName3 = "packageName3";
-        LauncherItem testItem3 = new AppItem(packageName3, "className3", "displayName3", null);
-
-        mLauncherItemsMap.put(new ComponentName("componentName1", "componentName1"), testItem1);
-        mLauncherItemsMap.put(new ComponentName("componentName2", "componentName2"), testItem2);
-        ComponentName componentName3 = new ComponentName("componentName3", "componentName3");
-        mLauncherItemsMap.put(componentName3, testItem3);
-        List<LauncherItem> newAppList = new ArrayList<>();
-        newAppList.add(testItem1);
-        newAppList.add(testItem2);
-        newAppList.add(testItem3);
-        when(mCurrentAppList.getValue()).thenReturn(newAppList);
-
-        // simulate launcher cold start - no app list from file
-        mController.loadAppOrderFromFile();
-        assertThat(mController.shouldUseCustomOrder()).isFalse();
-        mController.loadAppListFromPlatform(mLauncherItemsMap, newAppList);
-        verify(mCurrentAppList, times(1)).postValue(any());
-
-        AppMetaData mockApp3MetaData = mock(AppMetaData.class);
-        when(mockApp3MetaData.getComponentName()).thenReturn(componentName3);
-
-        // tests that setAppPosition posts update to the user interface
-        mController.setAppPosition(0, mockApp3MetaData);
-        verify(mCurrentAppList, times(2)).postValue(any());
-
-        // tests that the setAppPosition correctly modifies app position
-        assertThat(mCurrentAppList.getValue()).isNotNull();
-        assertThat(mCurrentAppList.getValue().isEmpty()).isFalse();
-        assertThat(mCurrentAppList.getValue().get(0).getPackageName()).isEqualTo(packageName3);
-    }
-}
diff --git a/libs/car-launcher-common/Android.bp b/libs/car-launcher-common/Android.bp
new file mode 100644
index 0000000..ebecfe1
--- /dev/null
+++ b/libs/car-launcher-common/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2024 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_library {
+    name: "CarLauncherCommon",
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+        ":hidden_api_enabled_srcs",
+    ],
+
+    resource_dirs: ["res"],
+
+    libs: ["android.car"],
+
+    static_libs: [
+        "androidx.lifecycle_lifecycle-extensions",
+        "com.google.android.material_material",
+        "car-ui-lib-no-overlayable",
+        "libprotobuf-java-lite",
+    ],
+
+    manifest: "AndroidManifest.xml",
+}
diff --git a/libs/car-launcher-common/AndroidManifest.xml b/libs/car-launcher-common/AndroidManifest.xml
new file mode 100644
index 0000000..8fee1c0
--- /dev/null
+++ b/libs/car-launcher-common/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2024 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.car.carlaunchercommon">
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+</manifest>
diff --git a/libs/car-launcher-common/OWNERS b/libs/car-launcher-common/OWNERS
new file mode 100644
index 0000000..f7ec255
--- /dev/null
+++ b/libs/car-launcher-common/OWNERS
@@ -0,0 +1,8 @@
+# Default code reviewers picked from top 3 or more developers.
+# Please update this list if you find better candidates.
+set noparent
+
[email protected]
[email protected]
[email protected]
[email protected]
diff --git a/libs/car-launcher-common/README b/libs/car-launcher-common/README
new file mode 100644
index 0000000..4dd17d2
--- /dev/null
+++ b/libs/car-launcher-common/README
@@ -0,0 +1,3 @@
+# AAOS Car Launcher Utility Library
+
+This library is to provides helper classes for common launcher components in AAOS.
diff --git a/libs/car-launcher-common/build.gradle b/libs/car-launcher-common/build.gradle
new file mode 100644
index 0000000..f3d1441
--- /dev/null
+++ b/libs/car-launcher-common/build.gradle
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+    id 'com.android.library'
+    id 'kotlin-android'
+    id "com.google.protobuf"
+}
+
+android {
+    namespace 'com.android.car.carlaunchercommon'
+    compileSdk gradle.ext.aaosTargetSDK
+
+    defaultConfig {
+        minSdk gradle.ext.aaosLatestSDK
+        targetSdk gradle.ext.aaosLatestSDK
+        versionCode gradle.ext.getVersionCode()
+        versionName gradle.ext.getVersionName()
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    sourceSets {
+        main {
+            manifest.srcFile 'AndroidManifest.xml'
+            res.srcDirs = ['res']
+            java.srcDirs = ['src']
+        }
+
+        androidTest {
+            java.srcDirs += 'tests/src'
+        }
+    }
+
+    testOptions {
+        unitTests {
+            includeAndroidResources = true
+            returnDefaultValues = true
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+
+    lintOptions {
+        disable 'PrivateResource'
+    }
+}
+
+dependencies {
+    compileOnly files(gradle.ext.lib_car_system_stubs)
+    compileOnly files(gradle.ext.lib_system_stubs)
+    // Verify that the code should fail on runtime with hidden api access
+    compileOnly project(":libs:hidden-apis-compat:hidden-apis-disabled")
+    implementation project(":libs:car-ui-lib")
+    implementation 'androidx.annotation:annotation:1.7.1'
+    implementation('com.google.protobuf:protobuf-javalite:3.25.1')
+
+    //Android Test dependencies
+    androidTestCompileOnly files(gradle.ext.lib_system_stubs)
+    androidTestImplementation files(gradle.ext.lib_car_system_stubs)
+    androidTestImplementation project(":libs:hidden-apis-compat:hidden-apis-disabled")
+    androidTestImplementation 'junit:junit:4.13.2'
+    androidTestImplementation "org.mockito:mockito-android:5.10.0"
+    androidTestImplementation "org.mockito:mockito-core:5.10.0"
+    androidTestImplementation "org.mockito.kotlin:mockito-kotlin:3.2.0"
+    androidTestImplementation "androidx.test:rules:1.5.0"
+    androidTestImplementation 'androidx.test:runner:1.5.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+    androidTestImplementation "com.google.truth:truth:1.3.0"
+}
+
+protobuf {
+    protoc {
+        artifact = "com.google.protobuf:protoc:3.25.1"
+    }
+
+    // Generates the java Protobuf-lite code for the Protobufs in this project. See
+    // https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
+    // for more information.
+    generateProtoTasks {
+        all().each { task ->
+            task.builtins {
+                java {
+                    option 'lite'
+                }
+            }
+        }
+    }
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+}
diff --git a/libs/appgrid/lib/res/drawable/ic_app_info.xml b/libs/car-launcher-common/res/drawable/ic_app_info.xml
similarity index 96%
rename from libs/appgrid/lib/res/drawable/ic_app_info.xml
rename to libs/car-launcher-common/res/drawable/ic_app_info.xml
index 420ba81..038eab8 100644
--- a/libs/appgrid/lib/res/drawable/ic_app_info.xml
+++ b/libs/car-launcher-common/res/drawable/ic_app_info.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
diff --git a/docklib-util/res/drawable/ic_dock_pin.xml b/libs/car-launcher-common/res/drawable/ic_dock_pin.xml
similarity index 95%
rename from docklib-util/res/drawable/ic_dock_pin.xml
rename to libs/car-launcher-common/res/drawable/ic_dock_pin.xml
index d52696e..44f4a83 100644
--- a/docklib-util/res/drawable/ic_dock_pin.xml
+++ b/libs/car-launcher-common/res/drawable/ic_dock_pin.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
diff --git a/docklib-util/res/drawable/ic_dock_unpin.xml b/libs/car-launcher-common/res/drawable/ic_dock_unpin.xml
similarity index 60%
rename from docklib-util/res/drawable/ic_dock_unpin.xml
rename to libs/car-launcher-common/res/drawable/ic_dock_unpin.xml
index ea3a291..469d1b6 100644
--- a/docklib-util/res/drawable/ic_dock_unpin.xml
+++ b/libs/car-launcher-common/res/drawable/ic_dock_unpin.xml
@@ -1,3 +1,19 @@
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
 <vector xmlns:android="http://schemas.android.com/apk/res/android"
     android:width="24dp"
     android:height="24dp"
diff --git a/libs/appgrid/lib/res/drawable/ic_force_stop_caution_icon.xml b/libs/car-launcher-common/res/drawable/ic_force_stop_caution_icon.xml
similarity index 94%
rename from libs/appgrid/lib/res/drawable/ic_force_stop_caution_icon.xml
rename to libs/car-launcher-common/res/drawable/ic_force_stop_caution_icon.xml
index 8adfb22..ea8454e 100644
--- a/libs/appgrid/lib/res/drawable/ic_force_stop_caution_icon.xml
+++ b/libs/car-launcher-common/res/drawable/ic_force_stop_caution_icon.xml
@@ -1,5 +1,5 @@
 <!--
-  ~ Copyright (C) 2022 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
diff --git a/libs/car-launcher-common/res/values-af/strings.xml b/libs/car-launcher-common/res/values-af/strings.xml
new file mode 100644
index 0000000..f1aefae
--- /dev/null
+++ b/libs/car-launcher-common/res/values-af/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Speld app vas"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Ontspeld app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stop app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Appinligting"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Stop app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"As jy ’n app verplig om te stop, kan dit verkeerd optree."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gestop."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan nie gebruik word terwyl jy bestuur nie."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-am/strings.xml b/libs/car-launcher-common/res/values-am/strings.xml
new file mode 100644
index 0000000..adb8b95
--- /dev/null
+++ b/libs/car-launcher-common/res/values-am/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"መተግበሪያን ፒን ያድርጉ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"መተግበሪያ ይንቀሉ"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"መተግበሪያን አቁም"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"የመተግበሪያ መረጃ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"መተግበሪያ ይቁም?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"አንድ መተግበሪያን በኃይል እንዲቆም ካደረጉት በትክክል ላይሠራ ይችላል።"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> እንዲቆም ተደርጓል።"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> በመንዳት ላይ ሳለ ጥቅም ላይ መዋል አይችልም።"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ar/strings.xml b/libs/car-launcher-common/res/values-ar/strings.xml
new file mode 100644
index 0000000..ece78ea
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ar/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"تثبيت التطبيق"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"إزالة تثبيت التطبيق"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"إيقاف التطبيق"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"معلومات التطبيقات"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"هل تريد إيقاف التطبيق؟"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"في حال فرض إيقاف أحد التطبيقات، قد لا يعمل بشكل صحيح."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"تم إيقاف \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"لا يمكن استخدام \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" أثناء القيادة."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-as/strings.xml b/libs/car-launcher-common/res/values-as/strings.xml
new file mode 100644
index 0000000..f068283
--- /dev/null
+++ b/libs/car-launcher-common/res/values-as/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"এপ্‌ পিন কৰক"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"এপ্‌ আনপিন কৰক"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"এপ্‌ বন্ধ কৰক"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"এপৰ তথ্য"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"এপ্‌ বন্ধ কৰিবনে?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"আপুনি কোনো এপ্‌ বলেৰে বন্ধ কৰিলে, ই অস্বাভাৱিক আচৰণ কৰিব পাৰে।"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ কৰা হৈছে।"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"গাড়ী চলাই থকাৰ সময়ত <xliff:g id="APP_NAME">%1$s</xliff:g> ব্যৱহাৰ কৰিব নোৱাৰি।"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-az/strings.xml b/libs/car-launcher-common/res/values-az/strings.xml
new file mode 100644
index 0000000..15b9323
--- /dev/null
+++ b/libs/car-launcher-common/res/values-az/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Tətbiqi bərkidin"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Tətbiqi bərkitməyin"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Tətbiqi dayandırın"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Tətbiq haqqında"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Tətbiq dayandırılsın?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Tətbiqi məcburi dayandırsanız, səhv işləyə bilər."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> dayandırılıb."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Avtomobil sürərkən <xliff:g id="APP_NAME">%1$s</xliff:g> istifadə edilə bilməz."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-b+sr+Latn/strings.xml b/libs/car-launcher-common/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000..27efc08
--- /dev/null
+++ b/libs/car-launcher-common/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Zakači aplikaciju"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Otkači aplikaciju"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zaustavi aplikaciju"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informacije o aplikaciji"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Želite da zaustavite aplikaciju?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ako prinudno zaustavite aplikaciju, možda će se ponašati neočekivano."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Ne možete da koristite aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> tokom vožnje."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-be/strings.xml b/libs/car-launcher-common/res/values-be/strings.xml
new file mode 100644
index 0000000..d693371
--- /dev/null
+++ b/libs/car-launcher-common/res/values-be/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Замацаваць праграму"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Адмацаваць праграму"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Спыніць праграму"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Звесткі пра праграму"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Спыніць праграму?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Прымусовае спыненне праграмы можа прывесці да збою."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" спынена."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Праграмай \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" нельга карыстацца, калі вы за рулём."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-bg/strings.xml b/libs/car-launcher-common/res/values-bg/strings.xml
new file mode 100644
index 0000000..2899582
--- /dev/null
+++ b/libs/car-launcher-common/res/values-bg/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Фиксиране на приложението"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Освобождаване на приложението"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Спиране на приложението"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Информация от приложенията"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Да се спре ли приложението?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ако принудително спрете приложение, то може да не функционира правилно."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Приложението <xliff:g id="APP_NAME">%1$s</xliff:g> е спряно."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се използва при шофиране."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-bn/strings.xml b/libs/car-launcher-common/res/values-bn/strings.xml
new file mode 100644
index 0000000..ca41a7d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-bn/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"অ্যাপ পিন করুন"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"অ্যাপ আনপিন করুন"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"অ্যাপ বন্ধ করুন"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"অ্যাপের তথ্য"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"অ্যাপ বন্ধ করবেন?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"আপনি কোনও অ্যাপকে জোর করে বন্ধ করলে, তা সঠিকভাবে কাজ নাও করতে পারে।"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ড্রাইভ করার সময় <xliff:g id="APP_NAME">%1$s</xliff:g> ব্যবহার করা যাবে না।"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-bs/strings.xml b/libs/car-launcher-common/res/values-bs/strings.xml
new file mode 100644
index 0000000..f08bcfc
--- /dev/null
+++ b/libs/car-launcher-common/res/values-bs/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Kačenje aplikacije"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Otkačivanje aplikacije"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zaustavljanje aplikacije"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informacije o aplikaciji"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Zaustaviti aplikaciju?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ako prisilno zaustavite aplikaciju, moguće je da će se ponašati nepredviđeno."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Nije moguće koristiti aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> tokom vožnje."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ca/strings.xml b/libs/car-launcher-common/res/values-ca/strings.xml
new file mode 100644
index 0000000..9d6ab6a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ca/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fixa l\'aplicació"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Deixa de fixar l\'aplicació"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Atura l\'aplicació"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informació de l\'aplicació"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Vols aturar l\'aplicació?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Si forces l\'aturada d\'una aplicació, és possible que no funcioni correctament."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'ha aturat."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"No es pot utilitzar <xliff:g id="APP_NAME">%1$s</xliff:g> mentre es condueix."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-cs/strings.xml b/libs/car-launcher-common/res/values-cs/strings.xml
new file mode 100644
index 0000000..a9fac2a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-cs/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Připnout aplikaci"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Odepnout aplikaci"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zastavit aplikaci"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informace o aplikaci"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Zastavit aplikaci?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Vynucené zastavení může způsobit nepředvídatelné chování aplikace."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> byla zastavena."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g> nelze používat při řízení."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-da/strings.xml b/libs/car-launcher-common/res/values-da/strings.xml
new file mode 100644
index 0000000..cb3e2db
--- /dev/null
+++ b/libs/car-launcher-common/res/values-da/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fastgør app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Frigør app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stands appen"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Appoplysninger"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Vil du standse appen?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Hvis du tvinger en app til at standse, kan det medføre, at den ikke fungerer korrekt."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> er blevet standset."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> kan ikke bruges under kørsel."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-de/strings.xml b/libs/car-launcher-common/res/values-de/strings.xml
new file mode 100644
index 0000000..0ab11e0
--- /dev/null
+++ b/libs/car-launcher-common/res/values-de/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"App anpinnen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"App loslösen"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"App beenden"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App-Info"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"App beenden?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Das Beenden der App zu erzwingen kann zu unerwünschtem Verhalten führen."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> wurde beendet."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> kann während der Fahrt nicht genutzt werden."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-el/strings.xml b/libs/car-launcher-common/res/values-el/strings.xml
new file mode 100644
index 0000000..b389471
--- /dev/null
+++ b/libs/car-launcher-common/res/values-el/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Καρφίτσωμα εφαρμογής"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Ξεκαρφίτσωμα εφαρμογής"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Διακοπή εφαρμογής"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Πληροφορίες εφαρμογής"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Διακοπή εφαρμογής;"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Αν κάνετε αναγκαστική διακοπή μιας εφαρμογής, ενδέχεται να μην λειτουργεί σωστά."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> διακόπηκε."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Η χρήση της εφαρμογής <xliff:g id="APP_NAME">%1$s</xliff:g> είναι αδύνατη κατά την οδήγηση."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-en-rAU/strings.xml b/libs/car-launcher-common/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000..eb650c6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-en-rAU/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Unpin app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stop app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App info"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Stop app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"If you force stop an app, it may misbehave."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-en-rCA/strings.xml b/libs/car-launcher-common/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000..eb650c6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-en-rCA/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Unpin app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stop app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App info"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Stop app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"If you force stop an app, it may misbehave."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-en-rGB/strings.xml b/libs/car-launcher-common/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000..eb650c6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-en-rGB/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Unpin app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stop app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App info"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Stop app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"If you force stop an app, it may misbehave."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-en-rIN/strings.xml b/libs/car-launcher-common/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000..eb650c6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-en-rIN/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pin app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Unpin app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Stop app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App info"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Stop app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"If you force stop an app, it may misbehave."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> has been stopped."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> can\'t be used while driving."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-en-rXC/strings.xml b/libs/car-launcher-common/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000..d3de402
--- /dev/null
+++ b/libs/car-launcher-common/res/values-en-rXC/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‏‎‏‎‏‏‎‎‏‎‎‎‏‎‎‎‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‏‏‎‏‏‎‏‎‏‎‏‎‎‏‎‎Pin app‎‏‎‎‏‎"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‎‎‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‏‏‏‎‏‎Unpin app‎‏‎‎‏‎"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‏‏‎‏‏‏‎‏‎‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎Stop app‎‏‎‎‏‎"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‎‎‎‏‏‏‏‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‎‎‏‎App info‎‏‎‎‏‎"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‎‎‏‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‏‏‎‎‎‎‎‏‎‏‎‏‎‏‏‎‏‎‏‎‎‎‏‎‎‏‎Stop app?‎‏‎‎‏‎"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‎‎‏‏‎‎‏‏‏‎‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‏‏‎‎‏‏‏‏‎‎If you force stop an app, it may misbehave.‎‏‎‎‏‎"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‎‏‏‏‎‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ has been stopped.‎‏‎‎‏‎"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‏‎‎‎‎‎‎‎‎‎‏‏‎‎‏‏‏‎‎‎‎‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎ can\'t be used while driving.‎‏‎‎‏‎"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-es-rUS/strings.xml b/libs/car-launcher-common/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..0437b20
--- /dev/null
+++ b/libs/car-launcher-common/res/values-es-rUS/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fijar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Dejar de fijar app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Detener app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Información de la app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"¿Quieres detener la app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Si fuerzas la detención de una app, es posible que funcione incorrectamente."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Se detuvo <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> no puede usarse mientras conduces."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-es/strings.xml b/libs/car-launcher-common/res/values-es/strings.xml
new file mode 100644
index 0000000..cf71ff8
--- /dev/null
+++ b/libs/car-launcher-common/res/values-es/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fijar aplicación"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Desfijar aplicación"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Detener aplicación"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Información de la aplicación"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"¿Detener aplicación?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Si fuerzas la detención de una aplicación, puede que no funcione correctamente."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> se ha detenido."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> no se puede usar mientras se conduce."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-et/strings.xml b/libs/car-launcher-common/res/values-et/strings.xml
new file mode 100644
index 0000000..a225561
--- /dev/null
+++ b/libs/car-launcher-common/res/values-et/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Rakenduse kinnitamine"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Rakenduse vabastamine"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Peata rakendus"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Rakenduse teave"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Kas peatada rakendus?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Kui sundpeatate rakenduse, võib see valesti toimida."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> peatati."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Rakendust <xliff:g id="APP_NAME">%1$s</xliff:g> ei saa sõidu ajal kasutada."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-eu/strings.xml b/libs/car-launcher-common/res/values-eu/strings.xml
new file mode 100644
index 0000000..9b35f7e
--- /dev/null
+++ b/libs/car-launcher-common/res/values-eu/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Ainguratu aplikazioa"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Kendu aplikazioaren aingura"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Gelditu aplikazioa"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Aplikazioari buruzko informazioa"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Aplikazioa gelditu nahi duzu?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Aplikazioak gelditzera behartzen badituzu, baliteke behar bezala ez funtzionatzea."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Gelditu da <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> ezin da erabili gidatu bitartean."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-fa/strings.xml b/libs/car-launcher-common/res/values-fa/strings.xml
new file mode 100644
index 0000000..774908d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-fa/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"سنجاق کردن برنامه"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"برداشتن سنجاق برنامه"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"متوقف کردن برنامه"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"اطلاعات برنامه"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"برنامه متوقف شود؟"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"توقف اجباری برنامه ممکن است باعث عملکرد نادرست آن شود."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> متوقف شده است."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"هنگام رانندگی نمی‌توان از <xliff:g id="APP_NAME">%1$s</xliff:g> استفاده کرد."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-fi/strings.xml b/libs/car-launcher-common/res/values-fi/strings.xml
new file mode 100644
index 0000000..a0d147c
--- /dev/null
+++ b/libs/car-launcher-common/res/values-fi/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Kiinnitä sovellus"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Irrota sovellus"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Sulje sovellus"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Sovelluksen tiedot"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Suljetaanko sovellus?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Jos pakotat sovelluksen sulkeutumaan, se ei välttämättä toimi oikein."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> on suljettu."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei voi olla käytössä ajon aikana."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-fr-rCA/strings.xml b/libs/car-launcher-common/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..6ef5f6a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-fr-rCA/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Épingler l\'application"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Annuler l\'épinglage de l\'application"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Arrêter l\'application"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Détails de l\'appli"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Arrêter l\'application?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Si vous forcez l\'arrêt d\'une application, son fonctionnement peut en être affecté."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> a été arrêtée."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> ne peut pas être utilisée en conduisant."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-fr/strings.xml b/libs/car-launcher-common/res/values-fr/strings.xml
new file mode 100644
index 0000000..5777b56
--- /dev/null
+++ b/libs/car-launcher-common/res/values-fr/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Épingler l\'appli"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Retirer l\'appli"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Arrêter l\'appli"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informations sur l\'appli"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Arrêter l\'appli ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"L\'arrêt forcé d\'une appli peut provoquer un fonctionnement instable."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"L\'appli <xliff:g id="APP_NAME">%1$s</xliff:g> a été arrêtée."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Impossible d\'utiliser <xliff:g id="APP_NAME">%1$s</xliff:g> en conduisant."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-gl/strings.xml b/libs/car-launcher-common/res/values-gl/strings.xml
new file mode 100644
index 0000000..fe2d18f
--- /dev/null
+++ b/libs/car-launcher-common/res/values-gl/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fixar aplicación"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Deixar de fixar aplicación"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Deter aplicación"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Información da aplicación"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Queres deter a aplicación?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Se forzas a parada dunha aplicación, é posible que non funcione correctamente."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Detívose a aplicación <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Non se pode utilizar <xliff:g id="APP_NAME">%1$s</xliff:g> mentres se conduce."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-gu/strings.xml b/libs/car-launcher-common/res/values-gu/strings.xml
new file mode 100644
index 0000000..5155eed
--- /dev/null
+++ b/libs/car-launcher-common/res/values-gu/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ઍપ પિન કરો"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ઍપ અનપિન કરો"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ઍપ બંધ કરો"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ઍપની માહિતી"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ઍપ બંધ કરીએ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"જો તમે કોઈ ઍપને ફરજિયાત બંધ કરો, તો તે અયોગ્ય વર્તન કરી શકે છે."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> બંધ કરવામાં આવી છે."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ડ્રાઇવ કરતી વખતે <xliff:g id="APP_NAME">%1$s</xliff:g>નો ઉપયોગ કરી શકાતો નથી."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-hi/strings.xml b/libs/car-launcher-common/res/values-hi/strings.xml
new file mode 100644
index 0000000..a4fe146
--- /dev/null
+++ b/libs/car-launcher-common/res/values-hi/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ऐप्लिकेशन को पिन करें"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ऐप्लिकेशन को अनपिन करें"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ऐप्लिकेशन को बंद करें"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ऐप्लिकेशन की जानकारी"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"क्या आपको ऐप्लिकेशन रोकना है?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"अगर किसी ऐप्लिकेशन को ज़बरदस्ती रोका जाता है, तो हो सकता है कि वह ठीक तरह से काम न करें."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> को रोक दिया गया है."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"गाड़ी चलाते समय <xliff:g id="APP_NAME">%1$s</xliff:g> ऐप्लिकेशन इस्तेमाल नहीं किया जा सकता."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-hr/strings.xml b/libs/car-launcher-common/res/values-hr/strings.xml
new file mode 100644
index 0000000..6ec46fd
--- /dev/null
+++ b/libs/car-launcher-common/res/values-hr/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Prikvačite aplikaciju"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Otkvačite aplikaciju"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zaustavi aplikaciju"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informacije o aplikaciji"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Želite li zaustaviti aplikaciju?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ako prisilno zaustavite aplikaciju, možda će se ponašati nepredviđeno."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je zaustavljena."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> ne može se koristiti tijekom vožnje."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-hu/strings.xml b/libs/car-launcher-common/res/values-hu/strings.xml
new file mode 100644
index 0000000..c8c269e
--- /dev/null
+++ b/libs/car-launcher-common/res/values-hu/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Alkalmazás kitűzése"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Alkalmazás kitűzésének megszüntetése"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Alkalmazás leállítása"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Alkalmazásadatok"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Leállítja az alkalmazást?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ha egy alkalmazást leállásra kényszerít, lehetséges, hogy hibásan fog működni."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> leállítva."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> nem használható vezetés közben."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-hy/strings.xml b/libs/car-launcher-common/res/values-hy/strings.xml
new file mode 100644
index 0000000..5d118e2
--- /dev/null
+++ b/libs/car-launcher-common/res/values-hy/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Ամրացնել հավելվածը"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Ապամրացնել հավելվածը"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Կանգնեցնել հավելվածը"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Հավելվածի մասին"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Կանգնեցնե՞լ հավելվածը"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Հավելվածի ստիպողական կանգնեցումը կարող է ազդել դրա աշխատանքի վրա։"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը կանգնեցվեց։"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածը հնարավոր չէ օգտագործել վարելու ժամանակ։"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-in/strings.xml b/libs/car-launcher-common/res/values-in/strings.xml
new file mode 100644
index 0000000..82e5e2e
--- /dev/null
+++ b/libs/car-launcher-common/res/values-in/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Sematkan aplikasi"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Lepaskan aplikasi"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Hentikan aplikasi"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Info aplikasi"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Hentikan aplikasi?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Jika aplikasi dihentikan paksa, fungsinya mungkin akan terganggu."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah dihentikan."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak dapat digunakan saat mengemudi."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-is/strings.xml b/libs/car-launcher-common/res/values-is/strings.xml
new file mode 100644
index 0000000..9969554
--- /dev/null
+++ b/libs/car-launcher-common/res/values-is/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Festa forrit"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Losa forrit"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Loka forriti"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Upplýsingar um forrit"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Loka forriti?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ef þú þvingar lokun forrits gæti það látið illa."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> var lokað."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Ekki er hægt að nota <xliff:g id="APP_NAME">%1$s</xliff:g> við akstur."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-it/strings.xml b/libs/car-launcher-common/res/values-it/strings.xml
new file mode 100644
index 0000000..d8588f8
--- /dev/null
+++ b/libs/car-launcher-common/res/values-it/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fissa app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Sblocca app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Interrompi l\'app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informazioni app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Interrompere l\'app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Se forzi l\'interruzione di un\'app, questa potrebbe funzionare in modo anomalo."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> è stata interrotta."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Non è possibile usare l\'app <xliff:g id="APP_NAME">%1$s</xliff:g> durante la guida."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-iw/strings.xml b/libs/car-launcher-common/res/values-iw/strings.xml
new file mode 100644
index 0000000..3459473
--- /dev/null
+++ b/libs/car-launcher-common/res/values-iw/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"הצמדת האפליקציה"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ביטול הצמדת האפליקציה"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"עצירת האפליקציה"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"פרטי האפליקציה"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"לעצור את האפליקציה?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"אם סוגרים אפליקציה באופן ידני, יכול להיות שהיא לא תפעל כמו שצריך."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"עצרת את האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"לא ניתן להשתמש באפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> במהלך הנהיגה."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ja/strings.xml b/libs/car-launcher-common/res/values-ja/strings.xml
new file mode 100644
index 0000000..d93e110
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ja/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"アプリを固定"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"アプリの固定を解除"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"アプリを停止"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"アプリ情報"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"アプリを停止しますか?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"アプリを強制停止すると、アプリが正常に機能しないことがあります。"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> を停止しました。"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"運転中は<xliff:g id="APP_NAME">%1$s</xliff:g>を使用できません。"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ka/strings.xml b/libs/car-launcher-common/res/values-ka/strings.xml
new file mode 100644
index 0000000..78988f1
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ka/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"აპის ჩამაგრება"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"აპის ჩამაგრების მოხსნა"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"აპის შეჩერება"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"აპის ინფორმაცია"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"გსურთ აპის შეჩერება?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"თუ აპის მუშაობას ძალით შეაჩერებთ, მან შესაძლოა გაუმართავად განაგრძოს მუშაობა."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> შეჩერებულია."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"მანქანის მართვისას ვერ გამოიყენებთ <xliff:g id="APP_NAME">%1$s</xliff:g> აპს."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-kk/strings.xml b/libs/car-launcher-common/res/values-kk/strings.xml
new file mode 100644
index 0000000..7fb4673
--- /dev/null
+++ b/libs/car-launcher-common/res/values-kk/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Қолданбаны бекіту"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Қолданбаны босату"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Қолданба жұмысын тоқтату"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Қолданба туралы ақпарат"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Қолданба жұмысы тоқтатылсын ба?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Қолданбаны қолмен тоқтату оның жұмысына кері әсерін тигізуі мүмкін."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> жұмысы тоқтатылды."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Көлік жүргізу кезінде <xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасын пайдалануға болмайды."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-km/strings.xml b/libs/car-launcher-common/res/values-km/strings.xml
new file mode 100644
index 0000000..418ca83
--- /dev/null
+++ b/libs/car-launcher-common/res/values-km/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ខ្ទាស់កម្មវិធី"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ដកខ្ទាស់កម្មវិធី"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"បញ្ឈប់កម្មវិធី"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ព័ត៌មានកម្មវិធី"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"បញ្ឈប់កម្មវិធីឬ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ប្រសិនបើអ្នក​បង្ខំឱ្យបញ្ឈប់​កម្មវិធី​ កម្មវិធីអាចនឹង​ដំណើរការ​មិនត្រឹមត្រូវ។"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ត្រូវបានបញ្ឈប់។"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"មិនអាចប្រើ​ <xliff:g id="APP_NAME">%1$s</xliff:g> នៅពេលបើកបរបានទេ។"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-kn/strings.xml b/libs/car-launcher-common/res/values-kn/strings.xml
new file mode 100644
index 0000000..c0c43ae
--- /dev/null
+++ b/libs/car-launcher-common/res/values-kn/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ಆ್ಯಪ್ ಪಿನ್ ಮಾಡಿ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ಆ್ಯಪ್ ಅನ್‌ಪಿನ್ ಮಾಡಿ"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ಆ್ಯಪ್ ಅನ್ನು ನಿಲ್ಲಿಸಿ"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ಆ್ಯಪ್ ಮಾಹಿತಿ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ಆ್ಯಪ್ ಅನ್ನು ನಿಲ್ಲಿಸಬೇಕೆ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಬಲವಂತವಾಗಿ ನಿಲ್ಲಿಸಿದರೆ, ಅದು ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರಬಹುದು."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಅನ್ನು ನಿಲ್ಲಿಸಲಾಗಿದೆ."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ಡ್ರೈವ್ ಮಾಡುವಾಗ <xliff:g id="APP_NAME">%1$s</xliff:g> ಬಳಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ko/strings.xml b/libs/car-launcher-common/res/values-ko/strings.xml
new file mode 100644
index 0000000..16ec54d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ko/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"앱 고정"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"앱 고정 해제"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"앱 종료"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"앱 정보"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"앱을 종료하시겠습니까?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"강제로 앱을 종료하면 예기치 않은 오류가 발생할 수 있습니다."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> 앱이 종료되었습니다."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"운전 중에는 <xliff:g id="APP_NAME">%1$s</xliff:g> 앱을 사용할 수 없습니다."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ky/strings.xml b/libs/car-launcher-common/res/values-ky/strings.xml
new file mode 100644
index 0000000..3de6f0b
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ky/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Колдонмону кадап коюу"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Колдонмону бошотуу"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Колдонмону токтотуу"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Колдонмо тууралуу"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Колдонмону токтотосузбу?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Колдонмону мажбурлап токтотсоңуз, ал туура эмес иштеп калышы мүмкүн."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> токтотулду."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Айдап баратканда <xliff:g id="APP_NAME">%1$s</xliff:g> колдонулбайт."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-lo/strings.xml b/libs/car-launcher-common/res/values-lo/strings.xml
new file mode 100644
index 0000000..53e137f
--- /dev/null
+++ b/libs/car-launcher-common/res/values-lo/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ປັກໝຸດແອັບ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ຖອດປັກໝຸດແອັບ"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ຢຸດແອັບ"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ຂໍ້ມູນແອັບ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ຢຸດແອັບໄວ້ບໍ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ຫາກທ່ານບັງຄັບປິດແອັບໃດໜຶ່ງ, ມັນອາດເຮັດວຽກຜິດປົກກະຕິໄດ້."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"ຢຸດ <xliff:g id="APP_NAME">%1$s</xliff:g> ແລ້ວ."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ບໍ່ສາມາດໃຊ້ <xliff:g id="APP_NAME">%1$s</xliff:g> ໃນຂະນະທີ່ຂັບລົດໄດ້."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-lt/strings.xml b/libs/car-launcher-common/res/values-lt/strings.xml
new file mode 100644
index 0000000..4db7df6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-lt/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Prisegti programą"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Atsegti programą"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Sustabdyti programą"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Programos informacija"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Sustabdyti programą?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Jei priverstinai sustabdysite programą, ji gali tinkamai neveikti."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Programa „<xliff:g id="APP_NAME">%1$s</xliff:g>“ sustabdyta."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Vairuojant negalima naudoti „<xliff:g id="APP_NAME">%1$s</xliff:g>“."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-lv/strings.xml b/libs/car-launcher-common/res/values-lv/strings.xml
new file mode 100644
index 0000000..84541fc
--- /dev/null
+++ b/libs/car-launcher-common/res/values-lv/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Piespraust lietotni"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Atspraust lietotni"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Apturēt lietotnes darbību"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informācija par lietotni"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Vai apturēt lietotnes darbību?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Piespiedu kārtā apturot lietotnes darbību, var rasties šīs lietotnes darbības traucējumi."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> darbība ir apturēta."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Lietotni <xliff:g id="APP_NAME">%1$s</xliff:g> nevar izmantot braukšanas laikā."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-mk/strings.xml b/libs/car-launcher-common/res/values-mk/strings.xml
new file mode 100644
index 0000000..cec5e63
--- /dev/null
+++ b/libs/car-launcher-common/res/values-mk/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Закачување на апликацијата"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Откачување на апликацијата"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Исклучи ја апликацијата"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Информации за апликацијата"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Да се исклучи апликацијата?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ако исклучите апликација присилно, таа може да не се однесува правилно."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> е исклучена."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> не може да се користи при возење."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ml/strings.xml b/libs/car-launcher-common/res/values-ml/strings.xml
new file mode 100644
index 0000000..0c207ff
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ml/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ആപ്പ് പിൻ ചെയ്യുക"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ആപ്പ് അൺപിൻ ചെയ്യുക"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ആപ്പിന്റെ പ്രവർത്തനം നിർത്തുക"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ആപ്പ് വിവരങ്ങൾ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ആപ്പിന്റെ പ്രവർത്തനം നിർത്തണോ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ഒരു ആപ്പിന്റെ പ്രവർത്തനം നിർബന്ധിതമായി നിർത്തിയാൽ, അത് ശരിയായി പ്രവർത്തിക്കാനിടയില്ല."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിന്റെ പ്രവർത്തനം നിർത്തി."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ഡ്രൈവിംഗിനിടെ <xliff:g id="APP_NAME">%1$s</xliff:g> ഉപയോഗിക്കാനാകില്ല."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-mn/strings.xml b/libs/car-launcher-common/res/values-mn/strings.xml
new file mode 100644
index 0000000..4c310de
--- /dev/null
+++ b/libs/car-launcher-common/res/values-mn/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Аппыг бэхлэх"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Аппыг бэхэлснийг болиулах"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Аппыг зогсоох"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Аппын мэдээлэл"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Аппыг зогсоох уу?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Хэрэв та аппыг хүчээр зогсоовол энэ нь буруу ажиллаж магадгүй."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g>-г зогсоосон."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Жолоо барьж байх үед <xliff:g id="APP_NAME">%1$s</xliff:g>-г ашиглах боломжгүй."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-mr/strings.xml b/libs/car-launcher-common/res/values-mr/strings.xml
new file mode 100644
index 0000000..0230643
--- /dev/null
+++ b/libs/car-launcher-common/res/values-mr/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"अ‍ॅप पिन करा"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"अ‍ॅप अनपिन करा"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"अ‍ॅप थांबवा"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ॲप माहिती"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"अ‍ॅप थांबवायचे आहे का?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"तुम्ही अ‍ॅप सक्तीने थांबवल्यास, ते अयोग्य वर्तन करू शकते."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> थांबवण्यात आले आहे."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ड्राइव्ह करताना <xliff:g id="APP_NAME">%1$s</xliff:g> वापरता येऊ शकत नाही."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ms/strings.xml b/libs/car-launcher-common/res/values-ms/strings.xml
new file mode 100644
index 0000000..7f095f8
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ms/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Semat apl"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Nyahsemat apl"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Hentikan apl"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Maklumat apl"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Hentikan apl?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Jika anda menghentikan apl secara paksa, fungsi apl mungkin terganggu."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah dihentikan."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak boleh digunakan semasa memandu."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-my/strings.xml b/libs/car-launcher-common/res/values-my/strings.xml
new file mode 100644
index 0000000..cad5ccb
--- /dev/null
+++ b/libs/car-launcher-common/res/values-my/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"အက်ပ်ကို ပင်ထိုးရန်"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"အက်ပ်ကို ပင်ဖြုတ်ရန်"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"အက်ပ် ရပ်ရန်"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"အက်ပ်အချက်အလက်များ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"အက်ပ်ကို ရပ်မလား။"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"အက်ပ်ကို မဖြစ်မနေ ရပ်ခိုင်းလျှင် အမှားဖြစ်နိုင်သည်။"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ကို ရပ်လိုက်ပါပြီ။"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ကားမောင်းနေစဉ် <xliff:g id="APP_NAME">%1$s</xliff:g> ကို သုံး၍မရပါ။"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-nb/strings.xml b/libs/car-launcher-common/res/values-nb/strings.xml
new file mode 100644
index 0000000..e6074ff
--- /dev/null
+++ b/libs/car-launcher-common/res/values-nb/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fest appen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Løsne appen"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Avslutt appen"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Appinformasjon"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Vil du avslutte appen?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Hvis du tvinger en app til å avslutte, kan det oppstå problemer."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> er avsluttet."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Du kan ikke bruke <xliff:g id="APP_NAME">%1$s</xliff:g> mens du kjører."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ne/strings.xml b/libs/car-launcher-common/res/values-ne/strings.xml
new file mode 100644
index 0000000..15fc8b6
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ne/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"एप पिन गर्नुहोस्"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"एप अनपिन गर्नुहोस्"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"एप बन्द गर्नुहोस्"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"एपसम्बन्धी जानकारी"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"एप बन्द गर्ने हो?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"तपाईंले कुनै एप जबरजस्ती रोक्नुभयो भने त्यसले सही तरिकाले काम नगर्न सक्छ।"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> बन्द गरिएको छ।"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"गाडी चलाइरहेका बेला <xliff:g id="APP_NAME">%1$s</xliff:g> प्रयोग गर्न मिल्दैन।"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-nl/strings.xml b/libs/car-launcher-common/res/values-nl/strings.xml
new file mode 100644
index 0000000..caf836d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-nl/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"App vastzetten"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"App losmaken"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"App stoppen"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"App-informatie"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"App stoppen?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Als je een app gedwongen stopt, kan deze onverwacht gedrag vertonen."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> is gestopt."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Je kunt <xliff:g id="APP_NAME">%1$s</xliff:g> niet gebruiken tijdens het rijden."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-or/strings.xml b/libs/car-launcher-common/res/values-or/strings.xml
new file mode 100644
index 0000000..127b09a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-or/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ଆପ ପିନ କରନ୍ତୁ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ଆପ ଅନପିନ କରନ୍ତୁ"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ଆପକୁ ବନ୍ଦ କରନ୍ତୁ"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ଆପ୍ ସୂଚନା"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ଆପକୁ ବନ୍ଦ କରିବେ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ଯଦି ଆପଣ ଏକ ଆପକୁ ବାଧ୍ୟତାର ସହ ବନ୍ଦ କରନ୍ତି, ତେବେ ଏହା ଠିକ୍ ଭାବେ କାମ ନକରିପାରେ।"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g>କୁ ବନ୍ଦ କରାଯାଇଛି।"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ଡ୍ରାଇଭ୍ କରିବା ସମୟରେ <xliff:g id="APP_NAME">%1$s</xliff:g> ବ୍ୟବହାର କରାଯାଇପାରିବ ନାହିଁ।"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-pa/strings.xml b/libs/car-launcher-common/res/values-pa/strings.xml
new file mode 100644
index 0000000..394620d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-pa/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ਐਪ ਨੂੰ ਪਿੰਨ ਕਰੋ"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ਐਪ ਨੂੰ ਅਣਪਿੰਨ ਕਰੋ"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ਐਪ ਬੰਦ ਕਰੋ"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ਐਪ ਜਾਣਕਾਰੀ"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ਕੀ ਐਪ ਬੰਦ ਕਰਨੀ ਹੈ?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ਜੇ ਤੁਸੀਂ ਕਿਸੇ ਐਪ ਨੂੰ ਜ਼ਬਰਦਸਤੀ ਬੰਦ ਕਰਦੇ ਹੋ, ਤਾਂ ਹੋ ਸਕਦਾ ਹੈ ਇਹ ਠੀਕ ਤਰ੍ਹਾਂ ਕੰਮ ਨਾ ਕਰੇ।"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਬੰਦ ਕਰ ਦਿੱਤੀ ਗਈ ਹੈ।"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ਗੱਡੀ ਚਲਾਉਣ ਵੇਲੇ <xliff:g id="APP_NAME">%1$s</xliff:g> ਨੂੰ ਵਰਤਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ।"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-pl/strings.xml b/libs/car-launcher-common/res/values-pl/strings.xml
new file mode 100644
index 0000000..3decf2a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-pl/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Przypnij aplikację"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Odepnij aplikację"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zatrzymaj aplikację"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informacje o aplikacji"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Zatrzymać aplikację?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Jeśli wymusisz zatrzymanie aplikacji, może ona zadziałać nieprawidłowo."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> została zatrzymana."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Podczas jazdy nie można korzystać z aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-pt-rPT/strings.xml b/libs/car-launcher-common/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..48ce4f8
--- /dev/null
+++ b/libs/car-launcher-common/res/values-pt-rPT/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Afixar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Desafixar app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Parar app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informações da app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Parar a app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Se forçar a paragem de uma app, esta pode apresentar um comportamento anormal."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> foi parada."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Não é possível usar a app <xliff:g id="APP_NAME">%1$s</xliff:g> durante a condução."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-pt/strings.xml b/libs/car-launcher-common/res/values-pt/strings.xml
new file mode 100644
index 0000000..6648f9a
--- /dev/null
+++ b/libs/car-launcher-common/res/values-pt/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fixar app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Liberar app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Parar o app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informações do app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Parar o app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Se você forçar o fechamento de um app, ele pode apresentar mau funcionamento."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> foi parado."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> não pode ser usado ao dirigir."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ro/strings.xml b/libs/car-launcher-common/res/values-ro/strings.xml
new file mode 100644
index 0000000..3ab7385
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ro/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fixează aplicația"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Anulează fixarea aplicației"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Oprește aplicația"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informații despre aplicații"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Oprești aplicația?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Dacă oprești forțat o aplicație, aceasta se poate comporta necorespunzător."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost oprită."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Nu poți folosi <xliff:g id="APP_NAME">%1$s</xliff:g> în timp ce conduci."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ru/strings.xml b/libs/car-launcher-common/res/values-ru/strings.xml
new file mode 100644
index 0000000..47e4924
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ru/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Закрепить приложение"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Открепить приложение"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Остановить"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Сведения о приложении"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Остановить приложение?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Если принудительно остановить приложение, оно может работать некорректно."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" остановлено."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" нельзя использовать за рулем."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-si/strings.xml b/libs/car-launcher-common/res/values-si/strings.xml
new file mode 100644
index 0000000..a017561
--- /dev/null
+++ b/libs/car-launcher-common/res/values-si/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"යෙදුම අමුණන්න"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"යෙදුම නොඅමුණන්න"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"යෙදුම නවත්වන්න"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"යෙදුම් තතු"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"යෙදුම නවත්වන්න ද?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ඔබ යෙදුමක් බලෙන් නැවත වුවහොත්, එය වැරදි ලෙස ක්‍රියා කරනු ඇත."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> නතර කර ඇත."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> හට රිය පදවන අතරේ යතුරු පුවරුව භාවිතා කළ නොහැක."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sk/strings.xml b/libs/car-launcher-common/res/values-sk/strings.xml
new file mode 100644
index 0000000..911ab53
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sk/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pripnúť aplikáciu"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Odopnúť aplikáciu"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zastaviť aplikáciu"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informácie o aplikácii"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Chcete zastaviť aplikáciu?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ak vynútite zastavenie aplikácie, môže sa správať zvláštne."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> bola zastavená."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Pri šoférovaní nie je možné používať aplikáciu <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sl/strings.xml b/libs/car-launcher-common/res/values-sl/strings.xml
new file mode 100644
index 0000000..3e9ddfc
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sl/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Pripenjanje aplikacije"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Odpenjanje aplikacije"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Ustavi aplikacijo"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Podatki o aplikacijah"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Želite ustaviti aplikacijo?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Če boste vsilili zaustavitev aplikacije, morda ne bo pravilno delovala."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je ustavljena."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Med vožnjo ni mogoče uporabljati aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sq/strings.xml b/libs/car-launcher-common/res/values-sq/strings.xml
new file mode 100644
index 0000000..d34e189
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sq/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Gozhdo aplikacionin"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Zhgozhdo aplikacionin"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Ndalo aplikacionin"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Informacione mbi aplikacionin"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Të ndalohet aplikacioni?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Nëse e ndalon me forcë një aplikacion, ai mund të ketë çrregullime në funksionim."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> është ndaluar."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Aplikacioni \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" nuk mund të përdoret gjatë drejtimit të makinës."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sr/strings.xml b/libs/car-launcher-common/res/values-sr/strings.xml
new file mode 100644
index 0000000..e6f2b3b
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sr/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Закачи апликацију"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Откачи апликацију"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Заустави апликацију"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Информације о апликацији"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Желите да зауставите апликацију?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ако принудно зауставите апликацију, можда ће се понашати неочекивано."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је заустављена."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Не можете да користите апликацију <xliff:g id="APP_NAME">%1$s</xliff:g> током вожње."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sv/strings.xml b/libs/car-launcher-common/res/values-sv/strings.xml
new file mode 100644
index 0000000..8ce82bb
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sv/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Fäst appen"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Lossa appen"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Avsluta appen"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Appinformation"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Vill du avsluta appen?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Om du tvingar appen att avsluta kanske den inte fungerar som den ska."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> har avslutats."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g> går inte att använda under körning."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-sw/strings.xml b/libs/car-launcher-common/res/values-sw/strings.xml
new file mode 100644
index 0000000..cda2ca0
--- /dev/null
+++ b/libs/car-launcher-common/res/values-sw/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Bandika programu"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Bandua programu"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Zima programu"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Maelezo ya programu"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Ungependa kuzima programu?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Huenda programu isifanye kazi ipasavyo, iwapo utailazimisha kuzima."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Umezima <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Huwezi kutumia <xliff:g id="APP_NAME">%1$s</xliff:g> wakati unaendesha gari."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ta/strings.xml b/libs/car-launcher-common/res/values-ta/strings.xml
new file mode 100644
index 0000000..af3ebdb
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ta/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ஆப்ஸைப் பின் செய்தல்"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ஆப்ஸை அகற்றுதல்"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ஆப்ஸை நிறுத்து"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ஆப்ஸ் தகவல்கள்"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ஆப்ஸை நிறுத்தவா?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"ஆப்ஸை உடனே நிறுத்தினால் அது சரியாகச் செயல்படாமல் போகக்கூடும்."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் நிறுத்தப்பட்டது."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"வாகனம் ஓட்டும்போது <xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸைப் பயன்படுத்த முடியாது."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-te/strings.xml b/libs/car-launcher-common/res/values-te/strings.xml
new file mode 100644
index 0000000..c6e9151
--- /dev/null
+++ b/libs/car-launcher-common/res/values-te/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"యాప్‌ను పిన్ చేయండి"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"యాప్‌ను అన్‌పిన్ చేయండి"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"యాప్‌ను ఆపివేయండి"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"యాప్ సమాచారం"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"యాప్‌ను ఆపివేయాలా?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"మీరు యాప్‌ను ఫోర్స్ స్టాప్ చేస్తే, అది సరిగ్గా పని చేయకపోవచ్చు."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఆపివేయబడింది."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"డ్రైవింగ్ చేస్తున్నపుడు <xliff:g id="APP_NAME">%1$s</xliff:g>‌ను ఉపయోగించలేరు."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-th/strings.xml b/libs/car-launcher-common/res/values-th/strings.xml
new file mode 100644
index 0000000..78dc957
--- /dev/null
+++ b/libs/car-launcher-common/res/values-th/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ปักหมุดแอป"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"เลิกปักหมุดแอป"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"หยุดแอป"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ข้อมูลแอป"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"หยุดแอปไหม"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"แอปอาจทำงานผิดพลาดหากคุณบังคับให้หยุด"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"หยุด <xliff:g id="APP_NAME">%1$s</xliff:g> แล้ว"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ใช้ <xliff:g id="APP_NAME">%1$s</xliff:g> ขณะขับรถไม่ได้"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-tl/strings.xml b/libs/car-launcher-common/res/values-tl/strings.xml
new file mode 100644
index 0000000..668396d
--- /dev/null
+++ b/libs/car-launcher-common/res/values-tl/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"I-pin ang app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"I-unpin ang app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Ihinto ang app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Impormasyon ng app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Ihinto ang app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Kung sapilitan mong ihihinto ang isang app, posible itong hindi gumana nang maayos."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Inihinto ang <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Hindi magagamit ang <xliff:g id="APP_NAME">%1$s</xliff:g> habang nagmamaneho."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-tr/strings.xml b/libs/car-launcher-common/res/values-tr/strings.xml
new file mode 100644
index 0000000..738ac6c
--- /dev/null
+++ b/libs/car-launcher-common/res/values-tr/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Uygulamayı sabitle"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Uygulamanın sabitlemesini kaldır"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Uygulamayı durdur"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Uygulama bilgisi"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Uygulama durdurulsun mu?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Uygulamayı zorla durdurursanız düzgün çalışmayabilir."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> durduruldu."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"<xliff:g id="APP_NAME">%1$s</xliff:g>, sürüş sırasında kullanılamaz."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-uk/strings.xml b/libs/car-launcher-common/res/values-uk/strings.xml
new file mode 100644
index 0000000..e8df735
--- /dev/null
+++ b/libs/car-launcher-common/res/values-uk/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Закріпити додаток"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Відкріпити додаток"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Припинити роботу додатка"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Інформація про додаток"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Припинити роботу додатка?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Примусове припинення роботи додатка може призвести до збою в ньому."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"Роботу додатка <xliff:g id="APP_NAME">%1$s</xliff:g> припинено."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Додатком <xliff:g id="APP_NAME">%1$s</xliff:g> не можна користуватися під час руху."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-ur/strings.xml b/libs/car-launcher-common/res/values-ur/strings.xml
new file mode 100644
index 0000000..f9ce48c
--- /dev/null
+++ b/libs/car-launcher-common/res/values-ur/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"ایپ پن کریں"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"ایپ کی پن ہٹائیں"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"ایپ کو روکیں"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"ایپ کی معلومات"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"ایپ کو روکیں؟"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"اگر آپ کسی ایپ کو زبردستی روک دیتے ہیں تو یہ غلط برتاؤ کر سکتی ہے۔"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> کو روک دیا گیا ہے۔"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"ڈرائیو کرتے وقت <xliff:g id="APP_NAME">%1$s</xliff:g> کا استعمال نہیں کیا جا سکتا۔"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-uz/strings.xml b/libs/car-launcher-common/res/values-uz/strings.xml
new file mode 100644
index 0000000..d352e59
--- /dev/null
+++ b/libs/car-launcher-common/res/values-uz/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Ilovani mahkamlash"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Ilovani yechish"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Ilovani toʻxtatish"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Ilova haqida"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Ilova toʻxtatilsinmi?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Ilovani majburan toʻxtatish uning ishlashiga taʼsir koʻrsatishi mumkin."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasi toʻxtatildi."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Avtomobil rejimida <xliff:g id="APP_NAME">%1$s</xliff:g> ishlamaydi."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-vi/strings.xml b/libs/car-launcher-common/res/values-vi/strings.xml
new file mode 100644
index 0000000..4043959
--- /dev/null
+++ b/libs/car-launcher-common/res/values-vi/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Ghim ứng dụng"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Bỏ ghim ứng dụng"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Dừng ứng dụng"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Thông tin ứng dụng"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Dừng ứng dụng?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Nếu bạn buộc một ứng dụng dừng lại, ứng dụng đó có thể hoạt động không đúng cách."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã bị dừng."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"Không dùng <xliff:g id="APP_NAME">%1$s</xliff:g> được trong khi lái xe."</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-zh-rCN/strings.xml b/libs/car-launcher-common/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..b854ed2
--- /dev/null
+++ b/libs/car-launcher-common/res/values-zh-rCN/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"固定应用"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"取消固定应用"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"停止应用"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"应用信息"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"要停止应用吗?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"强行停止某个应用可能会导致其出现异常。"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"已停止“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"驾车时无法使用“<xliff:g id="APP_NAME">%1$s</xliff:g>”。"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-zh-rHK/strings.xml b/libs/car-launcher-common/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000..7d76ba2
--- /dev/null
+++ b/libs/car-launcher-common/res/values-zh-rHK/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"固定應用程式"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"取消固定應用程式"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"停止應用程式"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"應用程式資料"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"要停止應用程式嗎?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"強制停止應用程式,可能會導致操作不正常。"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"已停止「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"駕駛時無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-zh-rTW/strings.xml b/libs/car-launcher-common/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..f38b89b
--- /dev/null
+++ b/libs/car-launcher-common/res/values-zh-rTW/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"固定應用程式"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"取消固定應用程式"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"停止應用程式"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"應用程式資訊"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"要停止應用程式嗎?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"請注意,強制停止可能會使應用程式出現異常行為。"</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"已停止「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"開車時無法使用「<xliff:g id="APP_NAME">%1$s</xliff:g>」。"</string>
+</resources>
diff --git a/libs/car-launcher-common/res/values-zu/strings.xml b/libs/car-launcher-common/res/values-zu/strings.xml
new file mode 100644
index 0000000..6d52830
--- /dev/null
+++ b/libs/car-launcher-common/res/values-zu/strings.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 
+  ~ Copyright (C) 2024 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.
+   -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label" msgid="7319916279393348946">"Phina i-app"</string>
+    <string name="dock_unpin_shortcut_label" msgid="1657441916649851101">"Susa ukuphina i-app"</string>
+    <string name="stop_app_shortcut_label" msgid="5893149773635675147">"Misa i-app"</string>
+    <string name="app_info_shortcut_label" msgid="6724408677044314793">"Ulwazi lwe-app"</string>
+    <string name="stop_app_dialog_title" msgid="7027389231920405129">"Misa i-app?"</string>
+    <string name="stop_app_dialog_text" msgid="4997162043741899166">"Uma uphoqelela ukumisa i-app, kungenzeka ukuthi ingasebenzi kahle."</string>
+    <string name="stop_app_success_toast_text" msgid="3740858295088091414">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> imisiwe."</string>
+    <string name="ndo_launch_fail_toast_text" msgid="633927891331266600">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> ayikwazi ukusetshenziswa ngenkathi ushayela."</string>
+</resources>
diff --git a/docklib-util/res/values/strings.xml b/libs/car-launcher-common/res/values/colors.xml
similarity index 66%
copy from docklib-util/res/values/strings.xml
copy to libs/car-launcher-common/res/values/colors.xml
index 2c24df7..b92b974 100644
--- a/docklib-util/res/values/strings.xml
+++ b/libs/car-launcher-common/res/values/colors.xml
@@ -1,6 +1,6 @@
-<?xml version="1.0" encoding="UTF-8" ?>
+<?xml version="1.0" encoding="utf-8"?>
 <!--
-  ~ Copyright (C) 2023 The Android Open Source Project
+  ~ Copyright (C) 2024 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.
@@ -14,9 +14,6 @@
   ~ See the License for the specific language governing permissions and
   ~ limitations under the License.
   -->
-
 <resources>
-    <!-- todo(b/314817575): update the string to final value -->
-    <string name="dock_pin_shortcut_label">Pin to the dock</string>
-    <string name="dock_unpin_shortcut_label">Unpin from the dock</string>
+    <color name="shortcuts_icon_color">#FFFFFFFF</color>
 </resources>
diff --git a/libs/car-launcher-common/res/values/strings.xml b/libs/car-launcher-common/res/values/strings.xml
new file mode 100644
index 0000000..66933d0
--- /dev/null
+++ b/libs/car-launcher-common/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (C) 2024 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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="dock_pin_shortcut_label">Pin app</string>
+    <string name="dock_unpin_shortcut_label">Unpin app</string>
+    <string name="stop_app_shortcut_label">Stop app</string>
+    <string name="app_info_shortcut_label">App info</string>
+
+    <!-- Manage applications, title for dialog when killing persistent apps. [CHAR LIMIT=40] -->
+    <string name="stop_app_dialog_title">Stop app?</string>
+    <!-- Manage applications, text for dialog when killing persistent apps. [CHAR LIMIT=200] -->
+    <string name="stop_app_dialog_text">If you force stop an app, it may
+        misbehave.
+    </string>
+    <string name="stop_app_success_toast_text">
+        <xliff:g id="app_name" example="Radio">%1$s</xliff:g>
+        has been stopped.
+    </string>
+
+    <!-- String for toast when user tries to launch NDO app when driving -->
+    <string name="ndo_launch_fail_toast_text">
+        <xliff:g id="app_name" example="Settings">%1$s</xliff:g>
+        can\'t be used while driving.
+    </string>
+</resources>
diff --git a/libs/car-launcher-common/src/com/android/car/carlaunchercommon/proto/ProtoDataSource.kt b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/proto/ProtoDataSource.kt
new file mode 100644
index 0000000..dc52cbf
--- /dev/null
+++ b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/proto/ProtoDataSource.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.proto
+
+import android.util.Log
+import com.google.protobuf.MessageLite
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import java.io.IOException
+import java.io.InputStream
+import java.io.OutputStream
+
+/**
+ * Class level abstraction representing a proto file holding app data.
+ *
+ * Only a single controller should hold reference to this class. All methods that perform read or
+ * write operations must be thread safe and idempotent.
+ *
+ * @param <T> the proto object type that this data file is holding
+</T> */
+// TODO: b/301482942 This class is copied from AppGrid. We should reuse it in AppGrid
+abstract class ProtoDataSource<T : MessageLite>(private val dataFile: File) {
+    private var mInputStream: FileInputStream? = null
+    private var mOutputStream: FileOutputStream? = null
+
+    /**
+     * @return true if the file exists on disk, and false otherwise.
+     */
+    fun exists(): Boolean {
+        return try {
+            dataFile.exists() && dataFile.canRead()
+        } catch (e: Exception) {
+            false
+        }
+    }
+
+    /**
+     * Writes the [MessageLite] subclass T to the file represented by this object.
+     * This method will write data as bytes to the declared file using protobuf library.
+     */
+    fun writeToFile(data: T) {
+        try {
+            if (mOutputStream == null) {
+                mOutputStream = FileOutputStream(dataFile, false)
+            }
+            writeDelimitedTo(data, mOutputStream)
+        } catch (e: IOException) {
+            Log.e(TAG, "Dock item list not written to file successfully.", e)
+        } finally {
+            try {
+                mOutputStream?.apply {
+                    flush()
+                    fd.sync()
+                    close()
+                }
+                mOutputStream = null
+            } catch (e: IOException) {
+                Log.e(TAG, "Unable to close output stream. ")
+            }
+        }
+    }
+
+    /**
+     * Reads the [MessageLite] subclass T from the file represented by this object.
+     * This method will parse the bytes using protobuf library.
+     */
+    fun readFromFile(): T? {
+        if (!exists()) {
+            Log.e(TAG, "File does not exist. Cannot read from file.")
+            return null
+        }
+        var result: T? = null
+        try {
+            if (mInputStream == null) {
+                mInputStream = FileInputStream(dataFile)
+            }
+            result = parseDelimitedFrom(mInputStream)
+        } catch (e: IOException) {
+            Log.e(TAG, "Read from input stream not successfully")
+        } finally {
+            try {
+                mInputStream?.close()
+                mInputStream = null
+            } catch (e: IOException) {
+                Log.e(TAG, "Unable to close input stream")
+            }
+        }
+        return result
+    }
+
+    /**
+     * This method will be called by [ProtoDataSource.readFromFile].
+     *
+     * Implementation is left to subclass since [MessageLite.parseDelimitedFrom]
+     * requires a defined class at compile time. Subclasses should implement this method by directly
+     * calling YourMessageType.parseDelimitedFrom(inputStream) here.
+     *
+     * @param inputStream the input stream to be which the data source should read from.
+     * @return the object T written to this file.
+     * @throws IOException an IOException for when reading from proto fails.
+     */
+    @Throws(IOException::class)
+    protected abstract fun parseDelimitedFrom(inputStream: InputStream?): T?
+
+    /**
+     * This method will be called by [ProtoDataSource.writeToFile].
+     *
+     * Implementation is left to subclass since [MessageLite.writeDelimitedTo]
+     * requires a defined class at compile time. Subclasses should implement this method by directly
+     * calling T.writeDelimitedTo(outputStream) here.
+     *
+     * @param outputData the output data T to be written to the file.
+     * @param outputStream the output stream which the data should be written to.
+     * @throws IOException an IO Exception for when writing to proto fails.
+     */
+    @Throws(IOException::class)
+    protected abstract fun writeDelimitedTo(outputData: T, outputStream: OutputStream?)
+
+    companion object {
+        private const val TAG = "ProtoDataSource"
+    }
+
+    override fun toString(): String {
+        return dataFile.absolutePath
+    }
+}
diff --git a/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/AppInfoShortcutItem.kt b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/AppInfoShortcutItem.kt
new file mode 100644
index 0000000..689f82a
--- /dev/null
+++ b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/AppInfoShortcutItem.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.shortcuts
+
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.car.carlaunchercommon.R
+import com.android.car.hidden.apis.HiddenApiAccess.startActivityAsUser
+import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
+
+class AppInfoShortcutItem constructor(
+    private val context: Context,
+    private val packageName: String,
+    private val userHandle: UserHandle
+) : CarUiShortcutsPopup.ShortcutItem {
+    private companion object {
+        private const val PACKAGE_URI_PREFIX = "package:"
+    }
+
+    override fun data(): CarUiShortcutsPopup.ItemData {
+        return CarUiShortcutsPopup.ItemData(
+            R.drawable.ic_app_info,
+            context.resources.getString(R.string.app_info_shortcut_label)
+        )
+    }
+
+    override fun onClick(): Boolean {
+        val packageURI = Uri.parse(PACKAGE_URI_PREFIX + packageName)
+        val intent = Intent(
+            Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+            packageURI
+        )
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+        startActivityAsUser(context, intent, userHandle)
+        return true
+    }
+
+    override fun isEnabled(): Boolean {
+        return true
+    }
+}
diff --git a/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItem.kt b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItem.kt
new file mode 100644
index 0000000..f0cc2a2
--- /dev/null
+++ b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItem.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.shortcuts
+
+import android.app.Activity
+import android.app.ActivityManager
+import android.app.admin.DevicePolicyManager
+import android.car.media.CarMediaManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.os.UserManager
+import android.util.Log
+import android.view.WindowManager
+import android.widget.Toast
+import androidx.annotation.VisibleForTesting
+import com.android.car.carlaunchercommon.R
+import com.android.car.hidden.apis.HiddenApiAccess.hasBaseUserRestriction
+import com.android.car.hidden.apis.HiddenApiAccess.isDebuggable
+import com.android.car.ui.AlertDialogBuilder
+import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
+
+/**
+ * @property context the [Context] for the user that the app is running in.
+ * @property mediaServiceComponents list of [ComponentName] of the services the adhere to the media
+ * service interface
+ */
+open class ForceStopShortcutItem(
+    private val context: Context,
+    private val packageName: String,
+    private val displayName: CharSequence,
+    private val carMediaManager: CarMediaManager?,
+    private val mediaServiceComponents: Set<ComponentName>
+) : CarUiShortcutsPopup.ShortcutItem {
+    // todo(b/312718542): hidden class(CarMediaManager) usage
+
+    companion object {
+        private const val TAG = "ForceStopShortcutItem"
+        private val DEBUG = isDebuggable()
+    }
+
+    override fun data(): CarUiShortcutsPopup.ItemData {
+        return CarUiShortcutsPopup.ItemData(
+            R.drawable.ic_force_stop_caution_icon,
+            context.resources.getString(
+                R.string.stop_app_shortcut_label
+            )
+        )
+    }
+
+    override fun onClick(): Boolean {
+        val builder = getAlertDialogBuilder(context)
+            .setTitle(R.string.stop_app_dialog_title)
+            .setMessage(R.string.stop_app_dialog_text)
+            .setPositiveButton(android.R.string.ok) { _, _ ->
+                forceStop(packageName, displayName)
+            }
+            .setNegativeButton(
+                android.R.string.cancel,
+                null // listener
+            )
+        builder.create().let {
+            if (context !is Activity || context.window.decorView.windowToken == null) {
+                // If the context is not an Activity or lacks a valid window token,
+                // it's likely we're in a non-Activity context (e.g., Service, SystemUI).
+                // To ensure the AlertDialog is displayed properly, we explicitly set its window
+                // type to SYSTEM_ALERT, allowing it to overlay other windows, even from SystemUI.
+                it.window?.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
+            }
+            it.show()
+        }
+        return true
+    }
+
+    override fun isEnabled(): Boolean {
+        return shouldAllowStopApp(packageName)
+    }
+
+    /**
+     * @param packageName name of the package to stop the app
+     * @return true if an app should show the Stop app action
+     */
+    private fun shouldAllowStopApp(packageName: String): Boolean {
+        val dm = context.getSystemService(DevicePolicyManager::class.java)
+        if (dm == null || dm.packageHasActiveAdmins(packageName)) {
+            return false
+        }
+        try {
+            val appInfo = context.packageManager.getApplicationInfo(
+                packageName,
+                PackageManager.ApplicationInfoFlags.of(PackageManager.GET_META_DATA.toLong())
+            )
+            // Show only if the User has no restrictions to force stop this app
+            if (hasUserRestriction(appInfo)) {
+                return false
+            }
+            // Show only if the app is running
+            if (appInfo.flags and ApplicationInfo.FLAG_STOPPED == 0) {
+                return true
+            }
+        } catch (e: PackageManager.NameNotFoundException) {
+            if (DEBUG) Log.d(TAG, "shouldAllowStopApp() package $packageName was not found")
+        }
+        return false
+    }
+
+    /**
+     * @return true if the user has restrictions to force stop an app with `appInfo`
+     */
+    private fun hasUserRestriction(appInfo: ApplicationInfo): Boolean {
+        val restriction = UserManager.DISALLOW_APPS_CONTROL
+        val userManager = context.getSystemService(UserManager::class.java)
+        if (userManager == null) {
+            if (DEBUG) Log.e(TAG, " Disabled because UserManager is null")
+            return true
+        }
+        if (!userManager.hasUserRestriction(restriction)) {
+            return false
+        }
+        val user = UserHandle.getUserHandleForUid(appInfo.uid)
+        if (hasBaseUserRestriction(userManager, restriction, user)) {
+            if (DEBUG) Log.d(TAG, " Disabled because $user has $restriction restriction")
+            return true
+        }
+        // Not disabled for this User
+        return false
+    }
+
+    /**
+     * Force stops an app
+     */
+    @VisibleForTesting
+    fun forceStop(packageName: String, displayName: CharSequence) {
+        // Both MEDIA_SOURCE_MODE_BROWSE and MEDIA_SOURCE_MODE_PLAYBACK should be replaced to their
+        // previous available values
+        maybeReplaceMediaSource(packageName, CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)
+        maybeReplaceMediaSource(packageName, CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)
+
+        val activityManager = context.getSystemService(ActivityManager::class.java) ?: return
+        // todo(b/312718542): hidden api(ActivityManager.forceStopPackage) usage
+        activityManager.forceStopPackage(packageName)
+        val message = context.resources.getString(R.string.stop_app_success_toast_text, displayName)
+        createToast(context, message, Toast.LENGTH_LONG).show()
+    }
+
+    /**
+     * Updates the MediaSource to second most recent if [packageName] is current media source.
+     * @param mode media source mode (ex. [CarMediaManager.MEDIA_SOURCE_MODE_BROWSE])
+     */
+    private fun maybeReplaceMediaSource(packageName: String, mode: Int) {
+        if (!isCurrentMediaSource(packageName, mode)) {
+            if (DEBUG) Log.e(TAG, "Not current media source")
+            return
+        }
+        // find the most recent source from history not equal to force-stopping package
+        val mediaSources = carMediaManager?.getLastMediaSources(mode)
+        var componentName = mediaSources?.firstOrNull { it?.packageName != packageName }
+        if (componentName == null) {
+            // no recent package found, find from all available media services.
+            componentName = mediaServiceComponents.firstOrNull { it.packageName != packageName }
+        }
+        if (componentName == null) {
+            if (DEBUG) Log.e(TAG, "Stop-app, no alternative media service found")
+            return
+        }
+        carMediaManager?.setMediaSource(componentName, mode)
+    }
+
+    private fun isCurrentMediaSource(packageName: String, mode: Int): Boolean {
+        val componentName = carMediaManager?.getMediaSource(mode)
+            ?: return false // There is no current media source.
+        if (DEBUG) Log.e(TAG, "isCurrentMediaSource: $packageName, $componentName")
+        return componentName.packageName == packageName
+    }
+
+    /**
+     * Should be overridden in the test to provide a mock [AlertDialogBuilder]
+     */
+    @VisibleForTesting
+    open fun getAlertDialogBuilder(context: Context) = AlertDialogBuilder(context)
+
+    /**
+     * Should be overridden in the test to provide a mock [Toast]
+     */
+    @VisibleForTesting
+    open fun createToast(context: Context, text: CharSequence, duration: Int): Toast =
+        Toast.makeText(context, text, duration)
+}
diff --git a/docklib-util/src/com/android/car/dockutil/shortcuts/PinShortcutItem.kt b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItem.kt
similarity index 67%
rename from docklib-util/src/com/android/car/dockutil/shortcuts/PinShortcutItem.kt
rename to libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItem.kt
index 6aba157..88f95b1 100644
--- a/docklib-util/src/com/android/car/dockutil/shortcuts/PinShortcutItem.kt
+++ b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItem.kt
@@ -1,7 +1,23 @@
-package com.android.car.dockutil.shortcuts
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.shortcuts
 
 import android.content.res.Resources
-import com.android.car.dockutil.R
+import com.android.car.carlaunchercommon.R
 import com.android.car.ui.shortcutspopup.CarUiShortcutsPopup
 
 /**
diff --git a/libs/car-launcher-common/src/com/android/car/carlaunchercommon/toasts/NonDrivingOptimizedLaunchFailedToast.kt b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/toasts/NonDrivingOptimizedLaunchFailedToast.kt
new file mode 100644
index 0000000..17108a5
--- /dev/null
+++ b/libs/car-launcher-common/src/com/android/car/carlaunchercommon/toasts/NonDrivingOptimizedLaunchFailedToast.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.toasts
+
+import android.content.Context
+import android.widget.Toast
+import com.android.car.carlaunchercommon.R
+
+/**
+ * Helper to create and show a [Toast] when user taps on a non driving optimized app when
+ * UX restrictions are in effect.
+ */
+class NonDrivingOptimizedLaunchFailedToast {
+    companion object {
+        /**
+         * Show the NDO launch fail toast
+         *
+         * @param displayName app's display name/label
+         */
+        fun showToast(context: Context, displayName: String) {
+            getToast(context, displayName).show()
+        }
+
+        private fun getToast(context: Context, displayName: String): Toast {
+            val warningText: String = context.getResources()
+                .getString(R.string.ndo_launch_fail_toast_text, displayName)
+
+            return Toast.makeText(context, warningText, Toast.LENGTH_LONG)
+        }
+    }
+}
diff --git a/libs/car-launcher-common/tests/Android.bp b/libs/car-launcher-common/tests/Android.bp
new file mode 100644
index 0000000..e72cea6
--- /dev/null
+++ b/libs/car-launcher-common/tests/Android.bp
@@ -0,0 +1,66 @@
+//
+// Copyright (C) 2024 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+    default_team: "trendy_team_system_experience",
+}
+
+android_test {
+    name: "CarLauncherCommonTests",
+
+    srcs: [
+        "src/**/*.java",
+        "src/**/*.kt",
+    ],
+
+    libs: [
+        "android.test.base.stubs.system",
+        "android.car",
+    ],
+
+    optimize: {
+        enabled: false,
+    },
+
+    static_libs: [
+        "androidx.test.runner",
+        "androidx.test.ext.junit",
+        "mockito-target-extended",
+        "mockito-kotlin2",
+        "truth",
+        "CarLauncherCommon",
+        "flag-junit",
+    ],
+
+    manifest: "AndroidManifest.xml",
+
+    instrumentation_for: "CarLauncherCommon",
+
+    dex_preopt: {
+        enabled: false,
+    },
+
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+
+    test_suites: [
+        "automotive-tests",
+    ],
+}
diff --git a/libs/car-launcher-common/tests/AndroidManifest.xml b/libs/car-launcher-common/tests/AndroidManifest.xml
new file mode 100644
index 0000000..a1149df
--- /dev/null
+++ b/libs/car-launcher-common/tests/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.car.carlaunchercommon.test">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:label="Tests for Car Launcher common utility lib"
+        android:targetPackage="com.android.car.carlaunchercommon.test" />
+</manifest>
diff --git a/libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItemTest.kt b/libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItemTest.kt
new file mode 100644
index 0000000..7365314
--- /dev/null
+++ b/libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/ForceStopShortcutItemTest.kt
@@ -0,0 +1,442 @@
+/*
+ * Copyright (C) 2023 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.carlaunchercommon.shortcuts
+
+import android.app.ActivityManager
+import android.app.AlertDialog
+import android.car.media.CarMediaManager
+import android.content.ComponentName
+import android.content.Context
+import android.content.DialogInterface
+import android.content.res.Resources
+import android.widget.Toast
+import com.android.car.ui.AlertDialogBuilder
+import org.junit.Test
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.ArgumentMatchers.nullable
+import org.mockito.Mockito
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+class ForceStopShortcutItemTest {
+    private val resourcesMock = mock<Resources> {
+        on { getString(anyInt(), anyString()) } doReturn "testResourceString"
+    }
+    private val activityManagerMock = mock<ActivityManager> {}
+    private val contextMock = mock<Context> {
+        on { resources } doReturn resourcesMock
+        on { getSystemService(eq(ActivityManager::class.java)) } doReturn activityManagerMock
+    }
+    private val toastMock = mock<Toast> {}
+    private val carMediaManagerMock = mock<CarMediaManager> {}
+    private val alertDialogMock = mock<AlertDialog> {}
+    private val alertDialogBuilderMock = mock<AlertDialogBuilder> {
+        on { setTitle(anyInt()) } doReturn it
+        on { setMessage(anyInt()) } doReturn it
+        on { setPositiveButton(anyInt(), any<DialogInterface.OnClickListener>()) } doReturn it
+        on {
+            setNegativeButton(anyInt(), nullable(DialogInterface.OnClickListener::class.java))
+        } doReturn it
+        on { create() } doReturn alertDialogMock
+    }
+
+    @Test
+    fun forceStop_nonMediaApp_forceStopPackageCalled() {
+        val packageName = "com.example.app"
+
+        createForceStopShortcutItem(
+            contextMock,
+            packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(packageName, displayName = "testDisplayName")
+
+        verify(activityManagerMock).forceStopPackage(eq(packageName))
+    }
+
+    @Test
+    fun forceStop_nonMediaApp_shouldNotChangeMediaSource() {
+        createForceStopShortcutItem(
+            contextMock,
+            packageName = "com.example.app",
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(packageName = "com.example.app", displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, Mockito.never())
+            .setMediaSource(any<ComponentName>(), anyInt())
+    }
+
+    @Test
+    fun forceStop_activeBrowseMediaSrc_foundLastBrowseSrc_forceStopPackageCalled() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastBrowsedMediaComponent =
+            ComponentName("lastBrowsedMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent, lastBrowsedMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(activityManagerMock).forceStopPackage(forceStoppedComponent.packageName)
+    }
+
+    @Test
+    fun forceStop_activeBrowseMediaSrc_foundLastBrowseSrc_browseMediaSrcSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastBrowsedMediaComponent =
+            ComponentName("lastBrowsedMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent, lastBrowsedMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            lastBrowsedMediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_BROWSE
+        )
+    }
+
+    @Test
+    fun forceStop_activeBrowseMediaSrc_foundLastBrowseSrc_playbackMediaSrcNotSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastBrowsedMediaComponent =
+            ComponentName("lastBrowsedMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent, lastBrowsedMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, never()).setMediaSource(
+            any<ComponentName>(),
+            eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)
+        )
+    }
+
+    @Test
+    fun forceStop_activeBrowseMediaSrc_noLastBrowseSrc_browseMediaSrcSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val mediaComponent = ComponentName("mediaComponent", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf(forceStoppedComponent, mediaComponent)
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            mediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_BROWSE
+        )
+    }
+
+    @Test
+    fun forceStop_activeBrowseMediaSrc_noLastBrowseSrc_noMediaService_browseMediaSrcNotSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, Mockito.never()).setMediaSource(
+            any<ComponentName>(),
+            anyInt()
+        )
+    }
+
+    @Test
+    fun forceStop_activePlaybackMediaSrc_lastPlaybackSrcFound_forceStopPackageCalled() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastPlaybackMediaComponent =
+            ComponentName("lastPlaybackMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent, lastPlaybackMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(activityManagerMock).forceStopPackage(forceStoppedComponent.packageName)
+    }
+
+    @Test
+    fun forceStop_activePlaybackMediaSrc_lastPlaybackSrcFound_playbackMediaSrcSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastPlaybackMediaComponent =
+            ComponentName("lastPlaybackMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent, lastPlaybackMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            lastPlaybackMediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK
+        )
+    }
+
+    @Test
+    fun forceStop_activePlaybackMediaSrc_lastPlaybackSrcFound_browseMediaSrcNotSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastPlaybackMediaComponent =
+            ComponentName("lastPlaybackMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent, lastPlaybackMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, never()).setMediaSource(
+            any<ComponentName>(),
+            eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)
+        )
+    }
+
+    @Test
+    fun forceStop_activePlaybackMediaSrc_noLastPlaybackSrc_playbackMediaSrcSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val mediaComponent = ComponentName("mediaComponent", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf(forceStoppedComponent, mediaComponent)
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            mediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK
+        )
+    }
+
+    @Test
+    fun forceStop_activePlaybackMediaSrc_noLastPlaybackSrc_noMediaService_playbackMediaSrcNotSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName("otherPkg", "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, Mockito.never()).setMediaSource(
+            any<ComponentName>(),
+            anyInt()
+        )
+    }
+
+    @Test
+    fun forceStop_activeBrowseAndPlaybackMediaSrc_foundLastSrcs_browseAndPlaybackMediaSrcSet() {
+        val forceStoppedComponent =
+            ComponentName("forceStoppedPkg", "testClassName")
+        val lastPlaybackMediaComponent =
+            ComponentName("lastPlaybackMediaPkg", "testClassName")
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE))
+        ).doReturn(listOf(forceStoppedComponent, lastPlaybackMediaComponent))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(forceStoppedComponent)
+        whenever(
+            carMediaManagerMock.getLastMediaSources(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK))
+        ).doReturn(listOf(forceStoppedComponent, lastPlaybackMediaComponent))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedComponent.packageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedComponent.packageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            lastPlaybackMediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_BROWSE
+        )
+        Mockito.verify(carMediaManagerMock).setMediaSource(
+            lastPlaybackMediaComponent,
+            CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK
+        )
+    }
+
+    @Test
+    fun forceStop_notActiveMediaBrowseNorPlaybackSource_forceStopPackageCalled() {
+        val forceStoppedPackageName = "forceStoppedPackageName"
+        val activeMediaPackageName = "activeMediaPackageName"
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName(activeMediaPackageName, "testClassName"))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName(activeMediaPackageName, "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedPackageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedPackageName, displayName = "testDisplayName")
+
+        Mockito.verify(activityManagerMock).forceStopPackage(forceStoppedPackageName)
+    }
+
+    @Test
+    fun forceStop_notActiveMediaBrowseNorPlaybackSource_doesNotChangeMediaSource() {
+        val forceStoppedPackageName = "forceStoppedPackageName"
+        val activeMediaPackageName = "activeMediaPackageName"
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_BROWSE)))
+            .doReturn(ComponentName(activeMediaPackageName, "testClassName"))
+        whenever(carMediaManagerMock.getMediaSource(eq(CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK)))
+            .doReturn(ComponentName(activeMediaPackageName, "testClassName"))
+
+        createForceStopShortcutItem(
+            contextMock,
+            forceStoppedPackageName,
+            carMediaManagerMock,
+            mediaServiceComponents = setOf()
+        ).forceStop(forceStoppedPackageName, displayName = "testDisplayName")
+
+        Mockito.verify(carMediaManagerMock, Mockito.never()).setMediaSource(
+            any<ComponentName>(),
+            anyInt()
+        )
+    }
+
+    private fun createForceStopShortcutItem(
+        context: Context,
+        packageName: String,
+        carMediaManager: CarMediaManager?,
+        mediaServiceComponents: Set<ComponentName>
+
+    ): ForceStopShortcutItem {
+        return object : ForceStopShortcutItem(
+            context,
+            packageName,
+            displayName = "testDisplayName",
+            carMediaManager,
+            mediaServiceComponents
+        ) {
+            override fun getAlertDialogBuilder(context: Context) = alertDialogBuilderMock
+            override fun createToast(context: Context, text: CharSequence, duration: Int) =
+                toastMock
+        }
+    }
+}
diff --git a/docklib-util/tests/src/com/android/car/dockutil/shortcuts/PinShortcutItemTest.kt b/libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItemTest.kt
similarity index 62%
rename from docklib-util/tests/src/com/android/car/dockutil/shortcuts/PinShortcutItemTest.kt
rename to libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItemTest.kt
index d3498ae..167e3df 100644
--- a/docklib-util/tests/src/com/android/car/dockutil/shortcuts/PinShortcutItemTest.kt
+++ b/libs/car-launcher-common/tests/src/com/android/car/carlaunchercommon/shortcuts/PinShortcutItemTest.kt
@@ -1,4 +1,20 @@
-package com.android.car.dockutil.shortcuts
+/*
+ * Copyright (C) 2024 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.carlaunchercommon.shortcuts
 
 import android.content.res.Resources
 import androidx.test.ext.junit.runners.AndroidJUnit4
diff --git a/libs/hidden-apis-compat/hidden-apis-disabled/build.gradle b/libs/hidden-apis-compat/hidden-apis-disabled/build.gradle
new file mode 100644
index 0000000..9b08470
--- /dev/null
+++ b/libs/hidden-apis-compat/hidden-apis-disabled/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+plugins {
+    id "com.android.library"
+}
+
+android {
+    namespace 'com.android.car.hidden.apis'
+    compileSdk gradle.ext.aaosTargetSDK
+
+    defaultConfig {
+        minSdk gradle.ext.aaosLatestSDK
+        targetSdk gradle.ext.aaosLatestSDK
+        versionCode gradle.ext.getVersionCode()
+        versionName gradle.ext.getVersionName()
+    }
+
+    sourceSets {
+        main {
+            java.srcDirs = ['src']
+        }
+    }
+
+    dependencies {
+        implementation 'junit:junit:4.13.2'
+    }
+}
diff --git a/libs/hidden-apis-compat/hidden-apis-disabled/src/android/platform/test/flag/junit/SetFlagsRule.java b/libs/hidden-apis-compat/hidden-apis-disabled/src/android/platform/test/flag/junit/SetFlagsRule.java
new file mode 100644
index 0000000..58c8b68
--- /dev/null
+++ b/libs/hidden-apis-compat/hidden-apis-disabled/src/android/platform/test/flag/junit/SetFlagsRule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2024 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 android.platform.test.flag.junit;
+
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Fake implementation of SetFlagRule for gradle builds
+ */
+public class SetFlagsRule implements TestRule {
+
+    /**
+     * Do nothing function. To enable the feature use cmd before running the tests.
+     * <p>
+     * Called for gradle builds only
+     */
+    public void enableFlags(String featureFlag) {
+        System.out.println(
+                "Gradle builds: Ensure that flagDockFeature is enabled before running the tests, "
+                        + "otherwise the tests might fail prematurely");
+    }
+
+    @Override
+    public Statement apply(Statement base, Description description) {
+        return base;
+    }
+
+}
diff --git a/libs/appgrid/lib/hidden_apis_disabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java b/libs/hidden-apis-compat/hidden-apis-disabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
similarity index 71%
rename from libs/appgrid/lib/hidden_apis_disabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java
rename to libs/hidden-apis-compat/hidden-apis-disabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
index b3fbb7d..1ea7abb 100644
--- a/libs/appgrid/lib/hidden_apis_disabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java
+++ b/libs/hidden-apis-compat/hidden-apis-disabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 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.
@@ -14,14 +14,18 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher.hidden;
+package com.android.car.hidden.apis;
 
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.view.Display;
 import android.view.DragEvent;
 import android.view.SurfaceControl;
 
-/***
+/**
  * This classes is a place to surface all hidden/blocked apis which are not available to be accessed
  * by unbundled application.
  * All of the apis here are only visible to platform apps.
@@ -29,10 +33,18 @@
  * Note: This folder location is compiled only with Gradle builds. There exists alternative Soong
  * build version under (hidden_apis_enabled) folder.
  * Gradle version fails softly when we need this api, so some functionality might appear broken.
- *
+ * <p>
  * Gradle builds are testing/development only. Please build with Soong once before merging changes
  */
 public class HiddenApiAccess {
+
+    /**
+     * @return true: For gradle builds can always consider as debuggable
+     */
+    public static boolean isDebuggable() {
+        return true;
+    }
+
     /**
      * Empty/Null SurfaceControl
      */
@@ -54,4 +66,18 @@
      * android.view.View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION
      */
     public static int DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION = 1 << 11;
+
+    /**
+     * Always returns {@link Display.DEFAULT_DISPLAY}
+     */
+    public static int getDisplayId(ActivityManager.RunningTaskInfo taskInfo) {
+        return Display.DEFAULT_DISPLAY;
+    }
+
+    /**
+     * Start Activity with the current user
+     */
+    public static void startActivityAsUser(Context context, Intent intent, UserHandle userHandle) {
+        context.startActivity(intent);
+    }
 }
diff --git a/libs/hidden-apis-compat/hidden-apis-enabled/Android.bp b/libs/hidden-apis-compat/hidden-apis-enabled/Android.bp
new file mode 100644
index 0000000..866f773
--- /dev/null
+++ b/libs/hidden-apis-compat/hidden-apis-enabled/Android.bp
@@ -0,0 +1,22 @@
+//
+// Copyright (C) 2024 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.
+//
+
+filegroup {
+    name: "hidden_api_enabled_srcs",
+    srcs: [
+        "src/**/*.java",
+    ],
+}
diff --git a/libs/appgrid/lib/hidden_apis_enabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java b/libs/hidden-apis-compat/hidden-apis-enabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
similarity index 68%
rename from libs/appgrid/lib/hidden_apis_enabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java
rename to libs/hidden-apis-compat/hidden-apis-enabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
index b36f057..21d985b 100644
--- a/libs/appgrid/lib/hidden_apis_enabled/src/com/android/car/carlauncher/hidden/HiddenApiAccess.java
+++ b/libs/hidden-apis-compat/hidden-apis-enabled/src/com/android/car/hidden/apis/HiddenApiAccess.java
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.car.carlauncher.hidden;
+package com.android.car.hidden.apis;
 
+import android.annotation.SuppressLint;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.view.DragEvent;
@@ -33,6 +38,13 @@
 public class HiddenApiAccess {
 
     /**
+     * @return true: For gradle builds can always consider as debuggable
+     */
+    public static boolean isDebuggable() {
+        return Build.isDebuggable();
+    }
+
+    /**
      * Calls hidden api {@link  android.view.DragEvent#getDragSurface}
      */
     public static SurfaceControl getDragSurface(DragEvent event) {
@@ -42,6 +54,7 @@
     /**
      * Calls hidden api {@link  android.os.UserManager#hasBaseUserRestriction}
      */
+    @SuppressLint("MissingPermission")
     public static boolean hasBaseUserRestriction(UserManager userManager, String restriction,
             UserHandle user) {
         return userManager.hasBaseUserRestriction(restriction, user);
@@ -52,4 +65,20 @@
      */
     public static int DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION =
             View.DRAG_FLAG_REQUEST_SURFACE_FOR_RETURN_ANIMATION;
+
+
+    /**
+     * Calls the hidden api
+     */
+    public static int getDisplayId(ActivityManager.RunningTaskInfo taskInfo) {
+        return taskInfo.getDisplayId();
+    }
+
+    /**
+     * Start Activity with the current user
+     */
+    @SuppressLint("MissingPermission")
+    public static void startActivityAsUser(Context context, Intent intent, UserHandle userHandle) {
+        context.startActivityAsUser(intent, userHandle);
+    }
 }
diff --git a/settings.gradle b/settings.gradle
index 195d14d..11fa3ff 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -34,3 +34,7 @@
 include ':libs:car-apps-common'
 include ':libs:car-media-common'
 include ':libs:car-ui-lib'
+include ':libs:aconfig-platform-compat'
+include ':libs:hidden-apis-compat:hidden-apis-disabled'
+include ':libs:car-launcher-common'
+include ':docklib-util'