summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-10-01 20:29:35 +0000
committerandroid-build-prod (mdb) <android-build-team-robot@google.com>2020-10-01 20:29:35 +0000
commitb3e3b17db72eb907f093c09982c0de7b64d54775 (patch)
treef27ab1b1903473925ef4d16a2cb3822779bb5ef3
parent03a42be085841a61c2a445bb1c78e8c23ba81a3e (diff)
parentbeaac8b823f752bf44d9a63baca23c0070b0fe4d (diff)
downloadplatform_packages_apps_Car_Launcher-sdk-release.tar.gz
platform_packages_apps_Car_Launcher-sdk-release.tar.bz2
platform_packages_apps_Car_Launcher-sdk-release.zip
Snap for 6877830 from beaac8b823f752bf44d9a63baca23c0070b0fe4d to sdk-releasesdk-release
Change-Id: Ie1408eb7e5250311c7a7c1d5a9d2d63533b68464
-rw-r--r--AndroidManifest.xml39
-rw-r--r--OWNERS5
-rw-r--r--res/drawable/app_launcher_ripple_background.xml26
-rw-r--r--res/layout-land/car_launcher.xml111
-rw-r--r--res/layout/app_grid_activity.xml16
-rw-r--r--res/layout/app_grid_activity_header.xml63
-rw-r--r--res/layout/app_item.xml1
-rw-r--r--res/layout/car_launcher.xml18
-rw-r--r--res/layout/contextual_fragment.xml16
-rw-r--r--res/values-land/dimens.xml21
-rw-r--r--res/values/dimens.xml6
-rw-r--r--res/values/strings.xml7
-rw-r--r--res/values/themes.xml8
-rw-r--r--src/com/android/car/carlauncher/AppGridActivity.java71
-rw-r--r--src/com/android/car/carlauncher/AppLauncherUtils.java16
-rw-r--r--src/com/android/car/carlauncher/CarLauncher.java70
-rw-r--r--src/com/android/car/carlauncher/ContextualViewModel.java23
17 files changed, 320 insertions, 197 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index f084f80..8b26907 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,22 +16,35 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- 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"/>
+ package="com.android.car.carlauncher">
+
+ <uses-permission android:name="android.car.permission.ACCESS_CAR_PROJECTION_STATUS"/>
+ <!-- System permission to host maps activity -->
+ <uses-permission android:name="android.permission.ACTIVITY_EMBEDDING"/>
<!-- System permission to send events to hosted maps activity -->
<uses-permission android:name="android.permission.INJECT_EVENTS"/>
+ <!-- System permission to use internal system windows -->
+ <uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
<!-- 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"/>
+ <!-- System permission to control media playback of the active session -->
+ <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
+ <!-- System permission to get app usage data -->
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
+ <!-- System permission to query all installed packages -->
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.REORDER_TASKS"/>
+
+ <!-- To connect to media browser services in other apps, media browser clients
+ that target Android 11 need to add the following in their manifest -->
+ <queries>
+ <intent>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent>
+ </queries>
+
<application
android:icon="@drawable/ic_launcher_home"
android:label="@string/app_title"
@@ -43,8 +56,7 @@
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:resumeWhilePausing="true"
- android:windowSoftInputMode="adjustPan"
- android:screenOrientation="nosensor">
+ android:windowSoftInputMode="adjustPan">
<meta-data android:name="distractionOptimized" android:value="true"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
@@ -56,7 +68,8 @@
android:name=".AppGridActivity"
android:launchMode="singleInstance"
android:exported="true"
- android:noHistory="true">
+ android:noHistory="true"
+ android:theme="@style/Theme.Launcher.AppGridActivity">
<meta-data android:name="distractionOptimized" android:value="true"/>
</activity>
</application>
diff --git a/OWNERS b/OWNERS
index 1686b3c..3818adf 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,8 @@
# Default code reviewers picked from top 3 or more developers.
# Please update this list if you find better candidates.
-qiaozhang@google.com
-davidln@google.com
+
ajchen@google.com
arnaudberry@google.com
+hseog@google.com
+jonathankoo@google.com
stenning@google.com
diff --git a/res/drawable/app_launcher_ripple_background.xml b/res/drawable/app_launcher_ripple_background.xml
deleted file mode 100644
index 450ca8f..0000000
--- a/res/drawable/app_launcher_ripple_background.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?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.
- -->
-<ripple
- xmlns:android="http://schemas.android.com/apk/res/android"
- 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/app_icon_ripple_radius" />
- </shape>
- </item>
-</ripple>
diff --git a/res/layout-land/car_launcher.xml b/res/layout-land/car_launcher.xml
new file mode 100644
index 0000000..b7d8ed5
--- /dev/null
+++ b/res/layout-land/car_launcher.xml
@@ -0,0 +1,111 @@
+<?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_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">
+ <android.car.app.CarActivityView
+ 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/app_grid_activity.xml b/res/layout/app_grid_activity.xml
index e5d644d..6dd8959 100644
--- a/res/layout/app_grid_activity.xml
+++ b/res/layout/app_grid_activity.xml
@@ -14,24 +14,16 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<LinearLayout
+<com.android.car.ui.FocusArea
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:id="@+id/focus_area"
android:layout_width="match_parent"
- 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_height="match_parent">
<com.android.car.ui.recyclerview.CarUiRecyclerView
android:id="@+id/apps_grid"
app:layoutStyle="grid"
app:numOfColumns="4"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
-</LinearLayout>
-
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/app_grid_activity_header.xml b/res/layout/app_grid_activity_header.xml
deleted file mode 100644
index 36aa10c..0000000
--- a/res/layout/app_grid_activity_header.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?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.
--->
-
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/header"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:clipChildren="false"
- android:clipToPadding="false"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <FrameLayout
- android:id="@+id/exit_button_container"
- android:layout_width="@dimen/panel_margin"
- android:layout_height="@dimen/app_bar_height"
- android:layout_alignParentStart="true"
- android:layout_centerVertical="true"
- android:background="?android:attr/selectableItemBackground"
- android:clickable="true"
- android:focusable="true">
- <ImageButton
- 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:scaleType="fitXY"
- android:src="@drawable/ic_clear_black"
- android:tint="@color/icon_tint"/>
- </FrameLayout>
-
- <TextView
- android:id="@+id/title"
- style="@style/TitleText"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/app_bar_height"
- android:layout_toEndOf="@id/exit_button_container"
- android:gravity="center_vertical"
- android:text="@string/app_launcher_title_all_apps"/>
-
- <View
- 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 2e7e011..cecbc65 100644
--- a/res/layout/app_item.xml
+++ b/res/layout/app_item.xml
@@ -23,7 +23,6 @@
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:padding="@dimen/app_touch_target_padding">
diff --git a/res/layout/car_launcher.xml b/res/layout/car_launcher.xml
index 4b28105..f2cd649 100644
--- a/res/layout/car_launcher.xml
+++ b/res/layout/car_launcher.xml
@@ -55,14 +55,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- app:layout_constraintGuide_percent="@dimen/maps_screen_percentage"/>
+ app:layout_constraintGuide_percent="@dimen/contextual_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"/>
+ app:layout_constraintGuide_percent="@dimen/maps_screen_percentage"/>
<View
android:id="@+id/top_line"
@@ -74,11 +74,11 @@
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_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">
- <ActivityView
+ <android.car.app.CarActivityView
android:id="@+id/maps"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
@@ -89,10 +89,10 @@
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_constraintBottom_toTopOf="@+id/bottom_edge"
app:layout_constraintEnd_toStartOf="@+id/end_edge"
app:layout_constraintStart_toEndOf="@+id/divider_vertical"
- app:layout_constraintTop_toBottomOf="@+id/top_edge"/>
+ app:layout_constraintTop_toBottomOf="@+id/divider_horizontal"/>
<FrameLayout
android:id="@+id/playback"
@@ -100,8 +100,8 @@
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_constraintEnd_toStartOf="@+id/divider_vertical"
+ app:layout_constraintStart_toEndOf="@+id/start_edge"
app:layout_constraintTop_toBottomOf="@+id/divider_horizontal"/>
<View
android:id="@+id/bottom_line"
diff --git a/res/layout/contextual_fragment.xml b/res/layout/contextual_fragment.xml
index 3f090f5..307ba27 100644
--- a/res/layout/contextual_fragment.xml
+++ b/res/layout/contextual_fragment.xml
@@ -19,30 +19,30 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
style="@style/ContextualSpace"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ app:contentPadding="@dimen/contextual_padding">
<androidx.constraintlayout.widget.ConstraintLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical">
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
<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_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/icon_end_margin"
- android:ellipsize="end"
- android:maxLines="1"
+ android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceLarge"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/bottom_line_container"
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
new file mode 100644
index 0000000..3840a4a
--- /dev/null
+++ b/res/values-land/dimens.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+<resources>
+
+ <item name="contextual_screen_percentage" type="dimen" format="float">.3</item>
+
+</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 4c276a4..bd38f62 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,15 +20,15 @@
<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>
+ <item name="contextual_screen_percentage" type="dimen" format="float">.6</item>
+ <!-- Padding on the edges of the contextual card -->
+ <dimen name="contextual_padding">@*android:dimen/car_padding_4</dimen>
<!--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 -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a46a2af..9bb7207 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -37,6 +37,11 @@
<item quantity="other"><xliff:g example="2" id="count">%d</xliff:g> devices</item>
</plurals>
+ <!-- Toolbar MenuItem text for hiding debug apps, only visible on debug builds [CHAR_LIMIT=50] -->
+ <string name="hide_debug_apps">Hide debug apps</string>
+ <!-- Toolbar MenuItem text for showing debug apps, only visible on debug builds [CHAR_LIMIT=50] -->
+ <string name="show_debug_apps">Show debug apps</string>
+
<!--
A list of package names to exclude from the app selector when filtering is active.
DO NOT TRANSLATE.
@@ -46,7 +51,7 @@
<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.bugreport</item>
<item>com.android.car.trust</item>
<item>com.android.car.datacenter</item>
<item>com.google.android.car.diagnosticrecorder</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 3001aff..abc9128 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -16,8 +16,14 @@
-->
<resources>
- <style name="Theme.Launcher" parent="android:Theme.DeviceDefault.NoActionBar">
+ <style name="Theme.Launcher" parent="Theme.CarUi.NoToolbar">
<item name="textAppearanceGridItem">@android:style/TextAppearance.DeviceDefault.Medium</item>
<item name="textAppearanceGridItemSecondary">@android:style/TextAppearance.DeviceDefault.Small</item>
</style>
+
+ <style name="Theme.Launcher.AppGridActivity" parent="Theme.CarUi.WithToolbar">
+ <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 616bb5c..9d77f18 100644
--- a/src/com/android/car/carlauncher/AppGridActivity.java
+++ b/src/com/android/car/carlauncher/AppGridActivity.java
@@ -26,6 +26,7 @@ import android.car.Car;
import android.car.CarNotConnectedException;
import android.car.content.pm.CarPackageManager;
import android.car.drivingstate.CarUxRestrictionsManager;
+import android.car.media.CarMediaManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -34,23 +35,28 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
-import android.net.Uri;
+import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.car.carlauncher.AppLauncherUtils.LauncherAppsInfo;
+import com.android.car.ui.FocusArea;
+import com.android.car.ui.baselayout.Insets;
+import com.android.car.ui.baselayout.InsetsChangedListener;
+import com.android.car.ui.core.CarUi;
+import com.android.car.ui.toolbar.MenuItem;
+import com.android.car.ui.toolbar.Toolbar;
+import com.android.car.ui.toolbar.ToolbarController;
import java.util.ArrayList;
import java.util.Arrays;
@@ -63,7 +69,7 @@ import java.util.Set;
/**
* Launcher activity that shows a grid of apps.
*/
-public final class AppGridActivity extends Activity {
+public final class AppGridActivity extends Activity implements InsetsChangedListener {
private static final String TAG = "AppGridActivity";
private static final String MODE_INTENT_EXTRA = "com.android.car.carlauncher.mode";
@@ -78,6 +84,7 @@ public final class AppGridActivity extends Activity {
private Car mCar;
private CarUxRestrictionsManager mCarUxRestrictionsManager;
private CarPackageManager mCarPackageManager;
+ private CarMediaManager mCarMediaManager;
private Mode mMode;
private enum Mode {
@@ -119,6 +126,7 @@ public final class AppGridActivity extends Activity {
restrictionInfo.isRequiresDistractionOptimization()));
mCarPackageManager = (CarPackageManager) mCar.getCarManager(Car.PACKAGE_SERVICE);
+ mCarMediaManager = (CarMediaManager) mCar.getCarManager(Car.CAR_MEDIA_SERVICE);
updateAppsLists();
} catch (CarNotConnectedException e) {
Log.e(TAG, "Car not connected in CarConnectionListener", e);
@@ -147,16 +155,26 @@ public final class AppGridActivity extends Activity {
updateMode();
- View exitView = findViewById(R.id.exit_button_container);
- exitView.setOnClickListener(v -> finish());
- exitView.setOnLongClickListener(v -> {
- mShowAllApps = !mShowAllApps;
- updateAppsLists();
- return true;
- });
+ ToolbarController toolbar = CarUi.requireToolbar(this);
+ toolbar.setNavButtonMode(Toolbar.NavButtonMode.CLOSE);
+ toolbar.setState(Toolbar.State.SUBPAGE);
+
+ if (Build.IS_DEBUGGABLE) {
+ toolbar.setMenuItems(Collections.singletonList(MenuItem.builder(this)
+ .setDisplayBehavior(MenuItem.DisplayBehavior.NEVER)
+ .setTitle(R.string.hide_debug_apps)
+ .setOnClickListener(i -> {
+ mShowAllApps = !mShowAllApps;
+ i.setTitle(mShowAllApps
+ ? R.string.hide_debug_apps
+ : R.string.show_debug_apps);
+ updateAppsLists();
+ })
+ .build()));
+ }
mGridAdapter = new AppGridAdapter(this);
- RecyclerView gridView = findViewById(R.id.apps_grid);
+ RecyclerView gridView = requireViewById(R.id.apps_grid);
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, mColumnNumber);
gridLayoutManager.setSpanSizeLookup(new SpanSizeLookup() {
@@ -176,10 +194,19 @@ public final class AppGridActivity extends Activity {
updateMode();
}
+ @Override
+ protected void onDestroy() {
+ if (mCar != null && mCar.isConnected()) {
+ mCar.disconnect();
+ mCar = null;
+ }
+ super.onDestroy();
+ }
+
private void updateMode() {
mMode = parseMode(getIntent());
- TextView titleView = findViewById(R.id.title);
- titleView.setText(mMode.mTitleStringId);
+ setTitle(mMode.mTitleStringId);
+ CarUi.requireToolbar(this).setTitle(mMode.mTitleStringId);
}
/**
@@ -213,7 +240,8 @@ public final class AppGridActivity extends Activity {
mMode.mOpenMediaCenter,
getSystemService(LauncherApps.class),
mCarPackageManager,
- mPackageManager);
+ mPackageManager,
+ mCarMediaManager);
mGridAdapter.setAllApps(appsInfo.getLaunchableComponentsList());
mGridAdapter.setMostRecentApps(getMostRecentApps(appsInfo));
}
@@ -326,6 +354,17 @@ public final class AppGridActivity extends Activity {
return apps;
}
+ @Override
+ public void onCarUiInsetsChanged(Insets insets) {
+ requireViewById(R.id.apps_grid)
+ .setPadding(0, insets.getTop(), 0, insets.getBottom());
+ FocusArea focusArea = requireViewById(R.id.focus_area);
+ focusArea.setHighlightPadding(0, insets.getTop(), 0, insets.getBottom());
+
+ requireViewById(android.R.id.content)
+ .setPadding(insets.getLeft(), 0, insets.getRight(), 0);
+ }
+
/**
* Comparator for {@link UsageStats} that sorts the list by the "last time used" property
* in descending order.
diff --git a/src/com/android/car/carlauncher/AppLauncherUtils.java b/src/com/android/car/carlauncher/AppLauncherUtils.java
index 223a551..d10ee5f 100644
--- a/src/com/android/car/carlauncher/AppLauncherUtils.java
+++ b/src/com/android/car/carlauncher/AppLauncherUtils.java
@@ -177,9 +177,11 @@ class AppLauncherUtils {
boolean openMediaCenter,
LauncherApps launcherApps,
CarPackageManager carPackageManager,
- PackageManager packageManager) {
+ PackageManager packageManager,
+ CarMediaManager carMediaManager) {
- if (launcherApps == null || carPackageManager == null || packageManager == null) {
+ if (launcherApps == null || carPackageManager == null || packageManager == null
+ || carMediaManager == null) {
return EMPTY_APPS_INFO;
}
@@ -216,7 +218,7 @@ class AppLauncherUtils {
if (openMediaCenter) {
AppLauncherUtils.launchApp(context, intent);
} else {
- selectMediaSourceAndFinish(context, componentName);
+ selectMediaSourceAndFinish(context, componentName, carMediaManager);
}
},
context -> AppLauncherUtils.launchApp(context,
@@ -285,12 +287,10 @@ class AppLauncherUtils {
}
}
- private static void selectMediaSourceAndFinish(Context context, ComponentName componentName) {
+ private static void selectMediaSourceAndFinish(Context context, ComponentName componentName,
+ CarMediaManager carMediaManager) {
try {
- Car carApi = Car.createCar(context);
- CarMediaManager manager = (CarMediaManager) carApi
- .getCarManager(Car.CAR_MEDIA_SERVICE);
- manager.setMediaSource(componentName);
+ carMediaManager.setMediaSource(componentName, CarMediaManager.MEDIA_SOURCE_MODE_BROWSE);
if (context instanceof Activity) {
((Activity) context).finish();
}
diff --git a/src/com/android/car/carlauncher/CarLauncher.java b/src/com/android/car/carlauncher/CarLauncher.java
index 5108215..9258b54 100644
--- a/src/com/android/car/carlauncher/CarLauncher.java
+++ b/src/com/android/car/carlauncher/CarLauncher.java
@@ -17,15 +17,18 @@
package com.android.car.carlauncher;
import android.app.ActivityManager;
-import android.app.ActivityOptions;
import android.app.ActivityView;
-import android.app.UserSwitchObserver;
+import android.car.app.CarActivityView;
+import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManager.DisplayListener;
import android.os.Bundle;
-import android.os.IRemoteCallback;
-import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
+import android.view.Display;
import android.widget.FrameLayout;
import androidx.fragment.app.FragmentActivity;
@@ -33,8 +36,6 @@ import androidx.fragment.app.FragmentTransaction;
import com.android.car.media.common.PlaybackFragment;
-import java.util.Set;
-
/**
* Basic Launcher for Android Automotive which demonstrates the use of {@link ActivityView} to host
* maps content.
@@ -57,9 +58,11 @@ public class CarLauncher extends FragmentActivity {
private static final String TAG = "CarLauncher";
private static final boolean DEBUG = false;
- private ActivityView mActivityView;
+ private CarActivityView mActivityView;
private boolean mActivityViewReady;
private boolean mIsStarted;
+ private DisplayManager mDisplayManager;
+ private final Handler mMainHandler = new Handler(Looper.getMainLooper());
/** Set to {@code true} once we've logged that the Activity is fully drawn. */
private boolean mIsReadyLogged;
@@ -98,6 +101,22 @@ public class CarLauncher extends FragmentActivity {
}
};
+ private final DisplayListener mDisplayListener = new DisplayListener() {
+ @Override
+ public void onDisplayAdded(int displayId) {}
+ @Override
+ public void onDisplayRemoved(int displayId) {}
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId != getDisplay().getDisplayId()) {
+ return;
+ }
+ // startMapsInActivityView() will check Display's State.
+ startMapsInActivityView();
+ }
+ };
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -114,15 +133,8 @@ public class CarLauncher extends FragmentActivity {
if (mActivityView != null) {
mActivityView.setCallback(mActivityViewCallback);
}
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- Set<String> categories = intent.getCategories();
- if (categories != null && categories.size() == 1 && categories.contains(
- Intent.CATEGORY_APP_MAPS)) {
- launchMapsActivity();
- }
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, mMainHandler);
}
@Override
@@ -147,30 +159,32 @@ public class CarLauncher extends FragmentActivity {
@Override
protected void onDestroy() {
super.onDestroy();
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
if (mActivityView != null && mActivityViewReady) {
mActivityView.release();
}
}
private void startMapsInActivityView() {
+ if (mActivityView == null || !mActivityViewReady) {
+ return;
+ }
// 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()) {
+ if (isInMultiWindowMode() || isInPictureInPictureMode()) {
return;
}
- if (mActivityView != null) {
+ // Don't start Maps when the display is off for ActivityVisibilityTests.
+ if (getDisplay().getState() != Display.STATE_ON) {
+ return;
+ }
+ try {
mActivityView.startActivity(getMapsIntent());
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Maps activity not found", e);
}
}
- 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);
}
@@ -185,14 +199,14 @@ public class CarLauncher extends FragmentActivity {
PlaybackFragment playbackFragment = new PlaybackFragment();
ContextualFragment contextualFragment = null;
FrameLayout contextual = findViewById(R.id.contextual);
- if(contextual != null) {
+ if (contextual != null) {
contextualFragment = new ContextualFragment();
}
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.playback, playbackFragment);
- if(contextual != null) {
+ if (contextual != null) {
fragmentTransaction.replace(R.id.contextual, contextualFragment);
}
fragmentTransaction.commitNow();
diff --git a/src/com/android/car/carlauncher/ContextualViewModel.java b/src/com/android/car/carlauncher/ContextualViewModel.java
index b1538df..23194c1 100644
--- a/src/com/android/car/carlauncher/ContextualViewModel.java
+++ b/src/com/android/car/carlauncher/ContextualViewModel.java
@@ -51,19 +51,21 @@ public class ContextualViewModel extends AndroidViewModel {
private final List<LiveData<ContextualInfo>> mInfoDelegates;
- public ContextualViewModel(Application application) {
- this(application, getCarProjectionManager(application));
- }
+ private Car mCar;
- private static CarProjectionManager getCarProjectionManager(Context context) {
- return (CarProjectionManager)
- Car.createCar(context).getCarManager(Car.PROJECTION_SERVICE);
+ public ContextualViewModel(Application application) {
+ this(application, null);
}
@VisibleForTesting
ContextualViewModel(Application application, CarProjectionManager carProjectionManager) {
super(application);
+ if (carProjectionManager == null) {
+ mCar = Car.createCar(application);
+ carProjectionManager =
+ (CarProjectionManager) mCar.getCarManager(Car.PROJECTION_SERVICE);
+ }
mInfoDelegates =
Collections.unmodifiableList(Arrays.asList(
@@ -77,6 +79,15 @@ public class ContextualViewModel extends AndroidViewModel {
}
}
+ @Override
+ protected void onCleared() {
+ if (mCar != null && mCar.isConnected()) {
+ mCar.disconnect();
+ mCar = null;
+ }
+ super.onCleared();
+ }
+
private void updateLiveData() {
for (LiveData<ContextualInfo> delegate : mInfoDelegates) {
ContextualInfo value = delegate.getValue();