diff options
| author | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-10-01 20:29:35 +0000 |
|---|---|---|
| committer | android-build-prod (mdb) <android-build-team-robot@google.com> | 2020-10-01 20:29:35 +0000 |
| commit | b3e3b17db72eb907f093c09982c0de7b64d54775 (patch) | |
| tree | f27ab1b1903473925ef4d16a2cb3822779bb5ef3 | |
| parent | 03a42be085841a61c2a445bb1c78e8c23ba81a3e (diff) | |
| parent | beaac8b823f752bf44d9a63baca23c0070b0fe4d (diff) | |
| download | platform_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.xml | 39 | ||||
| -rw-r--r-- | OWNERS | 5 | ||||
| -rw-r--r-- | res/drawable/app_launcher_ripple_background.xml | 26 | ||||
| -rw-r--r-- | res/layout-land/car_launcher.xml | 111 | ||||
| -rw-r--r-- | res/layout/app_grid_activity.xml | 16 | ||||
| -rw-r--r-- | res/layout/app_grid_activity_header.xml | 63 | ||||
| -rw-r--r-- | res/layout/app_item.xml | 1 | ||||
| -rw-r--r-- | res/layout/car_launcher.xml | 18 | ||||
| -rw-r--r-- | res/layout/contextual_fragment.xml | 16 | ||||
| -rw-r--r-- | res/values-land/dimens.xml | 21 | ||||
| -rw-r--r-- | res/values/dimens.xml | 6 | ||||
| -rw-r--r-- | res/values/strings.xml | 7 | ||||
| -rw-r--r-- | res/values/themes.xml | 8 | ||||
| -rw-r--r-- | src/com/android/car/carlauncher/AppGridActivity.java | 71 | ||||
| -rw-r--r-- | src/com/android/car/carlauncher/AppLauncherUtils.java | 16 | ||||
| -rw-r--r-- | src/com/android/car/carlauncher/CarLauncher.java | 70 | ||||
| -rw-r--r-- | src/com/android/car/carlauncher/ContextualViewModel.java | 23 |
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> @@ -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(); |
