DO NOT MERGE - Merge Android 10 into master

Bug: 139893257
Change-Id: I73df6396cf6dcfbb954c2bddfef186dceb26ea30
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index 2e24262..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,45 +0,0 @@
-// 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.
-//
-
-android_app {
-    name: "CarLauncher",
-    srcs: ["src/**/*.java"],
-    resource_dirs: ["res"],
-    platform_apis: true,
-    certificate: "platform",
-    privileged: true,
-    overrides: [
-        "Launcher2",
-        "Launcher3",
-        "Launcher3QuickStep",
-    ],
-    optimize: {
-        enabled: false,
-    },
-    dex_preopt: {
-        enabled: false,
-    },
-    static_libs: [
-        "androidx.car_car",
-        "androidx-constraintlayout_constraintlayout",
-        "androidx-constraintlayout_constraintlayout-solver",
-    ],
-    libs: ["android.car"],
-    product_variables: {
-        pdk: {
-            enabled: false,
-        },
-    },
-}
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..28fd418
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,63 @@
+# 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.
+#
+
+ifneq ($(TARGET_BUILD_PDK), true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+
+LOCAL_PACKAGE_NAME := CarLauncher
+
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+LOCAL_REQUIRED_MODULES := privapp_whitelist_com.android.car.carlauncher
+
+LOCAL_CERTIFICATE := platform
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_OVERRIDES_PACKAGES += Launcher2 Launcher3 Launcher3QuickStep
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    androidx-constraintlayout_constraintlayout-solver
+
+LOCAL_JAVA_LIBRARIES += android.car
+
+LOCAL_STATIC_ANDROID_LIBRARIES += \
+    androidx-constraintlayout_constraintlayout \
+    androidx.lifecycle_lifecycle-extensions \
+    car-media-common
+
+# Including the resources for the static android libraries allows to pick up their static overlays.
+LOCAL_RESOURCE_DIR += \
+    $(LOCAL_PATH)/../libs/car-apps-common/res \
+    $(LOCAL_PATH)/../libs/car-media-common/res
+
+include $(BUILD_PACKAGE)
+
+endif
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 8db647e..6b3044a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,46 +16,53 @@
   -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.car.carlauncher">
-    <uses-sdk
-        android:minSdkVersion="27"
-        android:targetSdkVersion='27'/>
+          package="com.android.car.carlauncher"
+          android:sharedUserId="android.uid.system">
     <!-- System permission to get app usage data -->
-    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+    <!-- System permission to send events to hosted maps activity -->
+    <uses-permission android:name="android.permission.INJECT_EVENTS"/>
+    <!-- System permissions to bring hosted maps activity to front on main display -->
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"/>
+    <uses-permission android:name="android.permission.REORDER_TASKS"/>
+    <!-- System permission to host maps activity -->
+    <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
+    <!-- System permission to control media playback of the active session -->
+    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+    <!-- System permission to query users on device -->
+    <uses-permission android:name="android.permission.MANAGE_USERS"/>
+    <uses-permission android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
     <application
-        android:label="@string/app_title"
         android:icon="@drawable/ic_launcher_home"
-        android:theme="@style/Theme.Car.Light.NoActionBar">
-        <activity
-            android:name=".HomeActivity"
-            android:launchMode="singleInstance"
-            android:label="@string/app_title"
-            android:resizeableActivity="true">
-        </activity>
+        android:label="@string/app_title"
+        android:theme="@style/Theme.Launcher">
         <activity
             android:name=".CarLauncher"
-            android:theme="@android:style/Theme.NoTitleBar">
+            android:configChanges="uiMode|mcc|mnc"
+            android:launchMode="singleTask"
+            android:clearTaskOnLaunch="true"
+            android:stateNotNeeded="true"
+            android:resumeWhilePausing="true"
+            android:windowSoftInputMode="adjustPan"
+            android:screenOrientation="nosensor">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.HOME" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <category android:name="android.intent.category.LAUNCHER" />
-                <category android:name="android.intent.category.LAUNCHER_APP" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.HOME"/>
+                <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
         <activity
-            android:name=".AppGridActivity">
+            android:name=".AppGridActivity"
+            android:launchMode="singleInstance"
+            android:noHistory="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
         </activity>
         <activity
-            android:name=".AppSearchActivity">
+            android:name=".AppSearchActivity"
+            android:launchMode="singleInstance"
+            android:noHistory="true">
+            <meta-data android:name="distractionOptimized" android:value="true"/>
         </activity>
-        <receiver android:name=".AppGridActivity$AppInstallUninstallReceiver">
-            <intent-filter>
-                <action android:name="android.intent.action.PACKAGE_ADDED"/>
-                <action android:name="android.intent.action.PACKAGE_CHANGED"/>
-                <action android:name="android.intent.action.PACKAGE_REMOVED"/>
-                <data android:scheme="package"/>
-            </intent-filter>
-        </receiver>
     </application>
 </manifest>
diff --git a/res/drawable/app_launcher_ripple_background.xml b/res/drawable/app_launcher_ripple_background.xml
index 33a25d6..450ca8f 100644
--- a/res/drawable/app_launcher_ripple_background.xml
+++ b/res/drawable/app_launcher_ripple_background.xml
@@ -16,11 +16,11 @@
   -->
 <ripple
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/car_card_ripple_background">
+    android:color="?android:attr/colorControlHighlight">
     <item android:id="@android:id/mask">
         <shape android:shape="rectangle">
             <solid android:color="@android:color/white" />
-            <corners android:radius="@dimen/car_radius_2" />
+            <corners android:radius="@dimen/app_icon_ripple_radius" />
         </shape>
     </item>
 </ripple>
diff --git a/res/drawable/ic_partly_cloudy.png b/res/drawable/ic_partly_cloudy.png
new file mode 100644
index 0000000..7a5be64
--- /dev/null
+++ b/res/drawable/ic_partly_cloudy.png
Binary files differ
diff --git a/res/layout/app_grid_activity.xml b/res/layout/app_grid_activity.xml
index 72538a8..af50bf1 100644
--- a/res/layout/app_grid_activity.xml
+++ b/res/layout/app_grid_activity.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-    Copyright (C) 2018 The Android Open Source Project
+    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.
@@ -19,21 +19,18 @@
     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="wrap_content"
-    android:background="@color/car_card"
+    android:layout_height="match_parent"
     android:orientation="vertical">
 
     <include
         layout="@layout/app_grid_activity_header"
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginBottom="@dimen/car_padding_4" />
+        android:layout_height="wrap_content"/>
 
-    <androidx.car.widget.PagedListView
+    <com.android.car.apps.common.widget.PagedRecyclerView
         android:id="@+id/apps_grid"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        app:scrollBarTopMargin="0dp"
-        app:dayNightStyle="auto"
-        app:showPagedListViewDivider="false" />
+    />
 </LinearLayout>
+
diff --git a/res/layout/app_grid_activity_header.xml b/res/layout/app_grid_activity_header.xml
index 4d0ab07..cc38658 100644
--- a/res/layout/app_grid_activity_header.xml
+++ b/res/layout/app_grid_activity_header.xml
@@ -20,7 +20,6 @@
     android:id="@+id/header"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:background="@color/car_card"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:gravity="center_vertical"
@@ -28,61 +27,61 @@
 
     <FrameLayout
         android:id="@+id/exit_button_container"
-        android:layout_width="@dimen/car_margin"
-        android:layout_height="@dimen/car_app_bar_height"
+        android:layout_width="@dimen/panel_margin"
+        android:layout_height="@dimen/app_bar_height"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        android:background="@drawable/car_card_ripple_background"
+        android:background="?android:attr/selectableItemBackground"
         android:clickable="true"
         android:focusable="true">
         <ImageButton
-            android:layout_width="@dimen/car_primary_icon_size"
-            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_width="@dimen/icon_size"
+            android:layout_height="@dimen/icon_size"
             android:layout_gravity="center"
-            android:background="@null"
-            android:src="@drawable/ic_clear_black"
-            android:tint="@color/car_tint"
-            android:scaleType="fitXY"
             android:adjustViewBounds="true"
+            android:background="@null"
             android:clickable="false"
-            android:focusable="false" />
+            android:focusable="false"
+            android:scaleType="fitXY"
+            android:src="@drawable/ic_clear_black"
+            android:tint="@color/icon_tint"/>
     </FrameLayout>
 
     <TextView
+        style="@style/TitleText"
         android:layout_width="wrap_content"
-        android:layout_height="@dimen/car_app_bar_height"
+        android:layout_height="@dimen/app_bar_height"
         android:layout_toEndOf="@id/exit_button_container"
         android:gravity="center_vertical"
         android:text="@string/all_apps"
-        android:textAppearance="@style/TextAppearance.Car.Title2" />
+    />
 
     <FrameLayout
         android:id="@+id/search_button_container"
-        android:layout_width="@dimen/car_margin"
-        android:layout_height="@dimen/car_touch_target_size"
-        android:layout_centerVertical="true"
+        android:layout_width="@dimen/panel_margin"
+        android:layout_height="@dimen/app_grid_touch_target_size"
         android:layout_alignParentEnd="true"
-        android:background="@drawable/car_card_ripple_background"
+        android:layout_centerVertical="true"
+        android:background="?android:attr/selectableItemBackground"
         android:clickable="true"
         android:focusable="true"
-        android:visibility="gone">
+        android:visibility="gone"
+    >
         <ImageButton
-            android:layout_width="@dimen/car_primary_icon_size"
-            android:layout_height="@dimen/car_primary_icon_size"
+            android:layout_width="@dimen/icon_size"
+            android:layout_height="@dimen/icon_size"
             android:layout_gravity="center"
-            android:background="@null"
-            android:src="@drawable/ic_search_black"
-            android:tint="@color/car_tint"
-            android:scaleType="fitXY"
             android:adjustViewBounds="true"
+            android:background="@null"
             android:clickable="false"
-            android:focusable="false" />
+            android:focusable="false"
+            android:scaleType="fitXY"
+            android:src="@drawable/ic_search_black"
+            android:tint="@color/icon_tint"/>
     </FrameLayout>
 
     <View
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/car_list_divider_height"
-        android:layout_alignBottom="@id/exit_button_container"
-        android:background="@color/car_list_divider"/>
+        style="@style/HorizontalLineDivider"
+        android:layout_alignBottom="@id/exit_button_container"/>
 </RelativeLayout>
 
diff --git a/res/layout/app_item.xml b/res/layout/app_item.xml
index ddfe8ff..2e7e011 100644
--- a/res/layout/app_item.xml
+++ b/res/layout/app_item.xml
@@ -19,26 +19,26 @@
               android:id="@+id/app_item"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
-              android:padding="@dimen/car_padding_1"
-              android:layout_marginStart="@dimen/car_padding_2"
-              android:layout_marginEnd="@dimen/car_padding_2"
-              android:layout_marginBottom="@dimen/car_padding_5"
+              android:layout_marginTop="@dimen/app_grid_row_margin"
+              android:layout_marginBottom="@dimen/app_grid_row_margin"
+              android:layout_marginEnd="@dimen/app_touch_target_margin"
+              android:layout_marginStart="@dimen/app_touch_target_margin"
+              android:background="@drawable/app_launcher_ripple_background"
               android:gravity="center"
               android:orientation="vertical"
-              android:background="@drawable/app_launcher_ripple_background">
+              android:padding="@dimen/app_touch_target_padding">
 
     <ImageView
         android:id="@+id/app_icon"
-        android:layout_width="@dimen/car_touch_target_size"
-        android:layout_height="@dimen/car_touch_target_size"
-        android:layout_marginBottom="@dimen/car_padding_4"
-        android:layout_gravity="center_horizontal" />
+        android:layout_width="@dimen/app_grid_touch_target_size"
+        android:layout_height="@dimen/app_grid_touch_target_size"
+        android:layout_gravity="center_horizontal"
+        android:layout_marginBottom="@dimen/app_icon_description_margin"/>
 
     <TextView
         android:id="@+id/app_name"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.Car.Label1"
         android:ellipsize="end"
-        android:maxLines="1" />
+        android:maxLines="1"/>
 </LinearLayout>
diff --git a/res/layout/app_search_activity.xml b/res/layout/app_search_activity.xml
index 04badf9..6b17641 100644
--- a/res/layout/app_search_activity.xml
+++ b/res/layout/app_search_activity.xml
@@ -21,60 +21,57 @@
     android:id="@+id/container"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@color/search_activity_background_color"
-    android:orientation="vertical"
-    android:gravity="center_horizontal">
+    android:background="?android:attr/colorPrimary"
+    android:gravity="center_horizontal"
+    android:orientation="vertical">
 
     <RelativeLayout
         android:id="@+id/app_search_header"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/car_app_bar_height">
+        android:layout_height="@dimen/app_bar_height">
 
         <FrameLayout
             android:id="@+id/exit_button_container"
-            android:layout_width="@dimen/car_margin"
-            android:layout_height="@dimen/car_touch_target_size"
-            android:background="@drawable/car_card_ripple_background"
+            android:layout_width="@dimen/panel_margin"
+            android:layout_height="@dimen/app_grid_touch_target_size"
+            android:background="?android:attr/selectableItemBackground"
             android:clickable="true"
             android:focusable="true">
             <ImageButton
-                android:layout_width="@dimen/car_primary_icon_size"
-                android:layout_height="@dimen/car_primary_icon_size"
+                android:layout_width="@dimen/icon_size"
+                android:layout_height="@dimen/icon_size"
                 android:layout_gravity="center"
+                android:adjustViewBounds="true"
                 android:background="@null"
+                android:clickable="false"
+                android:focusable="false"
                 android:gravity="center"
                 android:src="@drawable/ic_arrow_back_black"
-                android:tint="@color/car_tint"
-                android:adjustViewBounds="true"
-                android:clickable="false"
-                android:focusable="false" />
+                android:tint="@color/icon_tint"/>
         </FrameLayout>
 
         <EditText
             android:id="@+id/app_search_bar"
             android:layout_width="@dimen/search_item_width"
             android:layout_height="@dimen/search_box_height"
-            android:layout_gravity="center_vertical"
             android:layout_centerInParent="true"
-            android:drawableStart="@drawable/ic_search_black"
+            android:layout_gravity="center_vertical"
+            android:background="?android:attr/colorPrimary"
             android:drawablePadding="@dimen/search_bar_drawable_text_padding"
-            android:drawableTint="@color/car_tint"
-            android:background="@drawable/car_card_rounded_background"
+            android:drawableStart="@drawable/ic_search_black"
+            android:drawableTint="@color/icon_tint"
             android:inputType="text|textNoSuggestions"
             android:maxLines="1"
-            android:paddingStart="@dimen/car_keyline_1"
-            android:paddingEnd="@dimen/car_keyline_1"
-            android:textAppearance="@style/TextAppearance.Car.Body1" />
+            android:paddingEnd="@dimen/search_bar_margin"
+            android:paddingStart="@dimen/search_bar_margin"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
     </RelativeLayout>
 
-    <androidx.car.widget.PagedListView
+    <androidx.recyclerview.widget.RecyclerView
         android:id="@+id/search_result"
         android:layout_width="@dimen/search_item_width"
         android:layout_height="wrap_content"
-        android:layout_marginTop="@dimen/car_padding_1"
-        android:background="@drawable/car_card_rounded_background"
-        app:scrollBarEnabled="false"
-        app:gutter="none" />
+        android:layout_marginTop="@dimen/search_result_margin"/>
 
 </LinearLayout>
 
diff --git a/res/layout/app_search_result_item.xml b/res/layout/app_search_result_item.xml
index 131db89..5fdfcc1 100644
--- a/res/layout/app_search_result_item.xml
+++ b/res/layout/app_search_result_item.xml
@@ -20,18 +20,18 @@
     android:id="@+id/app_item"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:minHeight="@dimen/car_single_line_list_item_height"
-    android:background="@color/car_card"
+    android:background="?android:attr/selectableItemBackground"
+    android:minHeight="@*android:dimen/car_single_line_list_item_height"
     android:orientation="horizontal">
 
     <ImageView
         android:id="@+id/app_icon"
-        android:layout_width="@dimen/car_primary_icon_size"
-        android:layout_height="@dimen/car_primary_icon_size"
+        android:layout_width="@dimen/icon_size"
+        android:layout_height="@dimen/icon_size"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        android:layout_marginStart="@dimen/car_keyline_1"
-        android:layout_marginEnd="@dimen/car_keyline_1" />
+        android:layout_marginStart="@dimen/search_bar_margin"
+        android:layout_marginEnd="@dimen/search_bar_margin" />
 
     <TextView
         android:id="@+id/app_name"
@@ -39,9 +39,9 @@
         android:layout_height="wrap_content"
         android:layout_alignParentStart="true"
         android:layout_centerVertical="true"
-        android:layout_marginStart="@dimen/car_keyline_3"
-        android:layout_marginEnd="@dimen/car_keyline_3"
-        android:textAppearance="@style/TextAppearance.Car.Body1"
+        android:layout_marginStart="@dimen/search_result_text_margin"
+        android:layout_marginEnd="@dimen/search_result_text_margin"
+        android:textAppearance="?android:attr/textAppearanceLarge"
         android:ellipsize="end"
         android:maxLines="1" />
 </RelativeLayout>
diff --git a/res/layout/car_launcher.xml b/res/layout/car_launcher.xml
index 583323b..4b28105 100644
--- a/res/layout/car_launcher.xml
+++ b/res/layout/car_launcher.xml
@@ -22,35 +22,90 @@
     android:layout_height="match_parent"
     tools:context=".CarLauncher">
 
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/title_text"
-        android:textSize="40sp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintLeft_toLeftOf="parent"
-        app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <ImageButton
-        android:id="@+id/appsButton"
-        android:layout_width="@dimen/button_size"
-        android:layout_height="@dimen/button_size"
-        android:layout_marginTop="32dp"
-        android:background="?android:attr/selectableItemBackground"
-        android:src="@drawable/ic_apps_black"
-        android:onClick="openAppsList"
-        app:layout_constraintEnd_toStartOf="@+id/centerLine"
-        app:layout_constraintStart_toStartOf="@+id/centerLine"
-        app:layout_constraintTop_toBottomOf="@+id/title"
-        />
-
     <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/centerLine"
+        android:id="@+id/start_edge"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        app:layout_constraintGuide_percent="0.5" />
+        app:layout_constraintGuide_begin="@dimen/horizontal_border_size"/>
 
-</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/top_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_begin="@dimen/vertical_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/end_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_end="@dimen/horizontal_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/bottom_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="@dimen/vertical_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/divider_vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_percent="@dimen/maps_screen_percentage"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/divider_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="@dimen/contextual_screen_percentage"/>
+
+    <View
+        android:id="@+id/top_line"
+        style="@style/HorizontalLineDivider"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <androidx.cardview.widget.CardView
+        style="@style/CardViewStyle"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="@dimen/main_screen_widget_margin"
+        app:layout_constraintBottom_toTopOf="@+id/bottom_edge"
+        app:layout_constraintEnd_toStartOf="@+id/divider_vertical"
+        app:layout_constraintStart_toEndOf="@+id/start_edge"
+        app:layout_constraintTop_toBottomOf="@+id/top_edge">
+        <ActivityView
+            android:id="@+id/maps"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"/>
+    </androidx.cardview.widget.CardView>
+
+    <FrameLayout
+        android:id="@+id/contextual"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="@dimen/main_screen_widget_margin"
+        app:layout_constraintBottom_toTopOf="@+id/divider_horizontal"
+        app:layout_constraintEnd_toStartOf="@+id/end_edge"
+        app:layout_constraintStart_toEndOf="@+id/divider_vertical"
+        app:layout_constraintTop_toBottomOf="@+id/top_edge"/>
+
+    <FrameLayout
+        android:id="@+id/playback"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="@dimen/main_screen_widget_margin"
+        app:layout_constraintBottom_toTopOf="@+id/bottom_edge"
+        app:layout_constraintEnd_toStartOf="@+id/end_edge"
+        app:layout_constraintStart_toEndOf="@+id/divider_vertical"
+        app:layout_constraintTop_toBottomOf="@+id/divider_horizontal"/>
+    <View
+        android:id="@+id/bottom_line"
+        style="@style/HorizontalLineDivider"
+        app:layout_constraintBottom_toBottomOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/car_launcher_multiwindow.xml b/res/layout/car_launcher_multiwindow.xml
new file mode 100644
index 0000000..04924be
--- /dev/null
+++ b/res/layout/car_launcher_multiwindow.xml
@@ -0,0 +1,89 @@
+<?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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context=".CarLauncher">
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/start_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_begin="@dimen/horizontal_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/top_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_begin="@dimen/vertical_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/end_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_end="@dimen/horizontal_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/bottom_edge"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_end="@dimen/vertical_border_size"/>
+
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/divider_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="@dimen/contextual_screen_percentage"/>
+
+    <View
+        android:id="@+id/top_line"
+        style="@style/HorizontalLineDivider"
+        app:layout_constraintTop_toTopOf="parent"/>
+
+    <FrameLayout
+        android:id="@+id/contextual"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="@dimen/main_screen_widget_margin"
+        app:layout_constraintBottom_toTopOf="@+id/divider_horizontal"
+        app:layout_constraintEnd_toStartOf="@+id/end_edge"
+        app:layout_constraintStart_toEndOf="@+id/start_edge"
+        app:layout_constraintTop_toBottomOf="@+id/top_edge"/>
+
+    <FrameLayout
+        android:id="@+id/playback"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_margin="@dimen/main_screen_widget_margin"
+        app:layout_constraintBottom_toTopOf="@+id/bottom_edge"
+        app:layout_constraintEnd_toStartOf="@+id/end_edge"
+        app:layout_constraintStart_toEndOf="@+id/start_edge"
+        app:layout_constraintTop_toBottomOf="@+id/divider_horizontal"/>
+    <View
+        android:id="@+id/bottom_line"
+        style="@style/HorizontalLineDivider"
+        app:layout_constraintBottom_toBottomOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/layout/contextual_fragment.xml b/res/layout/contextual_fragment.xml
new file mode 100644
index 0000000..3f090f5
--- /dev/null
+++ b/res/layout/contextual_fragment.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright 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.
+-->
+<androidx.cardview.widget.CardView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    style="@style/ContextualSpace"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center_vertical">
+
+        <ImageView
+            android:id="@+id/icon"
+            android:layout_width="80dp"
+            android:layout_height="80dp"
+            android:layout_marginStart="@dimen/icon_start_margin"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+        <TextView
+            android:id="@+id/top_line"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/icon_end_margin"
+            android:ellipsize="end"
+            android:maxLines="1"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            app:layout_constraintStart_toEndOf="@+id/icon"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@+id/bottom_line_container"
+            app:layout_constraintVertical_chainStyle="packed"/>
+
+        <LinearLayout
+            android:id="@+id/bottom_line_container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/icon_end_margin"
+            android:layout_marginTop="@dimen/line_gap_margin"
+            android:baselineAligned="true"
+            app:layout_constraintStart_toEndOf="@+id/icon"
+            app:layout_constraintTop_toBottomOf="@+id/top_line"
+            app:layout_constraintBottom_toBottomOf="parent">
+
+            <TextView
+                android:id="@+id/bottom_line"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:ellipsize="end"
+                android:maxLines="1"
+                android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+            <TextView
+                android:id="@+id/date_divider"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginStart="4dp"
+                android:layout_marginEnd="6dp"
+                android:text="&#x2502;"
+                android:textAppearance="?android:attr/textAppearanceLarge"
+                android:textColor="@color/date_divider_bar_color"/>
+
+            <com.android.car.carlauncher.LocalizedTextClock
+                android:id="@+id/date"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:format12Hour="@string/date"
+                android:format24Hour="@string/date"
+                android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        </LinearLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+</androidx.cardview.widget.CardView>
diff --git a/res/layout/recent_apps_row.xml b/res/layout/recent_apps_row.xml
index 3848aff..7c58de1 100644
--- a/res/layout/recent_apps_row.xml
+++ b/res/layout/recent_apps_row.xml
@@ -16,9 +16,9 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
               android:layout_width="match_parent"
-              android:layout_height="wrap_content">
+              android:layout_height="wrap_content"
+              android:orientation="vertical">
 
     <LinearLayout
         android:id="@+id/recent_apps_row"
@@ -29,8 +29,7 @@
     <View
         android:id="@+id/divider"
         android:layout_width="match_parent"
-        android:layout_height="@dimen/car_list_divider_height"
-        android:layout_marginTop="@dimen/recent_apps_divider_margin"
-        android:layout_marginBottom="@dimen/recent_apps_divider_margin"
-        android:background="@color/car_list_divider"/>
+        android:layout_height="@dimen/recent_apps_row_height"
+        android:layout_marginTop="@dimen/app_grid_row_margin"
+        android:background="@color/recent_apps_line_divider_color"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/res/values-night/colors.xml b/res/values-night/colors.xml
deleted file mode 100644
index 5019fbc..0000000
--- a/res/values-night/colors.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
-      http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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>
-    <color name="title_text_color">#FFFFFF</color>
-    <color name="list_item_bg">#000000</color>
-    <color name="list_item_text_color">#bdbdbd</color>
-    <color name="search_activity_background_color">#ff222222</color>
-</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 9519eac..b17ae79 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -14,8 +14,7 @@
  limitations under the License.
 -->
 <resources>
-    <color name="title_text_color">#FFFFFF</color>
-    <color name="list_item_bg">#bdbdbd</color>
-    <color name="list_item_text_color">#424242</color>
-    <color name="search_activity_background_color">#ffeeeeee</color>
+    <color name="date_divider_bar_color">@*android:color/car_grey_500</color>
+    <color name="icon_tint">@*android:color/car_tint</color>
+    <color name="recent_apps_line_divider_color">@*android:color/car_list_divider</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 8a3fe69..4c276a4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -15,19 +15,49 @@
     limitations under the License.
 -->
 <resources>
+    <!-- CarLauncher Activity values -->
+    <dimen name="launcher_card_corner_radius">6dp</dimen>
+    <item name="maps_screen_percentage" type="dimen" format="float">0.6</item>
+    <!-- Vertical percentage of screen (not occupied by maps to devote to the contextual space
+      (Ex: date time temp) -->
+    <item name="contextual_screen_percentage" type="dimen" format="float">.3</item>
+     <!--Used for the space between the top, and bottom of the screen and the content -->
+    <dimen name="vertical_border_size">@*android:dimen/car_padding_1</dimen>
+    <!-- Used for the space between the start, and end of the screen and the content -->
+    <dimen name="horizontal_border_size">@*android:dimen/car_padding_1</dimen>
+    <!-- Margin surrounding fragments on the main activity -->
+    <dimen name="main_screen_widget_margin">@*android:dimen/car_padding_1</dimen>
+    <!-- Margin between the edge of the card and start of the icon -->
+    <dimen name="icon_start_margin">@*android:dimen/car_padding_4</dimen>
+    <!-- Gap between the end of the icon and the start of the text -->
+    <dimen name="icon_end_margin">@*android:dimen/car_padding_3</dimen>
+    <!-- Gap between text lines -->
+    <dimen name="line_gap_margin">@*android:dimen/car_padding_1</dimen>
+
+    <!-- AppGridActivity -->
     <dimen name="recent_apps_divider_margin">40dp</dimen>
+    <item name="app_icon_opacity_unavailable" type="dimen" format="float">0.5</item>
+    <item name="app_icon_opacity" type="dimen" format="float">1.0</item>
+    <!-- Gap between top of screen (statusbar) and header content -->
+    <dimen name="app_grid_header_margin">@*android:dimen/car_padding_4</dimen>
+    <dimen name="panel_margin">@*android:dimen/car_margin</dimen>
+    <!-- Height of the text and exit button on the app selection screen -->
+    <dimen name="app_bar_height">@*android:dimen/car_app_bar_height</dimen>
+    <dimen name="icon_size">@*android:dimen/car_primary_icon_size</dimen>
+    <dimen name="recent_apps_row_height">@*android:dimen/car_list_divider_height</dimen>
+    <dimen name="app_grid_touch_target_size">@*android:dimen/car_touch_target_size</dimen>
+    <!-- Padding around the touch target (makes ripple look better) -->
+    <dimen name="app_touch_target_padding">@*android:dimen/car_padding_1</dimen>
+    <dimen name="app_touch_target_margin">@*android:dimen/car_padding_2</dimen>
+    <dimen name="app_grid_row_margin">@*android:dimen/car_padding_4</dimen>
+    <dimen name="app_icon_description_margin">@*android:dimen/car_padding_4</dimen>
+    <dimen name="app_icon_ripple_radius">@*android:dimen/car_radius_2</dimen>
 
+    <!-- AppSearchActivity -->
     <dimen name="search_item_width">760dp</dimen>
+    <dimen name="search_result_margin">@*android:dimen/car_padding_1</dimen>
+    <dimen name="search_result_text_margin">@*android:dimen/car_keyline_3</dimen>
     <dimen name="search_box_height">65dp</dimen>
+    <dimen name="search_bar_margin">@*android:dimen/car_keyline_1</dimen>
     <dimen name="search_bar_drawable_text_padding">46dp</dimen>
-
-    <dimen name="tile_text_size">56sp</dimen>
-    <dimen name="title_text_margin_bottom">96dp</dimen>
-    <dimen name="button_size">96dp</dimen>
-
-    <dimen name="list_item_text_size">32sp</dimen>
-    <dimen name="list_item_icon_size">96dp</dimen>
-    <dimen name="list_item_vertical_padding">32dp</dimen>
-    <dimen name="list_item_marginStart">24dp</dimen>
-    <dimen name="list_item_text_margin_start">105dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ab544c6..57e5376 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -14,8 +14,59 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<resources>
-    <string name="title_text">Let\'s Drive</string>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_title">Car Launcher</string>
     <string name="all_apps">All apps</string>
-</resources>
\ No newline at end of file
+
+    <string name="driving_toast_text">
+        <xliff:g id="app_name" example="Settings">%1$s</xliff:g> can\'t be used while driving.
+    </string>
+
+    <!-- Contextual Fragment -->
+    <string name="greeting">Hi,
+        <xliff:g example="Owner" id="user">%s</xliff:g>
+    </string>
+    <string name="temperature" translatable="false">%d\u00B0</string>
+    <string name="temperature_empty" translatable="false">--\u00B0</string>
+    <string name="date" translatable="false">EEEMMMd</string>
+
+    <plurals name="projection_devices">
+        <item quantity="one"><xliff:g example="1" id="count">%d</xliff:g> device</item>
+        <item quantity="other"><xliff:g example="2" id="count">%d</xliff:g> devices</item>
+    </plurals>
+
+    <!--
+        A list of package names to exclude from the app selector when filtering is active.
+        DO NOT TRANSLATE.
+    -->
+    <string-array name="hidden_apps" translatable="false">
+        <item>com.android.car.themeplayground</item>
+        <item>com.google.android.auto.welcome</item>
+        <item>com.android.car.acast.source</item>
+        <item>com.google.android.embedded.projection</item>
+        <item>com.google.android.car.bugreport</item>
+        <item>com.android.car.trust</item>
+        <item>com.android.car.datacenter</item>
+        <item>com.google.android.car.diagnosticrecorder</item>
+        <item>com.google.android.car.diskwriteapp</item>
+        <item>com.android.documentsui</item>
+        <item>com.google.android.car.flashapp</item>
+        <item>com.android.gallery3d</item>
+        <item>com.google.android.car.garagemode.testapp</item>
+        <item>com.google.android.gms.vehicle.testapp</item>
+        <item>com.google.android.car.kitchensink</item>
+        <item>com.android.support.car.lenspicker</item>
+        <item>com.google.android.apps.gmm</item>
+        <item>com.android.car.media</item>
+        <item>com.qualcomm.qti.sensors.qsensortest</item>
+        <item>com.qualcomm.qti.logkit</item>
+        <item>com.qualcomm.qti.roamingsettings</item>
+        <item>com.android.settings</item>
+        <item>com.google.android.car.uxr.sample</item>
+        <item>com.google.android.apps.geo.autograph.vms.client.visualizer.car</item>
+        <item>com.google.android.apps.geo.autograph.vms.client.systemstate</item>
+        <item>com.google.android.car.vms.subscriber</item>
+        <item>org.chromium.webview_shell</item>
+    </string-array>
+
+</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
new file mode 100644
index 0000000..c576b1c
--- /dev/null
+++ b/res/values/styles.xml
@@ -0,0 +1,37 @@
+<?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>
+    <style name="CardViewStyle">
+        <item name="cardCornerRadius">@dimen/launcher_card_corner_radius</item>
+        <item name="cardElevation">0dp</item>
+    </style>
+
+    <style name="ContextualSpace" parent="CardViewStyle">
+        <item name="cardBackgroundColor">?android:attr/colorPrimary</item>
+    </style>
+
+    <style name="TitleText">
+        <item name="android:textAppearance">?android:attr/textAppearanceLarge</item>
+        <item name="android:fontFamily">sans-serif-medium</item>
+    </style>
+
+    <style name="HorizontalLineDivider">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">@*android:dimen/car_list_divider_height</item>
+        <item name="android:background">@*android:color/car_list_divider</item>
+    </style>
+</resources>
diff --git a/res/values/themes.xml b/res/values/themes.xml
new file mode 100644
index 0000000..5422778
--- /dev/null
+++ b/res/values/themes.xml
@@ -0,0 +1,24 @@
+<?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>
+    <!--Theme for the app, it's defined empty here so it can be overlaid easily -->
+    <style name="Theme.Launcher" parent="android:Theme.DeviceDefault.Wallpaper.NoTitleBar">
+        <item name="textAppearanceGridItem">@android:style/TextAppearance.DeviceDefault.Medium</item>
+        <item name="textAppearanceGridItemSecondary">@android:style/TextAppearance.DeviceDefault.Small</item>
+    </style>
+</resources>
diff --git a/src/com/android/car/carlauncher/AppGridActivity.java b/src/com/android/car/carlauncher/AppGridActivity.java
index 62d0699..a4a8bed 100644
--- a/src/com/android/car/carlauncher/AppGridActivity.java
+++ b/src/com/android/car/carlauncher/AppGridActivity.java
@@ -30,10 +30,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.ActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.text.TextUtils;
@@ -44,12 +42,16 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
 
+import androidx.recyclerview.widget.RecyclerView;
+import com.android.car.carlauncher.AppLauncherUtils.LauncherAppsInfo;
+
 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 androidx.car.widget.PagedListView;
+import java.util.Set;
 
 /**
  * Launcher activity that shows a grid of apps.
@@ -59,6 +61,8 @@
     private static final String TAG = "AppGridActivity";
 
     private int mColumnNumber;
+    private boolean mShowAllApps = true;
+    private final Set<String> mHiddenApps = new HashSet<>();
     private AppGridAdapter mGridAdapter;
     private PackageManager mPackageManager;
     private UsageStatsManager mUsageStatsManager;
@@ -83,9 +87,7 @@
                                         restrictionInfo.isRequiresDistractionOptimization()));
 
                 mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
-                mGridAdapter.setAllApps(getAllApps());
-                mGridAdapter.setMostRecentApps(getMostRecentApps());
-
+                updateAppsLists();
             } catch (CarNotConnectedException e) {
                 Log.e(TAG, "Car not connected in CarConnectionListener", e);
             }
@@ -105,10 +107,20 @@
         mPackageManager = getPackageManager();
         mUsageStatsManager = (UsageStatsManager) getSystemService(Context.USAGE_STATS_SERVICE);
         mCar = Car.createCar(this, mCarConnectionListener);
+        mHiddenApps.addAll(Arrays.asList(getResources().getStringArray(R.array.hidden_apps)));
 
         setContentView(R.layout.app_grid_activity);
 
-        findViewById(R.id.exit_button_container).setOnClickListener(v -> finish());
+        View exitView = findViewById(R.id.exit_button_container);
+        exitView.setOnClickListener(v -> finish());
+        exitView.setOnLongClickListener(new View.OnLongClickListener() {
+            @Override
+            public boolean onLongClick(View v) {
+                mShowAllApps = !mShowAllApps;
+                updateAppsLists();
+                return true;
+            }
+        });
 
         findViewById(R.id.search_button_container).setOnClickListener((View view) -> {
             Intent intent = new Intent(this, AppSearchActivity.class);
@@ -116,7 +128,7 @@
         });
 
         mGridAdapter = new AppGridAdapter(this);
-        PagedListView gridView = findViewById(R.id.apps_grid);
+        RecyclerView gridView = findViewById(R.id.apps_grid);
 
         GridLayoutManager gridLayoutManager = new GridLayoutManager(this, mColumnNumber);
         gridLayoutManager.setSpanSizeLookup(new SpanSizeLookup() {
@@ -125,7 +137,7 @@
                 return mGridAdapter.getSpanSizeLookup(position);
             }
         });
-        gridView.getRecyclerView().setLayoutManager(gridLayoutManager);
+        gridView.setLayoutManager(gridLayoutManager);
 
         gridView.setAdapter(mGridAdapter);
     }
@@ -133,10 +145,18 @@
     @Override
     protected void onResume() {
         super.onResume();
-        mGridAdapter.setAllApps(getAllApps());
-        // using onResume() to refresh most recently used apps because we want to refresh even if
+        // Using onResume() to refresh most recently used apps because we want to refresh even if
         // the app being launched crashes/doesn't cover the entire screen.
-        mGridAdapter.setMostRecentApps(getMostRecentApps());
+        updateAppsLists();
+    }
+
+    /** Updates the list of all apps, and the list of the most recently used ones. */
+    private void updateAppsLists() {
+        Set<String> blackList = mShowAllApps ? Collections.emptySet() : mHiddenApps;
+        LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(blackList,
+                getSystemService(LauncherApps.class), mCarPackageManager, mPackageManager);
+        mGridAdapter.setAllApps(appsInfo.getApplicationsList());
+        mGridAdapter.setMostRecentApps(getMostRecentApps(appsInfo));
     }
 
     @Override
@@ -177,8 +197,15 @@
         }
     }
 
-    private List<AppMetaData> getMostRecentApps() {
+    /**
+     * 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:
@@ -195,14 +222,15 @@
             return apps; // empty list
         }
 
-        Collections.sort(stats, new LastTimeUsedComparator());
+        stats.sort(new LastTimeUsedComparator());
 
         int currentIndex = 0;
         int itemsAdded = 0;
         int statsSize = stats.size();
         int itemCount = Math.min(mColumnNumber, statsSize);
         while (itemsAdded < itemCount && currentIndex < statsSize) {
-            String packageName = stats.get(currentIndex).getPackageName();
+            UsageStats usageStats = stats.get(currentIndex);
+            String packageName = usageStats.mPackageName;
             currentIndex++;
 
             // do not include self
@@ -210,36 +238,26 @@
                 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;
+            // Exempt media services from background and launcher checks
+            if (!appsInfo.isMediaService(packageName)) {
+                // 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;
+                }
             }
 
-            try {
-                // try getting activity info from package name
-                Drawable icon = mPackageManager.getActivityIcon(intent);
-                ActivityInfo info = mPackageManager.getActivityInfo(intent.getComponent(), 0);
-                CharSequence displayName = info.loadLabel(mPackageManager);
-                if (icon == null || TextUtils.isEmpty(displayName)) {
-                    continue;
-                }
-                boolean isDistractionOptimized = AppLauncherUtils.isActivityDistractionOptimized(
-                        mCarPackageManager, packageName, intent.getComponent().getClassName());
-                AppMetaData app =
-                        new AppMetaData(displayName, packageName, icon, isDistractionOptimized);
-
-                // edge case: do not include duplicated entries
-                // e.g. app is used at 2017/12/31 23:59, and 2018/01/01 00:00
-                if (apps.contains(app)) {
-                    continue;
-                }
-
+            AppMetaData app = appsInfo.getAppMetaData(packageName);
+            // 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++;
-            } catch (PackageManager.NameNotFoundException e) {
-                // this should never happen
-                Log.e(TAG, "NameNotFoundException when getting app icon in AppGridActivity", e);
             }
         }
         return apps;
@@ -258,11 +276,6 @@
         }
     }
 
-    private List<AppMetaData> getAllApps() {
-        return AppLauncherUtils.getAllLaunchableApps(
-                getSystemService(LauncherApps.class), mCarPackageManager, mPackageManager);
-    }
-
     private class AppInstallUninstallReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -273,7 +286,7 @@
                 return;
             }
 
-            mGridAdapter.setAllApps(getAllApps());
+            updateAppsLists();
         }
     }
 }
diff --git a/src/com/android/car/carlauncher/AppGridAdapter.java b/src/com/android/car/carlauncher/AppGridAdapter.java
index 7b1be64..89cf10a 100644
--- a/src/com/android/car/carlauncher/AppGridAdapter.java
+++ b/src/com/android/car/carlauncher/AppGridAdapter.java
@@ -25,7 +25,6 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -61,7 +60,7 @@
         notifyDataSetChanged();
     }
 
-    void setAllApps(List<AppMetaData> apps) {
+    void setAllApps(@Nullable List<AppMetaData> apps) {
         mApps = apps;
         sortAllApps();
         notifyDataSetChanged();
diff --git a/src/com/android/car/carlauncher/AppItemViewHolder.java b/src/com/android/car/carlauncher/AppItemViewHolder.java
index 3007092..ff3b651 100644
--- a/src/com/android/car/carlauncher/AppItemViewHolder.java
+++ b/src/com/android/car/carlauncher/AppItemViewHolder.java
@@ -18,9 +18,11 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.Intent;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -29,11 +31,11 @@
  */
 public class AppItemViewHolder extends RecyclerView.ViewHolder {
     private final Context mContext;
-    public View mAppItem;
-    public ImageView mAppIconView;
-    public TextView mAppNameView;
+    private View mAppItem;
+    private ImageView mAppIconView;
+    private TextView mAppNameView;
 
-    public AppItemViewHolder(View view, Context context) {
+    AppItemViewHolder(View view, Context context) {
         super(view);
         mContext = context;
         mAppItem = view.findViewById(R.id.app_item);
@@ -48,7 +50,6 @@
      */
     public void bind(@Nullable AppMetaData app, boolean isDistractionOptimizationRequired) {
         // Empty out the view
-        mAppItem.setClickable(false);
         mAppIconView.setImageDrawable(null);
         mAppNameView.setText(null);
 
@@ -57,11 +58,30 @@
         }
 
         mAppNameView.setText(app.getDisplayName());
-        if (isDistractionOptimizationRequired && !app.getIsDistractionOptimized()) {
-            mAppIconView.setImageDrawable(AppLauncherUtils.toGrayscale(app.getIcon()));
-        } else {
-            mAppIconView.setImageDrawable(app.getIcon());
+        mAppIconView.setImageDrawable(app.getIcon());
+        boolean isLaunchable =
+                !isDistractionOptimizationRequired || app.getIsDistractionOptimized();
+        mAppIconView.setAlpha(mContext.getResources().getFloat(
+                isLaunchable ? R.dimen.app_icon_opacity : R.dimen.app_icon_opacity_unavailable));
+
+        if (isLaunchable) {
             mAppItem.setOnClickListener(v -> AppLauncherUtils.launchApp(mContext, app));
+            mAppItem.setLongClickable(app.getAlternateLaunchIntent() != null);
+            mAppItem.setOnLongClickListener(v-> openAlternateLaunchIntent(app));
+        } else {
+            String warningText = mContext.getResources()
+                    .getString(R.string.driving_toast_text, app.getDisplayName());
+            mAppItem.setOnClickListener(
+                    v -> Toast.makeText(mContext, warningText, Toast.LENGTH_LONG).show());
         }
     }
+
+    private boolean openAlternateLaunchIntent(AppMetaData app) {
+        Intent intent = app.getAlternateLaunchIntent();
+        if (intent != null) {
+            mContext.startActivity(intent);
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/src/com/android/car/carlauncher/AppLauncherUtils.java b/src/com/android/car/carlauncher/AppLauncherUtils.java
index 30e4ae6..dbeff4d 100644
--- a/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -17,6 +17,8 @@
 package com.android.car.carlauncher;
 
 import android.annotation.Nullable;
+import android.app.ActivityOptions;
+import android.car.Car;
 import android.car.CarNotConnectedException;
 import android.car.content.pm.CarPackageManager;
 import android.content.Context;
@@ -24,15 +26,20 @@
 import android.content.pm.LauncherActivityInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.Drawable;
+import android.content.pm.ResolveInfo;
 import android.os.Process;
+import android.service.media.MediaBrowserService;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Util class that contains helper method used by app launcher classes.
@@ -57,65 +64,128 @@
      * @param app the requesting app's AppMetaData
      */
     static void launchApp(Context context, AppMetaData app) {
-        Intent intent =
-                context.getPackageManager().getLaunchIntentForPackage(app.getPackageName());
-        context.startActivity(intent);
+        ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(context.getDisplayId());
+        context.startActivity(app.getMainLaunchIntent(), options.toBundle());
     }
 
-    /**
-     * Converts a {@link Drawable} to grey scale.
-     *
-     * @param drawable the original drawable
-     * @return the grey scale drawable
-     */
-    static Drawable toGrayscale(Drawable drawable) {
-        ColorMatrix matrix = new ColorMatrix();
-        matrix.setSaturation(0);
-        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
-        // deep copy the original drawable
-        Drawable newDrawable = drawable.getConstantState().newDrawable().mutate();
-        newDrawable.setColorFilter(filter);
-        return newDrawable;
+    /** Bundles application and services info. */
+    static class LauncherAppsInfo {
+        /** Map of all apps' metadata keyed by package name. */
+        private final Map<String, AppMetaData> mApplications;
+
+        /** Map of all the media services keyed by package name. */
+        private final Map<String, ResolveInfo> mMediaServices;
+
+        LauncherAppsInfo(@NonNull Map<String, AppMetaData> apps,
+                @NonNull Map<String, ResolveInfo> mediaServices) {
+            mApplications = apps;
+            mMediaServices = mediaServices;
+        }
+
+        /** Returns true if all maps are empty. */
+        boolean isEmpty() {
+            return mApplications.isEmpty() && mMediaServices.isEmpty();
+        }
+
+        /** Returns whether the given package name is a media service. */
+        boolean isMediaService(String packageName) {
+            return mMediaServices.containsKey(packageName);
+        }
+
+        /** Returns the {@link AppMetaData} for the given package name. */
+        @Nullable
+        AppMetaData getAppMetaData(String packageName) {
+            return mApplications.get(packageName);
+        }
+
+        /** Returns a new list of the applications' {@link AppMetaData}. */
+        @NonNull
+        List<AppMetaData> getApplicationsList() {
+            return new ArrayList<>(mApplications.values());
+        }
     }
 
+    private final static LauncherAppsInfo EMPTY_APPS_INFO = new LauncherAppsInfo(
+            Collections.emptyMap(), Collections.emptyMap());
+
     /**
-     * Gets all apps that support launching from launcher in unsorted order.
+     * Gets all the apps that we want to see in the launcher in unsorted order. Includes media
+     * services without launcher activities.
      *
+     * @param blackList         A (possibly empty) list of apps to hide
      * @param launcherApps      The {@link LauncherApps} system service
      * @param carPackageManager The {@link CarPackageManager} system service
      * @param packageManager    The {@link PackageManager} system service
-     * @return a list of all apps' metadata
+     * @return a new {@link LauncherAppsInfo}
      */
-    @Nullable
-    static List<AppMetaData> getAllLaunchableApps(
+    @NonNull
+    static LauncherAppsInfo getAllLauncherApps(
+            @NonNull Set<String> blackList,
             LauncherApps launcherApps,
             CarPackageManager carPackageManager,
             PackageManager packageManager) {
 
         if (launcherApps == null || carPackageManager == null || packageManager == null) {
-            return null;
+            return EMPTY_APPS_INFO;
         }
 
-        List<AppMetaData> apps = new ArrayList<>();
-
-        Intent intent = new Intent(Intent.ACTION_MAIN, null);
-        intent.addCategory(Intent.CATEGORY_LAUNCHER);
-
+        List<ResolveInfo> mediaServices = packageManager.queryIntentServices(
+                new Intent(MediaBrowserService.SERVICE_INTERFACE),
+                PackageManager.GET_RESOLVED_FILTER);
         List<LauncherActivityInfo> availableActivities =
                 launcherApps.getActivityList(null, Process.myUserHandle());
+
+        Map<String, AppMetaData> apps = new HashMap<>(
+                mediaServices.size() + availableActivities.size());
+        Map<String, ResolveInfo> mediaServicesMap = new HashMap<>(mediaServices.size());
+
+        // Process media services
+        for (ResolveInfo info : mediaServices) {
+            String packageName = info.serviceInfo.packageName;
+            mediaServicesMap.put(packageName, info);
+            if (shouldAdd(packageName, apps, blackList)) {
+                final boolean isDistractionOptimized = true;
+
+                Intent intent = new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
+                intent.putExtra(Car.CAR_EXTRA_MEDIA_PACKAGE, packageName);
+
+                AppMetaData appMetaData = new AppMetaData(
+                        info.serviceInfo.loadLabel(packageManager),
+                        packageName,
+                        info.serviceInfo.loadIcon(packageManager),
+                        isDistractionOptimized,
+                        intent,
+                        packageManager.getLaunchIntentForPackage(packageName));
+                apps.put(packageName, appMetaData);
+            }
+        }
+
+        // Process activities
         for (LauncherActivityInfo info : availableActivities) {
             String packageName = info.getComponentName().getPackageName();
-            boolean isDistractionOptimized =
-                    isActivityDistractionOptimized(carPackageManager, packageName, info.getName());
+            if (shouldAdd(packageName, apps, blackList)) {
+                boolean isDistractionOptimized =
+                        isActivityDistractionOptimized(carPackageManager, packageName,
+                                info.getName());
 
-            AppMetaData app = new AppMetaData(
-                    info.getLabel(),
-                    packageName,
-                    info.getApplicationInfo().loadIcon(packageManager),
-                    isDistractionOptimized);
-            apps.add(app);
+                AppMetaData appMetaData = new AppMetaData(
+                        info.getLabel(),
+                        packageName,
+                        info.getBadgedIcon(0),
+                        isDistractionOptimized,
+                        packageManager.getLaunchIntentForPackage(packageName),
+                        null);
+                apps.put(packageName, appMetaData);
+            }
         }
-        return apps;
+
+        return new LauncherAppsInfo(apps, mediaServicesMap);
+    }
+
+    private static boolean shouldAdd(String packageName, Map<String, AppMetaData> apps,
+            @NonNull Set<String> blackList) {
+        return !apps.containsKey(packageName) && !blackList.contains(packageName);
     }
 
     /**
diff --git a/src/com/android/car/carlauncher/AppMetaData.java b/src/com/android/car/carlauncher/AppMetaData.java
index ac8a744..affc472 100644
--- a/src/com/android/car/carlauncher/AppMetaData.java
+++ b/src/com/android/car/carlauncher/AppMetaData.java
@@ -17,30 +17,48 @@
 package com.android.car.carlauncher;
 
 import android.annotation.Nullable;
+import android.content.Intent;
 import android.graphics.drawable.Drawable;
 
 /**
- * Meta data of an app including the display name, the full package name, and the icon drawable.
+ * Meta data of an app including the display name, the full package name, the icon drawable, and an
+ * intent to either open the app or the media center (for media services).
  */
 
 final class AppMetaData {
     // The display name of the app
     @Nullable
-    private String mDisplayName;
+    private final String mDisplayName;
     // The package name of the app
-    private String mPackageName;
-    private Drawable mIcon;
-    private boolean mIsDistractionOptimized;
+    private final String mPackageName;
+    private final Drawable mIcon;
+    private final boolean mIsDistractionOptimized;
+    private final Intent mMainLaunchIntent;
+    private final Intent mAlternateLaunchIntent;
 
-    public AppMetaData(
+    /**
+     * AppMetaData
+     * @param displayName the name to display in the launcher
+     * @param packageName the application's package
+     * @param icon the application's icon
+     * @param isDistractionOptimized whether mainLaunchIntent is safe for driving
+     * @param mainLaunchIntent what to open by default (goes to the media center for media apps)
+     * @param alternateLaunchIntent temporary allowance for media apps that still need to show UI
+     *                              beyond sign in and settings
+     */
+    AppMetaData(
             CharSequence displayName,
             String packageName,
             Drawable icon,
-            boolean isDistractionOptimized) {
+            boolean isDistractionOptimized,
+            Intent mainLaunchIntent,
+            @Nullable Intent alternateLaunchIntent) {
         mDisplayName = displayName == null ? "" : displayName.toString();
         mPackageName = packageName == null ? "" : packageName;
         mIcon = icon;
         mIsDistractionOptimized = isDistractionOptimized;
+        mMainLaunchIntent = mainLaunchIntent;
+        mAlternateLaunchIntent = alternateLaunchIntent;
     }
 
     public String getDisplayName() {
@@ -51,11 +69,19 @@
         return mPackageName;
     }
 
+    Intent getMainLaunchIntent() {
+        return mMainLaunchIntent;
+    }
+
+    Intent getAlternateLaunchIntent() {
+        return mAlternateLaunchIntent;
+    }
+
     public Drawable getIcon() {
         return mIcon;
     }
 
-    public boolean getIsDistractionOptimized() {
+    boolean getIsDistractionOptimized() {
         return mIsDistractionOptimized;
     }
 
diff --git a/src/com/android/car/carlauncher/AppSearchActivity.java b/src/com/android/car/carlauncher/AppSearchActivity.java
index 77c1577..59315db 100644
--- a/src/com/android/car/carlauncher/AppSearchActivity.java
+++ b/src/com/android/car/carlauncher/AppSearchActivity.java
@@ -39,10 +39,10 @@
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
 
+import androidx.recyclerview.widget.RecyclerView;
+import java.util.Collections;
 import java.util.List;
 
-import androidx.car.widget.PagedListView;
-
 /**
  * Activity that allows user to search in apps.
  */
@@ -100,7 +100,7 @@
                 });
         findViewById(R.id.exit_button_container).setOnClickListener(view -> finish());
 
-        PagedListView searchResultView = findViewById(R.id.search_result);
+        RecyclerView searchResultView = findViewById(R.id.search_result);
         searchResultView.setClipToOutline(true);
         mSearchResultAdapter = new SearchResultAdapter(this);
         searchResultView.setAdapter(mSearchResultAdapter);
@@ -167,8 +167,10 @@
     }
 
     private List<AppMetaData> getAllApps() {
-        return AppLauncherUtils.getAllLaunchableApps(
-                getSystemService(LauncherApps.class), mCarPackageManager, mPackageManager);
+        AppLauncherUtils.LauncherAppsInfo appsInfo = AppLauncherUtils.getAllLauncherApps(
+                Collections.emptySet(), getSystemService(LauncherApps.class), mCarPackageManager,
+                mPackageManager);
+        return appsInfo.getApplicationsList();
     }
 
     public void hideKeyboard() {
diff --git a/src/com/android/car/carlauncher/CarLauncher.java b/src/com/android/car/carlauncher/CarLauncher.java
index 4eac517..3fa0cc4 100644
--- a/src/com/android/car/carlauncher/CarLauncher.java
+++ b/src/com/android/car/carlauncher/CarLauncher.java
@@ -1,23 +1,188 @@
+/*
+ * 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.
+ */
+
 package com.android.car.carlauncher;
 
-import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityView;
+import android.app.UserSwitchObserver;
 import android.content.Intent;
+import android.content.res.Configuration;
 import android.os.Bundle;
-import android.view.View;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.widget.FrameLayout;
+
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentTransaction;
+
+import com.android.car.media.common.PlaybackFragment;
+
+import java.util.Set;
 
 /**
- *  Basic Launcher for Android Automotive
+ * Basic Launcher for Android Automotive which demonstrates the use of {@link ActivityView} to host
+ * maps content.
+ *
+ * <p>Note: On some devices, the ActivityView may render with a width, height, and/or aspect
+ * ratio that does not meet Android compatibility definitions. Developers should work with content
+ * owners to ensure content renders correctly when extending or emulating this class.
+ *
+ * <p>Note: Since the hosted maps Activity in ActivityView is currently in a virtual display, the
+ * system considers the Activity to always be in front. Launching the maps Activity with a direct
+ * Intent will not work. To start the maps Activity on the real display, send the Intent to the
+ * Launcher with the {@link Intent#CATEGORY_APP_MAPS} category, and the launcher will start the
+ * Activity on the real display.
+ *
+ * <p>Note: The state of the virtual display in the ActivityView is nondeterministic when
+ * switching away from and back to the current user. To avoid a crash, this Activity will finish
+ * when switching users.
  */
-public class CarLauncher extends Activity {
+public class CarLauncher extends FragmentActivity {
+    private static final String TAG = "CarLauncher";
 
-   @Override
+    private ActivityView mActivityView;
+    private boolean mActivityViewReady = false;
+    private boolean mIsStarted = false;
+
+    private final ActivityView.StateCallback mActivityViewCallback =
+            new ActivityView.StateCallback() {
+                @Override
+                public void onActivityViewReady(ActivityView view) {
+                    mActivityViewReady = true;
+                    startMapsInActivityView();
+                }
+
+                @Override
+                public void onActivityViewDestroyed(ActivityView view) {
+                    mActivityViewReady = false;
+                }
+
+                @Override
+                public void onTaskMovedToFront(int taskId) {
+                    try {
+                        if (mIsStarted) {
+                            ActivityManager am =
+                                    (ActivityManager) getSystemService(ACTIVITY_SERVICE);
+                            am.moveTaskToFront(CarLauncher.this.getTaskId(), /* flags= */ 0);
+                        }
+                    } catch (RuntimeException e) {
+                        Log.w(TAG, "Failed to move CarLauncher to front.");
+                    }
+                }
+            };
+
+    @Override
     protected void onCreate(Bundle savedInstanceState) {
-       super.onCreate(savedInstanceState);
-       setContentView(R.layout.car_launcher);
-   }
+        super.onCreate(savedInstanceState);
+        // 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
+        // activity of the launcher
+        if (isInMultiWindowMode() || isInPictureInPictureMode()) {
+            setContentView(R.layout.car_launcher_multiwindow);
+        } else {
+            setContentView(R.layout.car_launcher);
+        }
+        initializeFragments();
+        mActivityView = findViewById(R.id.maps);
+        if (mActivityView != null) {
+            mActivityView.setCallback(mActivityViewCallback);
+        }
+    }
 
-   // called by onClick in xml
-    public void openAppsList(View v){
-        startActivity(new Intent(this, AppGridActivity.class));
+    @Override
+    protected void onNewIntent(Intent intent) {
+        Set<String> categories = intent.getCategories();
+        if (categories != null && categories.size() == 1 && categories.contains(
+                Intent.CATEGORY_APP_MAPS)) {
+            launchMapsActivity();
+        }
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        startMapsInActivityView();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mIsStarted = true;
+    }
+
+    @Override
+    protected void onStop() {
+        super.onStop();
+        mIsStarted = false;
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        if (mActivityView != null && mActivityViewReady) {
+            mActivityView.release();
+        }
+    }
+
+    private void startMapsInActivityView() {
+        // If we happen to be be resurfaced into a multi display mode we skip launching content
+        // in the activity view as we will get recreated anyway.
+        if (!mActivityViewReady || isInMultiWindowMode() || isInPictureInPictureMode()) {
+            return;
+        }
+        if (mActivityView != null) {
+            mActivityView.startActivity(getMapsIntent());
+        }
+    }
+
+    private void launchMapsActivity() {
+        // Make sure the Activity launches on the current display instead of in the ActivityView
+        // virtual display.
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchDisplayId(getDisplay().getDisplayId());
+        startActivity(getMapsIntent(), options.toBundle());
+    }
+
+    private Intent getMapsIntent() {
+        return Intent.makeMainSelectorActivity(Intent.ACTION_MAIN, Intent.CATEGORY_APP_MAPS);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        initializeFragments();
+    }
+
+    private void initializeFragments() {
+        PlaybackFragment playbackFragment = new PlaybackFragment();
+        ContextualFragment contextualFragment = null;
+        FrameLayout contextual = findViewById(R.id.contextual);
+        if(contextual != null) {
+            contextualFragment = new ContextualFragment();
+        }
+
+        FragmentTransaction fragmentTransaction =
+                getSupportFragmentManager().beginTransaction();
+        fragmentTransaction.replace(R.id.playback, playbackFragment);
+        if(contextual != null) {
+            fragmentTransaction.replace(R.id.contextual, contextualFragment);
+        }
+        fragmentTransaction.commitNow();
     }
 }
diff --git a/src/com/android/car/carlauncher/ContextualFragment.java b/src/com/android/car/carlauncher/ContextualFragment.java
new file mode 100644
index 0000000..522a362
--- /dev/null
+++ b/src/com/android/car/carlauncher/ContextualFragment.java
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package com.android.car.carlauncher;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.ViewModelProviders;
+
+/** {@link Fragment} which displays relevant information that changes over time. */
+public class ContextualFragment extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View rootView = inflater.inflate(R.layout.contextual_fragment, container, false);
+        ImageView iconView = rootView.findViewById(R.id.icon);
+        TextView topLineView = rootView.findViewById(R.id.top_line);
+        TextView bottomLineView = rootView.findViewById(R.id.bottom_line);
+        View dateDividerView = rootView.findViewById(R.id.date_divider);
+        View dateView = rootView.findViewById(R.id.date);
+        View bottomLineContainerView = rootView.findViewById(R.id.bottom_line_container);
+
+        ContextualViewModel viewModel = ViewModelProviders.of(this).get(ContextualViewModel.class);
+
+        viewModel.getContextualInfo().observe(this, info -> {
+            if (info == null) {
+                return;
+            }
+
+            iconView.setImageDrawable(info.getIcon());
+            topLineView.setText(info.getTopLine());
+
+            boolean showBottomLineMessage = (info.getBottomLine() != null);
+
+            bottomLineView.setVisibility(showBottomLineMessage ? View.VISIBLE : View.GONE);
+            bottomLineView.setText(info.getBottomLine());
+
+            dateView.setVisibility(info.getShowClock() ? View.VISIBLE : View.GONE);
+
+            // If both the bottom-line message and the clock are shown, show the divider.
+            dateDividerView.setVisibility(
+                    (showBottomLineMessage && info.getShowClock()) ? View.VISIBLE : View.GONE);
+            // Hide the bottom-line container if neither the bottom-line message nor the clock
+            // is being shown. This will center the top-line message in the card.
+            bottomLineContainerView.setVisibility(
+                    (showBottomLineMessage || info.getShowClock()) ? View.VISIBLE : View.GONE);
+
+            Intent onClickActivity = info.getOnClickActivity();
+            View.OnClickListener listener =
+                    onClickActivity != null
+                            ? v -> startActivity(info.getOnClickActivity())
+                            : null;
+            rootView.setOnClickListener(listener);
+            rootView.setClickable(listener != null);
+        });
+
+        return rootView;
+    }
+}
diff --git a/src/com/android/car/carlauncher/ContextualInfo.java b/src/com/android/car/carlauncher/ContextualInfo.java
new file mode 100644
index 0000000..a85a53e
--- /dev/null
+++ b/src/com/android/car/carlauncher/ContextualInfo.java
@@ -0,0 +1,62 @@
+package com.android.car.carlauncher;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.Nullable;
+
+final class ContextualInfo {
+    private final Drawable mIcon;
+    private final CharSequence mTopLine;
+    private final @Nullable CharSequence mBottomLine;
+    private final boolean mShowClock;
+    private final Intent mOnClickActivity;
+
+    public ContextualInfo(
+            Drawable icon,
+            CharSequence topLine,
+            @Nullable CharSequence bottomLine,
+            boolean showClock,
+            @Nullable Intent onClickActivity) {
+        mIcon = icon;
+        mTopLine = topLine;
+        mBottomLine = bottomLine;
+        mShowClock = showClock;
+        mOnClickActivity = onClickActivity;
+    }
+
+    /** Gets the icon to be shown in the contextual space. */
+    public Drawable getIcon() {
+        return mIcon;
+    }
+
+    /** Gets the top line of the text to be shown in the contextual space. */
+    public CharSequence getTopLine() {
+        return mTopLine;
+    }
+
+    /**
+     * Gets the bottom line of the text to be shown in the contextual space.
+     *
+     * If null, no bottom-line text will be shown in the contextual space.
+     */
+    @Nullable
+    public CharSequence getBottomLine() {
+        return mBottomLine;
+    }
+
+    /** Gets whether to show the date in the contextual space. */
+    public boolean getShowClock() {
+        return mShowClock;
+    }
+
+    /**
+     * Gets the {@link Intent} for the activity to be started when the contextual space is tapped.
+     *
+     * If null, the contextual space will not be tappable.
+     */
+    @Nullable
+    public Intent getOnClickActivity() {
+        return mOnClickActivity;
+    }
+}
diff --git a/src/com/android/car/carlauncher/ContextualViewModel.java b/src/com/android/car/carlauncher/ContextualViewModel.java
new file mode 100644
index 0000000..b1538df
--- /dev/null
+++ b/src/com/android/car/carlauncher/ContextualViewModel.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+package com.android.car.carlauncher;
+
+import android.app.Application;
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.CarProjectionManager;
+import android.car.CarProjectionManager.ProjectionStatusListener;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import androidx.lifecycle.AndroidViewModel;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Observer;
+import androidx.lifecycle.Transformations;
+import androidx.lifecycle.ViewModel;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation {@link ViewModel} for {@link ContextualFragment}.
+ *
+ * Returns the first non-null {@link ContextualInfo} from a set of delegates.
+ */
+public class ContextualViewModel extends AndroidViewModel {
+    private final MediatorLiveData<ContextualInfo> mContextualInfo = new MediatorLiveData<>();
+
+    private final List<LiveData<ContextualInfo>> mInfoDelegates;
+
+    public ContextualViewModel(Application application) {
+        this(application, getCarProjectionManager(application));
+    }
+
+    private static CarProjectionManager getCarProjectionManager(Context context) {
+        return (CarProjectionManager)
+                Car.createCar(context).getCarManager(Car.PROJECTION_SERVICE);
+    }
+
+    @VisibleForTesting
+    ContextualViewModel(Application application, CarProjectionManager carProjectionManager) {
+        super(application);
+
+
+        mInfoDelegates =
+                Collections.unmodifiableList(Arrays.asList(
+                        new ProjectionContextualInfoLiveData(application, carProjectionManager),
+                        new WeatherContextualInfoLiveData(application)
+                ));
+
+        Observer<Object> observer = x -> updateLiveData();
+        for (LiveData<ContextualInfo> delegate : mInfoDelegates) {
+            mContextualInfo.addSource(delegate, observer);
+        }
+    }
+
+    private void updateLiveData() {
+        for (LiveData<ContextualInfo> delegate : mInfoDelegates) {
+            ContextualInfo value = delegate.getValue();
+            if (value != null) {
+                mContextualInfo.setValue(value);
+                return;
+            }
+        }
+
+        mContextualInfo.setValue(null);
+    }
+
+    public LiveData<ContextualInfo> getContextualInfo() {
+        return mContextualInfo;
+    }
+}
diff --git a/src/com/android/car/carlauncher/LocalizedTextClock.java b/src/com/android/car/carlauncher/LocalizedTextClock.java
new file mode 100644
index 0000000..49b9666
--- /dev/null
+++ b/src/com/android/car/carlauncher/LocalizedTextClock.java
@@ -0,0 +1,43 @@
+package com.android.car.carlauncher;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.AttributeSet;
+import android.widget.TextClock;
+
+import java.util.Locale;
+
+/**
+ * {@link TextClock} implementation which expects a date format skeleton for
+ * {@link android.R.styleable#TextClock_format12Hour} and
+ * {@link android.R.styleable#TextClock_format24Hour} and applies the best format as determined by
+ * {@link DateFormat#getBestDateTimePattern(java.util.Locale, String)}.
+ */
+public class LocalizedTextClock extends TextClock {
+
+    public LocalizedTextClock(Context context) {
+        super(context);
+    }
+
+    public LocalizedTextClock(Context context, AttributeSet attrs) {
+        super(context, attrs, 0);
+    }
+
+    public LocalizedTextClock(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public LocalizedTextClock(Context context, AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        setFormat12Hour(DateFormat.getBestDateTimePattern(Locale.getDefault(),
+                getFormat12Hour().toString()));
+        setFormat24Hour(DateFormat.getBestDateTimePattern(Locale.getDefault(),
+                getFormat24Hour().toString()));
+    }
+}
diff --git a/src/com/android/car/carlauncher/ProjectionContextualInfoLiveData.java b/src/com/android/car/carlauncher/ProjectionContextualInfoLiveData.java
new file mode 100644
index 0000000..bd3d466
--- /dev/null
+++ b/src/com/android/car/carlauncher/ProjectionContextualInfoLiveData.java
@@ -0,0 +1,123 @@
+package com.android.car.carlauncher;
+
+import android.car.CarProjectionManager;
+import android.car.projection.ProjectionStatus;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.LiveData;
+
+import java.util.List;
+
+/** A {@link LiveData} of {@link ContextualInfo} on projection status. */
+class ProjectionContextualInfoLiveData extends LiveData<ContextualInfo>
+        implements CarProjectionManager.ProjectionStatusListener {
+    private static final String TAG = "ProjectionContext";
+
+    private final Context mContext;
+    private final CarProjectionManager mCarProjectionManager;
+
+    ProjectionContextualInfoLiveData(
+            Context context,
+            CarProjectionManager carProjectionManager) {
+        mContext = context;
+        mCarProjectionManager = carProjectionManager;
+    }
+
+    @Override
+    protected void onActive() {
+        super.onActive();
+        mCarProjectionManager.registerProjectionStatusListener(this);
+    }
+
+    @Override
+    protected void onInactive() {
+        mCarProjectionManager.unregisterProjectionStatusListener(this);
+        super.onInactive();
+    }
+
+    @Override
+    public void onProjectionStatusChanged(
+            int state, @Nullable String packageName, @NonNull List<ProjectionStatus> details) {
+        if (Log.isLoggable(TAG, Log.DEBUG)) {
+            Log.d(TAG, "onProjectionStatusChanged state=" + state + " package=" + packageName);
+        }
+        if (state == ProjectionStatus.PROJECTION_STATE_INACTIVE || packageName == null) {
+            setValue(null);
+            return;
+        }
+
+        PackageManager pm = mContext.getPackageManager();
+        ApplicationInfo applicationInfo;
+        try {
+            applicationInfo = pm.getApplicationInfo(packageName, 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Could not load projection package information", e);
+            setValue(null);
+            return;
+        }
+
+        setValue(
+                new ContextualInfo(
+                        applicationInfo.loadIcon(pm),
+                        applicationInfo.loadLabel(pm),
+                        getStatusMessage(packageName, details),
+                        /* showClock= */ false,
+                        pm.getLaunchIntentForPackage(packageName)));
+    }
+
+    @Nullable
+    private String getStatusMessage(
+            String packageName, List<ProjectionStatus> details) {
+        for (ProjectionStatus status : details) {
+            if (packageName.equals(status.getPackageName())) {
+                return getStatusMessage(status);
+            }
+        }
+
+        return null;
+    }
+
+    @Nullable
+    private String getStatusMessage(ProjectionStatus status) {
+        // The status message is as follows:
+        // - If there is an unambiguous "best" device, the name of that device.
+        //   "Unambiguous" is defined as only one projecting device, or no projecting devices
+        //   and only one non-projecting device.
+        // - If there are multiple projecting or non-projecting devices, "N devices", where N
+        //   is the total number of projecting and non-projecting devices.
+        // - If there are no devices at all, no message. This should not happen if projection
+        //   apps are behaving properly, but may happen in the event of a projection app bug.
+        String projectingDevice = null;
+        String nonProjectingDevice = null;
+        int projectingDeviceCount = 0;
+        int nonProjectingDeviceCount = 0;
+        for (ProjectionStatus.MobileDevice device : status.getConnectedMobileDevices()) {
+            if (device.isProjecting()) {
+                projectingDevice = device.getName();
+                projectingDeviceCount++;
+            } else {
+                nonProjectingDevice = device.getName();
+                nonProjectingDeviceCount++;
+            }
+        }
+
+        if (projectingDeviceCount == 1) {
+            return projectingDevice;
+        } else if (projectingDeviceCount == 0 && nonProjectingDeviceCount == 1) {
+            return nonProjectingDevice;
+        }
+
+        int totalDeviceCount = projectingDeviceCount + nonProjectingDeviceCount;
+        if (totalDeviceCount > 0) {
+            return mContext.getResources().getQuantityString(
+                    R.plurals.projection_devices, totalDeviceCount, totalDeviceCount);
+        } else {
+            return null;
+        }
+    }
+}
diff --git a/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java b/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
index 8090fe5..f0351e1 100644
--- a/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
+++ b/src/com/android/car/carlauncher/RecentAppsRowViewHolder.java
@@ -21,9 +21,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.ImageView;
 import android.widget.LinearLayout;
-import android.widget.TextView;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -62,18 +60,9 @@
         for (int i = 0; i < size; i++) {
             View view =
                     LayoutInflater.from(mContext).inflate(R.layout.app_item, mRecentAppsRow, false);
-            ImageView iconView = view.findViewById(R.id.app_icon);
-            TextView appNameView = view.findViewById(R.id.app_name);
 
-            AppMetaData app = apps.get(i);
-
-            if (isDistractionOptimizationRequired && !app.getIsDistractionOptimized()) {
-                iconView.setImageDrawable(AppLauncherUtils.toGrayscale(app.getIcon()));
-            } else {
-                iconView.setImageDrawable(app.getIcon());
-                view.setOnClickListener(v -> AppLauncherUtils.launchApp(mContext, app));
-            }
-            appNameView.setText(app.getDisplayName());
+            AppItemViewHolder holder = new AppItemViewHolder(view, mContext);
+            holder.bind(apps.get(i), isDistractionOptimizationRequired);
 
             LinearLayout.LayoutParams params =
                     (LinearLayout.LayoutParams) view.getLayoutParams();
diff --git a/src/com/android/car/carlauncher/WeatherContextualInfoLiveData.java b/src/com/android/car/carlauncher/WeatherContextualInfoLiveData.java
new file mode 100644
index 0000000..96c9773
--- /dev/null
+++ b/src/com/android/car/carlauncher/WeatherContextualInfoLiveData.java
@@ -0,0 +1,47 @@
+package com.android.car.carlauncher;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserManager;
+
+import androidx.lifecycle.LiveData;
+
+/** A {@link LiveData} that returns placeholder weather {@link ContextualInfo}. */
+class WeatherContextualInfoLiveData extends LiveData<ContextualInfo> {
+    private final Context mContext;
+
+    WeatherContextualInfoLiveData(Context context) {
+        mContext = context;
+    }
+
+    @Override
+    protected void onActive() {
+        super.onActive();
+        setValue(
+                new ContextualInfo(
+                        getWeatherIcon(),
+                        getGreeting(),
+                        getTemperature(),
+                        /* showClock= */ true,
+                        /* onClickActivity= */ null));
+    }
+
+    private Drawable getWeatherIcon() {
+        return mContext.getDrawable(R.drawable.ic_partly_cloudy);
+    }
+
+    private CharSequence getGreeting() {
+        UserManager userManager = UserManager.get(mContext);
+        String userName = userManager.getUserName();
+
+        if (userName != null) {
+            return mContext.getString(R.string.greeting, userName);
+        } else {
+            return "";
+        }
+    }
+
+    private CharSequence getTemperature() {
+        return mContext.getText(R.string.temperature_empty);
+    }
+}