Snap for 8589293 from e0843e4e487f0933442f1a3b17d31c5e76b1ecb6 to sc-v2-platform-release
Change-Id: I6e9f6a3fdc8854da3071312c5c7766d3f332241d
diff --git a/OWNERS b/OWNERS
index f749102..777187a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,12 +1 @@
-# Primary
-kwaky@google.com
-
-# Secondary
-hseog@google.com
-nehah@google.com
-
-# Owners from Core Android SystemUI in case quick approval is needed for simple refactoring.
-# But generally, someone from the AAOS SystemUI listed above should be included.
-dupin@google.com
-mankoff@google.com
-pixel@google.com
\ No newline at end of file
+include platform/packages/apps/Car/SystemUI:master:/OWNERS
diff --git a/res/drawable/car_ic_managed_device.xml b/res/drawable/car_ic_managed_device.xml
new file mode 100644
index 0000000..fd333a7
--- /dev/null
+++ b/res/drawable/car_ic_managed_device.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@*android:dimen/car_touch_target_size"
+ android:height="@*android:dimen/car_touch_target_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="@color/car_managed_device_icon_color"
+ android:pathData="M4 21Q3.175 21 2.588 20.413Q2 19.825 2 19V8Q2 7.175 2.588 6.588Q3.175 6 4 6H8V4Q8 3.175 8.588 2.587Q9.175 2 10 2H14Q14.825 2 15.413 2.587Q16 3.175 16 4V6H20Q20.825 6 21.413 6.588Q22 7.175 22 8V19Q22 19.825 21.413 20.413Q20.825 21 20 21ZM10 6H14V4Q14 4 14 4Q14 4 14 4H10Q10 4 10 4Q10 4 10 4ZM4 19H20Q20 19 20 19Q20 19 20 19V8Q20 8 20 8Q20 8 20 8H4Q4 8 4 8Q4 8 4 8V19Q4 19 4 19Q4 19 4 19ZM20 19H4Q4 19 4 19Q4 19 4 19V8Q4 8 4 8Q4 8 4 8H20Q20 8 20 8Q20 8 20 8V19Q20 19 20 19Q20 19 20 19Z"/>
+</vector>
\ No newline at end of file
diff --git a/res/layout/device_management_dialog.xml b/res/layout/device_management_dialog.xml
new file mode 100644
index 0000000..f3516bf
--- /dev/null
+++ b/res/layout/device_management_dialog.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingTop="?android:attr/dialogPreferredPadding"
+ android:paddingRight="?android:attr/dialogPreferredPadding"
+ android:paddingLeft="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
+ <LinearLayout
+ android:id="@+id/device_management_disclosures"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/device_management_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/TextAppearance.CarUi.AlertDialog.Title"
+ android:paddingBottom="@dimen/device_management_dialog_subtitle_padding"
+ />
+ <TextView
+ android:id="@+id/device_management_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Subtitle"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/ca_certs_disclosures"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/ca_certs_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/monitoring_subtitle_ca_certificate"
+ style="@style/TextAppearance.CarUi.AlertDialog.Title"
+ android:paddingBottom="@dimen/device_management_dialog_subtitle_padding"
+ />
+ <TextView
+ android:id="@+id/ca_certs_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Subtitle"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/network_logging_disclosures"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/network_logging_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/monitoring_subtitle_network_logging"
+ style="@style/TextAppearance.CarUi.AlertDialog.Title"
+ android:paddingBottom="@dimen/device_management_dialog_subtitle_padding"
+ />
+ <TextView
+ android:id="@+id/network_logging_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Subtitle"
+ />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/vpn_disclosures"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingBottom="?android:attr/dialogPreferredPadding"
+ android:orientation="vertical">
+ <TextView
+ android:id="@+id/vpn_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/monitoring_subtitle_vpn"
+ style="@style/TextAppearance.CarUi.AlertDialog.Title"
+ android:paddingBottom="@dimen/device_management_dialog_subtitle_padding"
+ />
+ <TextView
+ android:id="@+id/vpn_warning"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.CarUi.AlertDialog.Subtitle"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</ScrollView>
diff --git a/res/layout/hvac_panel_container.xml b/res/layout/hvac_panel_container.xml
index 7ed9ac4..284920d 100644
--- a/res/layout/hvac_panel_container.xml
+++ b/res/layout/hvac_panel_container.xml
@@ -46,7 +46,7 @@
android:id="@+id/driver_hvac"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
systemui:hvacAreaId="49">
@@ -58,8 +58,8 @@
android:layout_height="@dimen/hvac_panel_group_height"
android:background="@drawable/hvac_panel_button_bg"
app:layout_constraintTop_toBottomOf="@+id/top_guideline"
- app:layout_constraintStart_toEndOf="@+id/driver_hvac"
- app:layout_constraintEnd_toStartOf="@+id/fan_control"
+ app:layout_constraintLeft_toRightOf="@+id/driver_hvac"
+ app:layout_constraintRight_toLeftOf="@+id/fan_control"
systemui:hvacAreaId="117"
systemui:hvacPropertyId="354419976"
systemui:hvacTurnOffIfAutoOn="true"
@@ -71,8 +71,7 @@
android:layout_width="@dimen/hvac_fan_speed_bar_width"
android:layout_height="@dimen/hvac_panel_group_height"
app:layout_constraintTop_toBottomOf="@+id/top_guideline"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintLeft_toRightOf="@+id/recycle_air_button"
app:layout_constraintRight_toLeftOf="@+id/ac_button"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true"
@@ -85,7 +84,8 @@
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:orientation="horizontal"/>
+ android:orientation="horizontal"
+ android:layoutDirection="ltr"/>
</LinearLayout>
<com.android.systemui.car.hvac.toggle.HvacBooleanToggleButton
android:id="@+id/ac_button"
@@ -93,8 +93,8 @@
android:layout_height="@dimen/hvac_panel_group_height"
android:background="@drawable/hvac_panel_button_bg"
app:layout_constraintTop_toBottomOf="@+id/top_guideline"
- app:layout_constraintStart_toEndOf="@+id/fan_control"
- app:layout_constraintEnd_toStartOf="@+id/passenger_hvac"
+ app:layout_constraintLeft_toRightOf="@+id/fan_control"
+ app:layout_constraintRight_toLeftOf="@+id/passenger_hvac"
systemui:hvacAreaId="117"
systemui:hvacPropertyId="354419973"
systemui:hvacTurnOffIfAutoOn="true"
@@ -115,7 +115,7 @@
android:background="@drawable/hvac_panel_button_bg"
android:scaleType="center"
style="@style/HvacButton"
- app:layout_constraintStart_toEndOf="@+id/driver_hvac"
+ app:layout_constraintLeft_toRightOf="@+id/driver_hvac"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacAreaId="117"
systemui:hvacPropertyId="354419984"
@@ -126,7 +126,7 @@
android:id="@+id/seat_heat_level_button_left"
android:background="@drawable/hvac_panel_button_bg"
style="@style/HvacButton"
- app:layout_constraintStart_toEndOf="@+id/ac_master_switch"
+ app:layout_constraintLeft_toRightOf="@+id/ac_master_switch"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacAreaId="1"
systemui:seatTemperatureType="heating"
@@ -135,7 +135,7 @@
android:id="@+id/defroster_button"
android:background="@drawable/hvac_panel_button_bg"
style="@style/HvacButton"
- app:layout_constraintStart_toEndOf="@+id/seat_heat_level_button_left"
+ app:layout_constraintLeft_toRightOf="@+id/seat_heat_level_button_left"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacAreaId="1"
systemui:hvacPropertyId="320865540"
@@ -150,8 +150,8 @@
android:layout_gravity="center"
android:layout_width="0dp"
style="@style/HvacButton"
- app:layout_constraintStart_toEndOf="@+id/defroster_button"
- app:layout_constraintEnd_toStartOf="@+id/rear_defroster_button"
+ app:layout_constraintLeft_toRightOf="@+id/defroster_button"
+ app:layout_constraintRight_toLeftOf="@+id/rear_defroster_button"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacToggleOnButtonDrawable="@drawable/ic_auto_on"
systemui:hvacToggleOffButtonDrawable="@drawable/ic_auto_off"/>
@@ -161,7 +161,7 @@
style="@style/HvacButton"
systemui:hvacAreaId="2"
systemui:hvacPropertyId="320865540"
- app:layout_constraintEnd_toStartOf="@+id/seat_heat_level_button_right"
+ app:layout_constraintRight_toLeftOf="@+id/seat_heat_level_button_right"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacToggleOnButtonDrawable="@drawable/ic_rear_defroster_on"
systemui:hvacToggleOffButtonDrawable="@drawable/ic_rear_defroster_off"/>
@@ -169,7 +169,7 @@
android:id="@+id/seat_heat_level_button_right"
android:background="@drawable/hvac_panel_button_bg"
style="@style/HvacButton"
- app:layout_constraintEnd_toStartOf="@+id/passenger_hvac"
+ app:layout_constraintRight_toLeftOf="@+id/passenger_hvac"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline"
systemui:hvacAreaId="4"
systemui:seatTemperatureType="heating"
@@ -178,4 +178,4 @@
layout="@layout/hvac_panel_handle_bar"
app:layout_constraintTop_toTopOf="parent"/>
</com.android.systemui.car.hvac.HvacPanelView>
-</com.android.car.ui.FocusArea>
\ No newline at end of file
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/hvac_temperature_bar_overlay.xml b/res/layout/hvac_temperature_bar_overlay.xml
index 2644548..2ae3f0b 100644
--- a/res/layout/hvac_temperature_bar_overlay.xml
+++ b/res/layout/hvac_temperature_bar_overlay.xml
@@ -30,7 +30,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/temperature_bar_icon_margin"
- android:layout_centerHorizontal="true"
android:layout_alignParentTop="true"
android:src="@drawable/ic_increase_icon"
style="@style/HvacTemperatureControlIcon"/>
@@ -46,8 +45,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/temperature_bar_icon_margin"
- android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
- android:src="@drawable/ic_decrease_icon" />
+ android:src="@drawable/ic_decrease_icon"
+ style="@style/HvacTemperatureControlIcon"/>
</RelativeLayout>
</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/qc_bluetooth_panel.xml b/res/layout/qc_bluetooth_panel.xml
index 7cf88ba..0daff2f 100644
--- a/res/layout/qc_bluetooth_panel.xml
+++ b/res/layout/qc_bluetooth_panel.xml
@@ -13,33 +13,50 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<com.android.car.ui.FocusArea
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:gravity="center"
+ android:orientation="vertical"
android:background="@color/status_icon_panel_bg_color">
- <com.android.car.ui.FocusArea
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_height="wrap_content">
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/bluetooth_switch"/>
- <com.android.systemui.car.qc.SystemUIQCView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/paired_bluetooth_devices"/>
+ android:layout_height="0dp"
+ app:layout_constraintHeight_default="wrap"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/qc_bluetooth_footer_button">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/bluetooth_switch"/>
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/paired_bluetooth_devices"/>
+ </LinearLayout>
+ </ScrollView>
<com.android.systemui.car.qc.QCFooterButton
+ android:id="@+id/qc_bluetooth_footer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
style="@style/QCFooterButtonStyle"
android:text="@string/qc_footer_bluetooth_settings"
app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$BluetoothSettingsActivity;launchFlags=0x08080000;end"/>
- </com.android.car.ui.FocusArea>
-</FrameLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/qc_connectivity_panel.xml b/res/layout/qc_connectivity_panel.xml
index 42ae926..8371f70 100644
--- a/res/layout/qc_connectivity_panel.xml
+++ b/res/layout/qc_connectivity_panel.xml
@@ -13,38 +13,55 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<com.android.car.ui.FocusArea
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:gravity="center"
+ android:orientation="vertical"
android:background="@color/status_icon_panel_bg_color">
- <com.android.car.ui.FocusArea
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_height="wrap_content">
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/mobile_data_row"/>
- <com.android.systemui.car.qc.SystemUIQCView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/hotspot_row"/>
- <com.android.systemui.car.qc.SystemUIQCView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/wifi_row"/>
+ android:layout_height="0dp"
+ app:layout_constraintHeight_default="wrap"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/qc_connectivity_footer_button">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/mobile_data_row"/>
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/hotspot_row"/>
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/wifi_row"/>
+ </LinearLayout>
+ </ScrollView>
<com.android.systemui.car.qc.QCFooterButton
+ android:id="@+id/qc_connectivity_footer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
style="@style/QCFooterButtonStyle"
android:text="@string/qc_footer_network_internet_settings"
app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$NetworkAndInternetActivity;launchFlags=0x08080000;end"/>
- </com.android.car.ui.FocusArea>
-</FrameLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/qc_display_panel.xml b/res/layout/qc_display_panel.xml
index 19776c0..8eaa6b9 100644
--- a/res/layout/qc_display_panel.xml
+++ b/res/layout/qc_display_panel.xml
@@ -13,33 +13,50 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<com.android.car.ui.FocusArea
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:gravity="center"
+ android:orientation="vertical"
android:background="@color/status_icon_panel_bg_color">
- <com.android.car.ui.FocusArea
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_height="wrap_content">
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/brightness_slider"/>
- <com.android.systemui.car.qc.SystemUIQCView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:remoteQCProvider="content://com.android.car.settings.qc/adaptive_brightness_switch"/>
+ android:layout_height="0dp"
+ app:layout_constraintHeight_default="wrap"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/qc_display_footer_button">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/brightness_slider"/>
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:remoteQCProvider="content://com.android.car.settings.qc/adaptive_brightness_switch"/>
+ </LinearLayout>
+ </ScrollView>
<com.android.systemui.car.qc.QCFooterButton
+ android:id="@+id/qc_display_footer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
style="@style/QCFooterButtonStyle"
android:text="@string/qc_footer_display_settings"
app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$DisplaySettingsActivity;launchFlags=0x08080000;end"/>
- </com.android.car.ui.FocusArea>
-</FrameLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/qc_mic_panel.xml b/res/layout/qc_mic_panel.xml
index 71f4a07..e4724f9 100644
--- a/res/layout/qc_mic_panel.xml
+++ b/res/layout/qc_mic_panel.xml
@@ -13,30 +13,41 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<FrameLayout
+<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/mic_privacy_panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical"
android:background="@color/status_icon_panel_bg_color">
- <com.android.car.ui.FocusArea
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:orientation="vertical">
- <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_height="wrap_content">
+ <ScrollView
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:localQCProvider="com.android.systemui.car.privacy.MicQcPanel"/>
+ android:layout_height="0dp"
+ app:layout_constraintHeight_default="wrap"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/qc_mic_footer_button">
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:localQCProvider="com.android.systemui.car.privacy.MicQcPanel"/>
+ </ScrollView>
<com.android.systemui.car.qc.QCFooterButton
+ android:id="@+id/qc_mic_footer_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
style="@style/QCFooterButtonStyle"
android:text="@string/mic_privacy_chip_microphone_settings"
app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$PrivacySettingsActivity;launchFlags=0x08080000;end"
app:disableWhileDriving="true"/>
- </com.android.car.ui.FocusArea>
-</FrameLayout>
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.car.ui.FocusArea>
diff --git a/res/layout/qc_profile_switcher.xml b/res/layout/qc_profile_switcher.xml
index 2367bb8..ab0f974 100644
--- a/res/layout/qc_profile_switcher.xml
+++ b/res/layout/qc_profile_switcher.xml
@@ -24,17 +24,33 @@
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
- <com.android.systemui.car.qc.SystemUIQCView
+ <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- app:localQCProvider="com.android.systemui.car.qc.ProfileSwitcher"/>
- <com.android.systemui.car.qc.QCFooterButton
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
- style="@style/QCFooterButtonStyle"
- android:text="@string/qc_footer_profiles_accounts_settings"
- app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$ProfileDetailsActivity;launchFlags=0x08080000;end"/>
+ android:layout_height="wrap_content">
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ app:layout_constraintHeight_default="wrap"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/qc_profile_footer_button">
+ <com.android.systemui.car.qc.SystemUIQCView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ app:localQCProvider="com.android.systemui.car.qc.ProfileSwitcher"/>
+ </ScrollView>
+ <com.android.systemui.car.qc.QCFooterButton
+ android:id="@+id/qc_profile_footer_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ style="@style/QCFooterButtonStyle"
+ android:text="@string/qc_footer_profiles_accounts_settings"
+ app:intent="intent:#Intent;component=com.android.car.settings/.common.CarSettingActivities$ProfileDetailsActivity;launchFlags=0x08080000;end"/>
+ </androidx.constraintlayout.widget.ConstraintLayout>
</com.android.car.ui.FocusArea>
</FrameLayout>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index f874b3e..9124399 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -32,8 +32,8 @@
<string name="user_add_user_message_update" msgid="3310089453006545712">"Qualsiasi profilo può aggiornare le app che verranno usate da tutti gli altri profili."</string>
<string name="profile_limit_reached_title" msgid="7891779218496729653">"Limite di profili raggiunto"</string>
<plurals name="profile_limit_reached_message" formatted="false" msgid="910608128912533066">
- <item quantity="one">Puoi aggiungere al massimo <xliff:g id="COUNT">%d</xliff:g> profilo.</item>
<item quantity="other">Puoi aggiungere al massimo <xliff:g id="COUNT">%d</xliff:g> profili.</item>
+ <item quantity="one">È possibile creare un solo profilo.</item>
</plurals>
<string name="car_loading_profile" msgid="458961191993686065">"Caricamento"</string>
<string name="car_loading_profile_developer_message" msgid="737810794567935702">"Caricamento dell\'utente (da <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 66c81ec..194d3dc 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -32,8 +32,8 @@
<string name="user_add_user_message_update" msgid="3310089453006545712">"As apps podem ser atualizadas a partir de qualquer perfil para utilização por todos os perfis."</string>
<string name="profile_limit_reached_title" msgid="7891779218496729653">"Limite de perfis atingido"</string>
<plurals name="profile_limit_reached_message" formatted="false" msgid="910608128912533066">
- <item quantity="one">Apenas pode criar um perfil.</item>
<item quantity="other">Pode adicionar até <xliff:g id="COUNT">%d</xliff:g> perfis.</item>
+ <item quantity="one">Apenas pode criar um perfil.</item>
</plurals>
<string name="car_loading_profile" msgid="458961191993686065">"A carregar…"</string>
<string name="car_loading_profile_developer_message" msgid="737810794567935702">"A carregar o utilizador (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string>
diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml
index 1cccd8a..ad1b190 100644
--- a/res/values-te/strings.xml
+++ b/res/values-te/strings.xml
@@ -29,7 +29,7 @@
<string name="car_new_user" msgid="6766334721724989964">"కొత్త ప్రొఫైల్"</string>
<string name="user_add_profile_title" msgid="828371911076521952">"కొత్త ప్రొఫైల్ను జోడించాలా?"</string>
<string name="user_add_user_message_setup" msgid="6136074867764164790">"మీరు కొత్త ప్రొఫైల్ను క్రియేట్ చేసిన తర్వాత, ఆ వ్యక్తి దాన్ని వారి కోసం అనుకూలంగా మార్చుకోవాలి."</string>
- <string name="user_add_user_message_update" msgid="3310089453006545712">"అన్ని ఇతర ప్రొఫైల్ల ఉపయోగం కోసం యాప్లను ఏదైనా ఇతర ప్రొఫైల్ నుండి అప్డేట్ చేయవచ్చు."</string>
+ <string name="user_add_user_message_update" msgid="3310089453006545712">"అన్ని ఇతర ప్రొఫైళ్ల ఉపయోగం కోసం యాప్లను ఏదైనా ఇతర ప్రొఫైల్ నుండి అప్డేట్ చేయవచ్చు."</string>
<string name="profile_limit_reached_title" msgid="7891779218496729653">"ప్రొఫైల్ పరిమితిని చేరుకున్నారు"</string>
<plurals name="profile_limit_reached_message" formatted="false" msgid="910608128912533066">
<item quantity="other">మీరు గరిష్టంగా <xliff:g id="COUNT">%d</xliff:g> ప్రొఫైల్స్ను జోడించవచ్చు.</item>
@@ -55,6 +55,6 @@
<string name="qc_footer_network_internet_settings" msgid="2480582764252681575">"నెట్వర్క్ & ఇంటర్నెట్ సెట్టింగ్లు"</string>
<string name="qc_footer_display_settings" msgid="2950539240110437704">"ప్రదర్శన సెట్టింగ్లు"</string>
<string name="qc_footer_network_sound_settings" msgid="5117011034908775097">"సౌండ్ సెట్టింగ్లు"</string>
- <string name="qc_footer_profiles_accounts_settings" msgid="4456419248123950232">"ప్రొఫైల్లు & ఖాతాల సెట్టింగ్లు"</string>
+ <string name="qc_footer_profiles_accounts_settings" msgid="4456419248123950232">"ప్రొఫైళ్లు & ఖాతాల సెట్టింగ్లు"</string>
<string name="lockpattern_does_not_support_rotary" msgid="4605787900312103476">"ఆకృతి రోటరీకి సపోర్ట్ చేయదు; \'తాకండి\'"</string>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d48c9ba..44d268d 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -20,6 +20,7 @@
<color name="car_user_switcher_name_text_color">@*android:color/car_body1_light</color>
<color name="car_user_switcher_add_user_background_color">#131313</color>
<color name="car_user_switcher_add_user_add_sign_color">@*android:color/car_body1_light</color>
+ <color name="car_managed_device_icon_color">@android:color/white</color>
<color name="car_nav_icon_fill_color">#8F8F8F</color>
<color name="car_nav_icon_fill_color_selected">#ffffff</color>
<!-- colors for seekbar -->
diff --git a/res/values/config.xml b/res/values/config.xml
index a6b7736..03b6746 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -117,6 +117,7 @@
<item>@string/config_notificationPanelViewMediator</item>
<item>com.android.systemui.car.hvac.HvacPanelOverlayViewMediator</item>
<item>com.android.systemui.car.keyguard.CarKeyguardViewMediator</item>
+ <item>com.android.systemui.car.systemdialogs.SystemDialogsViewMediator</item>
<item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item>
<item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item>
</string-array>
@@ -183,6 +184,7 @@
<item>android.server.wm.app</item>
<item>android.server.wm.cts</item>
<item>android.server.wm.second</item>
+ <item>com.android.cts.verifier</item>
</string-array>
<!-- Specifies the component name of the app grid activity -->
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 5439b38..14999c1 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -101,6 +101,8 @@
<dimen name="car_keyline_2">96dp</dimen>
<dimen name="car_keyline_3">128dp</dimen>
+ <dimen name="device_management_dialog_subtitle_padding">20dp</dimen>
+
<!-- Height of icons in Privacy chip dialog. Both App Op icon and application icon -->
<dimen name="privacy_chip_dialog_icon_height">48dp</dimen>
<!-- Icon size for Privacy chip -->
diff --git a/res/values/overlayable.xml b/res/values/overlayable.xml
index 07bc358..cbe8929 100644
--- a/res/values/overlayable.xml
+++ b/res/values/overlayable.xml
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
-<!-- Copyright (C) 2021 The Android Open Source Project
+<!-- Copyright (C) 2022 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@@ -98,6 +98,7 @@
<item type="color" name="activity_blocking_activity_background"/>
<item type="color" name="blocking_text"/>
<item type="color" name="car_accent"/>
+ <item type="color" name="car_managed_device_icon_color"/>
<item type="color" name="car_nav_icon_fill_color"/>
<item type="color" name="car_nav_icon_fill_color_selected"/>
<item type="color" name="car_nav_unseen_indicator_color"/>
@@ -244,6 +245,7 @@
<item type="dimen" name="car_volume_item_seekbar_margin_vertical"/>
<item type="dimen" name="car_volume_item_seekbar_padding_vertical"/>
<item type="dimen" name="close_handle_underlap"/>
+ <item type="dimen" name="device_management_dialog_subtitle_padding"/>
<item type="dimen" name="hvac_button_margin_bottom"/>
<item type="dimen" name="hvac_button_margin_end"/>
<item type="dimen" name="hvac_button_margin_start"/>
@@ -385,6 +387,7 @@
<item type="drawable" name="car_ic_hvac"/>
<item type="drawable" name="car_ic_hvac_selected"/>
<item type="drawable" name="car_ic_keyboard_arrow_down"/>
+ <item type="drawable" name="car_ic_managed_device"/>
<item type="drawable" name="car_ic_music"/>
<item type="drawable" name="car_ic_music_mute"/>
<item type="drawable" name="car_ic_music_selected"/>
@@ -467,6 +470,9 @@
<item type="id" name="active_background"/>
<item type="id" name="auto_button"/>
<item type="id" name="bottom_guideline"/>
+ <item type="id" name="ca_certs_disclosures"/>
+ <item type="id" name="ca_certs_subtitle"/>
+ <item type="id" name="ca_certs_warning"/>
<item type="id" name="car_nav_button_icon"/>
<item type="id" name="car_nav_button_icon_image"/>
<item type="id" name="car_nav_button_more_icon"/>
@@ -480,6 +486,9 @@
<item type="id" name="dark_icon"/>
<item type="id" name="dark_muted_icon"/>
<item type="id" name="defroster_button"/>
+ <item type="id" name="device_management_disclosures"/>
+ <item type="id" name="device_management_subtitle"/>
+ <item type="id" name="device_management_warning"/>
<item type="id" name="direction_defrost"/>
<item type="id" name="direction_face"/>
<item type="id" name="direction_face_and_floor"/>
@@ -520,8 +529,10 @@
<item type="id" name="lock_screen_nav_buttons"/>
<item type="id" name="manage_button"/>
<item type="id" name="max_width_guideline"/>
- <item type="id" name="mic_privacy_panel"/>
<item type="id" name="nav_buttons"/>
+ <item type="id" name="network_logging_disclosures"/>
+ <item type="id" name="network_logging_subtitle"/>
+ <item type="id" name="network_logging_warning"/>
<item type="id" name="note"/>
<item type="id" name="notification_container"/>
<item type="id" name="notification_panel_stub"/>
@@ -532,11 +543,17 @@
<item type="id" name="phone_nav"/>
<item type="id" name="primary_icon"/>
<item type="id" name="privacy_chip"/>
+ <item type="id" name="qc_bluetooth_footer_button"/>
+ <item type="id" name="qc_connectivity_footer_button"/>
+ <item type="id" name="qc_display_footer_button"/>
<item type="id" name="qc_entry_points_container"/>
+ <item type="id" name="qc_mic_footer_button"/>
+ <item type="id" name="qc_profile_footer_button"/>
<item type="id" name="qs_car_top_bar"/>
<item type="id" name="read_only_icons_container"/>
<item type="id" name="rear_defroster_button"/>
<item type="id" name="recycle_air_button"/>
+ <item type="id" name="scrollView"/>
<item type="id" name="seat_heat_level_button_left"/>
<item type="id" name="seat_heat_level_button_right"/>
<item type="id" name="segment_container"/>
@@ -558,6 +575,9 @@
<item type="id" name="user_switching_dialog_stub"/>
<item type="id" name="volume_list"/>
<item type="id" name="volume_seek_bar"/>
+ <item type="id" name="vpn_disclosures"/>
+ <item type="id" name="vpn_subtitle"/>
+ <item type="id" name="vpn_warning"/>
<item type="id" name="wifi_airplane_spacer"/>
<item type="id" name="wifi_combo"/>
<item type="id" name="wifi_group"/>
@@ -610,6 +630,7 @@
<item type="layout" name="car_volume_dialog"/>
<item type="layout" name="car_volume_item"/>
<item type="layout" name="default_status_icon"/>
+ <item type="layout" name="device_management_dialog"/>
<item type="layout" name="fan_direction"/>
<item type="layout" name="fan_speed"/>
<item type="layout" name="hvac_panel_container"/>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7d3fa00..619fd61 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -81,10 +81,7 @@
<style name="HvacTemperatureControlIcon">
<item name="android:scaleType">center</item>
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">@dimen/temperature_control_arrow_height</item>
<item name="android:layout_centerHorizontal">true</item>
- <item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
</style>
<style name="HvacFanSpeedBar">
diff --git a/src/com/android/systemui/car/CarServiceProvider.java b/src/com/android/systemui/car/CarServiceProvider.java
index 5778d66..69c6b42 100644
--- a/src/com/android/systemui/car/CarServiceProvider.java
+++ b/src/com/android/systemui/car/CarServiceProvider.java
@@ -19,8 +19,10 @@
import android.car.Car;
import android.content.Context;
+import androidx.annotation.AnyThread;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.annotations.GuardedBy;
import com.android.systemui.dagger.SysUISingleton;
import java.util.ArrayList;
@@ -33,7 +35,18 @@
public class CarServiceProvider {
private final Context mContext;
+ /**
+ * mListeners is guarded by itself - when requiring locks on both mListeners and mCar, always
+ * obtain the car lock before the listeners.
+ */
+ @GuardedBy("mListeners")
private final List<CarServiceOnConnectedListener> mListeners = new ArrayList<>();
+ private final Object mCarLock = new Object();
+ /**
+ * mCar is guarded by mCarLock - when requiring locks on both mListeners and mCar, always
+ * obtain the car lock before the listeners.
+ */
+ @GuardedBy("mCarLock")
private Car mCar;
@Inject
@@ -41,16 +54,18 @@
mContext = context;
mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
(car, ready) -> {
- mCar = car;
-
- synchronized (mListeners) {
- for (CarServiceOnConnectedListener listener : mListeners) {
+ synchronized (mCarLock) {
+ synchronized (mListeners) {
+ mCar = car;
if (ready) {
- listener.onConnected(mCar);
+ for (CarServiceOnConnectedListener listener : mListeners) {
+ listener.onConnected(mCar);
+ }
}
}
}
});
+
}
@VisibleForTesting
@@ -63,11 +78,16 @@
* Let's other components hook into the connection to the car service. If we're already
* connected to the car service, the callback is immediately triggered.
*/
+ @AnyThread
public void addListener(CarServiceOnConnectedListener listener) {
- if (mCar.isConnected()) {
- listener.onConnected(mCar);
+ synchronized (mCarLock) {
+ if (mCar.isConnected()) {
+ listener.onConnected(mCar);
+ }
}
- mListeners.add(listener);
+ synchronized (mListeners) {
+ mListeners.add(listener);
+ }
}
/**
diff --git a/src/com/android/systemui/car/qc/ProfileSwitcher.java b/src/com/android/systemui/car/qc/ProfileSwitcher.java
index 920bd7d..8f0a85f 100644
--- a/src/com/android/systemui/car/qc/ProfileSwitcher.java
+++ b/src/com/android/systemui/car/qc/ProfileSwitcher.java
@@ -16,6 +16,7 @@
package com.android.systemui.car.qc;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
+import static android.provider.Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS;
import static android.view.WindowInsets.Type.statusBars;
import static com.android.car.ui.utils.CarUiUtils.drawableToBitmap;
@@ -24,6 +25,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
import android.car.Car;
import android.car.user.CarUserManager;
import android.car.user.UserCreationResult;
@@ -43,6 +45,7 @@
import android.view.Window;
import android.view.WindowManager;
+import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
@@ -67,6 +70,7 @@
private static final int TIMEOUT_MS = CarProperties.user_hal_timeout().orElse(5_000) + 500;
private final UserManager mUserManager;
+ private final DevicePolicyManager mDevicePolicyManager;
private final UserIconProvider mUserIconProvider;
private final Car mCar;
private final CarUserManager mCarUserManager;
@@ -75,15 +79,18 @@
public ProfileSwitcher(Context context) {
super(context);
mUserManager = context.getSystemService(UserManager.class);
+ mDevicePolicyManager = context.getSystemService(DevicePolicyManager.class);
mUserIconProvider = new UserIconProvider();
- mCar = Car.createCar(mContext);
+ mCar = Car.createCar(context);
mCarUserManager = (CarUserManager) mCar.getCarManager(Car.CAR_USER_SERVICE);
}
@VisibleForTesting
- ProfileSwitcher(Context context, UserManager userManager, CarUserManager carUserManager) {
+ ProfileSwitcher(Context context, UserManager userManager,
+ DevicePolicyManager devicePolicyManager, CarUserManager carUserManager) {
super(context);
mUserManager = userManager;
+ mDevicePolicyManager = devicePolicyManager;
mUserIconProvider = new UserIconProvider();
mCar = null;
mCarUserManager = carUserManager;
@@ -93,6 +100,11 @@
public QCItem getQCItem() {
QCList.Builder listBuilder = new QCList.Builder();
+ if (mDevicePolicyManager.isDeviceManaged()
+ || mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) {
+ listBuilder.addRow(createOrganizationOwnedDeviceRow());
+ }
+
int fgUserId = ActivityManager.getCurrentUser();
UserHandle fgUserHandle = UserHandle.of(fgUserId);
// If the foreground user CANNOT switch to other users, only display the foreground user.
@@ -106,7 +118,9 @@
listBuilder.addRow(createUserProfileRow(profile));
}
listBuilder.addRow(createGuestProfileRow());
- listBuilder.addRow(createAddProfileRow());
+ if (!hasAddUserRestriction(fgUserHandle)) {
+ listBuilder.addRow(createAddProfileRow());
+ }
return listBuilder.build();
}
@@ -125,6 +139,29 @@
.collect(Collectors.toList());
}
+ private QCRow createOrganizationOwnedDeviceRow() {
+ Icon icon = Icon.createWithBitmap(
+ drawableToBitmap(mContext.getDrawable(R.drawable.car_ic_managed_device)));
+ QCRow row = new QCRow.Builder()
+ .setIcon(icon)
+ .setSubtitle(mContext.getString(R.string.do_disclosure_generic))
+ .build();
+ row.setActionHandler(new QCItem.ActionHandler() {
+ @Override
+ public void onAction(@NonNull QCItem item, @NonNull Context context,
+ @NonNull Intent intent) {
+ mContext.startActivityAsUser(new Intent(ACTION_ENTERPRISE_PRIVACY_SETTINGS),
+ UserHandle.CURRENT);
+ }
+
+ @Override
+ public boolean isActivity() {
+ return true;
+ }
+ });
+ return row;
+ }
+
private QCRow createUserProfileRow(UserInfo userInfo) {
QCItem.ActionHandler actionHandler = (item, context, intent) -> {
if (mPendingUserAdd) {
@@ -134,7 +171,7 @@
};
return createProfileRow(userInfo.name,
- mUserIconProvider.getRoundedUserIcon(userInfo, mContext), actionHandler);
+ mUserIconProvider.getDrawableWithBadge(mContext, userInfo), actionHandler);
}
private QCRow createGuestProfileRow() {
@@ -165,7 +202,8 @@
}
};
- return createProfileRow(mContext.getString(R.string.car_add_user), getCircularAddUserIcon(),
+ return createProfileRow(mContext.getString(R.string.car_add_user),
+ mUserIconProvider.getDrawableWithBadge(mContext, getCircularAddUserIcon()),
actionHandler);
}
@@ -248,6 +286,10 @@
return circleIcon;
}
+ private boolean hasAddUserRestriction(UserHandle userHandle) {
+ return mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_ADD_USER, userHandle);
+ }
+
private int getMaxSupportedRealUsers() {
int maxSupportedUsers = UserManager.getMaxSupportedUsers();
if (UserManager.isHeadlessSystemUserMode()) {
diff --git a/src/com/android/systemui/car/statusicon/StatusIconPanelController.java b/src/com/android/systemui/car/statusicon/StatusIconPanelController.java
index 0b71756..feb3233 100644
--- a/src/com/android/systemui/car/statusicon/StatusIconPanelController.java
+++ b/src/com/android/systemui/car/statusicon/StatusIconPanelController.java
@@ -16,14 +16,16 @@
package com.android.systemui.car.statusicon;
-import static android.content.Intent.ACTION_USER_FOREGROUND;
import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
import static android.widget.ListPopupWindow.WRAP_CONTENT;
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.LayoutRes;
+import android.app.PendingIntent;
+import android.car.Car;
import android.car.drivingstate.CarUxRestrictions;
+import android.car.user.CarUserManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -42,6 +44,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
+import com.android.car.qc.QCItem;
+import com.android.car.qc.view.QCView;
import com.android.car.ui.FocusParkingView;
import com.android.car.ui.utils.CarUxRestrictionsUtil;
import com.android.car.ui.utils.ViewUtils;
@@ -58,8 +62,6 @@
*/
public class StatusIconPanelController {
private static final int DEFAULT_POPUP_WINDOW_ANCHOR_GRAVITY = Gravity.TOP | Gravity.START;
- private static final IntentFilter INTENT_FILTER_USER_CHANGED = new IntentFilter(
- ACTION_USER_FOREGROUND);
private final Context mContext;
private final String mIdentifier;
@@ -77,10 +79,10 @@
private ImageView mStatusIconView;
private CarUxRestrictionsUtil mCarUxRestrictionsUtil;
private float mDimValue = -1.0f;
+ private boolean mUserSwitchEventRegistered;
- private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
+ private final CarUserManager.UserLifecycleListener mUserLifecycleListener = event -> {
+ if (event.getEventType() == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
reset();
}
};
@@ -100,7 +102,7 @@
public void onRestrictionsChanged(@NonNull CarUxRestrictions carUxRestrictions) {
if (mIsDisabledWhileDriving
&& carUxRestrictions.isRequiresDistractionOptimization()
- && mPanel != null) {
+ && isPanelShowing()) {
mPanel.dismiss();
}
}
@@ -114,7 +116,7 @@
intent.getIdentifier() != null && intent.getIdentifier().equals(mIdentifier);
if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action) && !isIntentFromSelf
- && mPanel != null && mPanel.isShowing()) {
+ && isPanelShowing()) {
mPanel.dismiss();
}
}
@@ -122,7 +124,7 @@
private final ViewTreeObserver.OnGlobalFocusChangeListener mFocusChangeListener =
(oldFocus, newFocus) -> {
- if (mPanel != null && oldFocus != null && newFocus instanceof FocusParkingView) {
+ if (isPanelShowing() && oldFocus != null && newFocus instanceof FocusParkingView) {
// When nudging out of the panel, RotaryService will focus on the
// FocusParkingView to clear the focus highlight. When this occurs, dismiss the
// panel.
@@ -130,6 +132,21 @@
}
};
+ private final QCView.QCActionListener mQCActionListener = (item, action) -> {
+ if (!isPanelShowing()) {
+ return;
+ }
+ if (action instanceof PendingIntent) {
+ if (((PendingIntent) action).isActivity()) {
+ mPanel.dismiss();
+ }
+ } else if (action instanceof QCItem.ActionHandler) {
+ if (((QCItem.ActionHandler) action).isActivity()) {
+ mPanel.dismiss();
+ }
+ }
+ };
+
public StatusIconPanelController(
Context context,
CarServiceProvider carServiceProvider,
@@ -164,8 +181,14 @@
UserHandle.ALL);
configurationController.addCallback(mConfigurationListener);
- context.registerReceiverForAllUsers(mUserChangeReceiver, INTENT_FILTER_USER_CHANGED,
- /* broadcastPermission= */ null, /* scheduler= */ null);
+ carServiceProvider.addListener(car -> {
+ CarUserManager carUserManager = (CarUserManager) car.getCarManager(
+ Car.CAR_USER_SERVICE);
+ if (!mUserSwitchEventRegistered) {
+ carUserManager.addListener(Runnable::run, mUserLifecycleListener);
+ mUserSwitchEventRegistered = true;
+ }
+ });
mIsDisabledWhileDriving = isDisabledWhileDriving;
if (mIsDisabledWhileDriving) {
@@ -405,7 +428,9 @@
for (int i = 0; i < rootView.getChildCount(); i++) {
View v = rootView.getChildAt(i);
if (v instanceof SystemUIQCView) {
- mQCViews.add((SystemUIQCView) v);
+ SystemUIQCView qcv = (SystemUIQCView) v;
+ mQCViews.add(qcv);
+ qcv.setActionListener(mQCActionListener);
} else if (v instanceof ViewGroup) {
this.findQcViews((ViewGroup) v);
}
@@ -428,4 +453,8 @@
isHighlighted ? mIconHighlightedColor : mIconNotHighlightedColor);
}
}
+
+ private boolean isPanelShowing() {
+ return mPanel != null && mPanel.isShowing();
+ }
}
diff --git a/src/com/android/systemui/car/statusicon/ui/LocationStatusIconController.java b/src/com/android/systemui/car/statusicon/ui/LocationStatusIconController.java
index 5ceeb65..2c49007 100644
--- a/src/com/android/systemui/car/statusicon/ui/LocationStatusIconController.java
+++ b/src/com/android/systemui/car/statusicon/ui/LocationStatusIconController.java
@@ -16,9 +16,9 @@
package com.android.systemui.car.statusicon.ui;
-import static android.content.Intent.ACTION_USER_FOREGROUND;
-
import android.app.ActivityManager;
+import android.car.Car;
+import android.car.user.CarUserManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,6 +26,7 @@
import android.content.res.Resources;
import android.location.LocationManager;
import android.os.UserHandle;
+import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.car.CarServiceProvider;
@@ -42,14 +43,13 @@
private static final String TAG = LocationStatusIconController.class.getSimpleName();
private static final IntentFilter INTENT_FILTER_LOCATION_MODE_CHANGED = new IntentFilter(
LocationManager.MODE_CHANGED_ACTION);
- private static final IntentFilter INTENT_FILTER_USER_CHANGED = new IntentFilter(
- ACTION_USER_FOREGROUND);
private final Context mContext;
private final LocationManager mLocationManager;
private final CarServiceProvider mCarServiceProvider;
private boolean mIsLocationActive;
+ private boolean mUserLifecycleListenerRegistered;
private final BroadcastReceiver mLocationReceiver = new BroadcastReceiver() {
@Override
@@ -58,12 +58,18 @@
}
};
- private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- updateIconVisibilityForCurrentUser();
- }
- };
+ private final CarUserManager.UserLifecycleListener mUserLifecycleListener =
+ new CarUserManager.UserLifecycleListener() {
+ @Override
+ public void onEvent(CarUserManager.UserLifecycleEvent event) {
+ mContext.getMainExecutor().execute(() -> {
+ if (event.getEventType()
+ == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
+ updateIconVisibilityForCurrentUser();
+ }
+ });
+ }
+ };
@Inject
LocationStatusIconController(
@@ -77,9 +83,7 @@
context.registerReceiverForAllUsers(mLocationReceiver, INTENT_FILTER_LOCATION_MODE_CHANGED,
/* broadcastPermission= */ null, /* scheduler= */ null);
- context.registerReceiverForAllUsers(mUserChangeReceiver, INTENT_FILTER_USER_CHANGED,
- /* broadcastPermission= */ null,
- /* scheduler= */ null);
+ registerForUserChangeEvents();
setIconDrawableToDisplay(resources.getDrawable(R.drawable.ic_location, context.getTheme()));
updateIconVisibilityForCurrentUser();
}
@@ -96,4 +100,17 @@
mIsLocationActive = mLocationManager.isLocationEnabledForUser(fgUserHandle);
updateStatus();
}
+
+ private void registerForUserChangeEvents() {
+ mCarServiceProvider.addListener(car -> {
+ CarUserManager carUserManager = (CarUserManager) car.getCarManager(
+ Car.CAR_USER_SERVICE);
+ if (carUserManager != null && !mUserLifecycleListenerRegistered) {
+ carUserManager.addListener(Runnable::run, mUserLifecycleListener);
+ mUserLifecycleListenerRegistered = true;
+ } else {
+ Log.e(TAG, "CarUserManager could not be obtained.");
+ }
+ });
+ }
}
diff --git a/src/com/android/systemui/car/systembar/ButtonSelectionStateController.java b/src/com/android/systemui/car/systembar/ButtonSelectionStateController.java
index 3c4b604..19e5846 100644
--- a/src/com/android/systemui/car/systembar/ButtonSelectionStateController.java
+++ b/src/com/android/systemui/car/systembar/ButtonSelectionStateController.java
@@ -19,6 +19,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
import android.app.ActivityTaskManager;
import android.app.ActivityTaskManager.RootTaskInfo;
@@ -29,7 +30,6 @@
import android.content.pm.ResolveInfo;
import android.os.RemoteException;
import android.util.Log;
-import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
@@ -115,7 +115,7 @@
// Find the first stack info with a topActivity in the primary display.
// TODO: We assume that CarFacetButton will launch an app only in the primary display.
// We need to extend the functionality to handle the multiple display properly.
- if (taskInfo.topActivity != null && taskInfo.displayId == validDisplay) {
+ if (taskInfo.topActivity != null && taskInfo.displayAreaFeatureId == validDisplay) {
validTaskInfo = taskInfo;
break;
}
@@ -153,7 +153,7 @@
* @param taskInfoList
*/
protected void taskChanged(List<RootTaskInfo> taskInfoList) {
- taskChanged(taskInfoList, Display.DEFAULT_DISPLAY);
+ taskChanged(taskInfoList, FEATURE_DEFAULT_TASK_CONTAINER);
}
/**
diff --git a/src/com/android/systemui/car/systembar/CarSystemBar.java b/src/com/android/systemui/car/systembar/CarSystemBar.java
index bdc6d05..f423489 100644
--- a/src/com/android/systemui/car/systembar/CarSystemBar.java
+++ b/src/com/android/systemui/car/systembar/CarSystemBar.java
@@ -258,7 +258,7 @@
mActivityManagerWrapper.registerTaskStackListener(new TaskStackChangeListener() {
@Override
public void onLockTaskModeChanged(int mode) {
- mCarSystemBarController.refreshSystemBarByLockTaskFeatures();
+ mCarSystemBarController.refreshSystemBar();
}
});
@@ -477,7 +477,7 @@
mAppearanceRegions = appearanceRegions;
updateStatusBarAppearance();
}
- mCarSystemBarController.refreshSystemBarByLockTaskFeatures();
+ mCarSystemBarController.refreshSystemBar();
}
@Override
@@ -486,7 +486,7 @@
if (displayId != mDisplayId) {
return;
}
- mCarSystemBarController.setStatusBarState(state1);
+ mCarSystemBarController.setSystemBarStates(state1, state2);
}
private void updateStatusBarAppearance() {
diff --git a/src/com/android/systemui/car/systembar/CarSystemBarController.java b/src/com/android/systemui/car/systembar/CarSystemBarController.java
index d8ae28a..0fcdebd 100644
--- a/src/com/android/systemui/car/systembar/CarSystemBarController.java
+++ b/src/com/android/systemui/car/systembar/CarSystemBarController.java
@@ -85,7 +85,11 @@
private CarSystemBarView mLeftView;
private CarSystemBarView mRightView;
+ // Saved StatusBarManager.DisableFlags
private int mStatusBarState;
+ // Saved StatusBarManager.Disable2Flags
+ private int mStatusBarState2;
+ private int mLockTaskMode;
@Inject
public CarSystemBarController(Context context,
@@ -208,19 +212,27 @@
}
/**
- * Sets the status bar state and refreshes the system bar when the state was changed.
+ * Sets the system bar states - {@code StatusBarManager.DisableFlags},
+ * {@code StatusBarManager.Disable2Flags}, lock task mode. When there is a change in state,
+ * and refreshes the system bars.
+ *
+ * @param state {@code StatusBarManager.DisableFlags}
+ * @param state2 {@code StatusBarManager.Disable2Flags}
*/
- public void setStatusBarState(int state) {
- int diff = state ^ mStatusBarState;
- if (diff == 0) {
+ public void setSystemBarStates(int state, int state2) {
+ int diff = (state ^ mStatusBarState) | (state2 ^ mStatusBarState2);
+ int lockTaskMode = getLockTaskModeState();
+ if (diff == 0 && mLockTaskMode == lockTaskMode) {
if (DEBUG) {
- Log.d(TAG, "setStatusBarState(): status bar states unchanged: "
- + state);
+ Log.d(TAG, "setSystemBarStates(): status bar states unchanged: state: "
+ + state + " state2: " + state2 + " lockTaskMode: " + mLockTaskMode);
}
return;
}
mStatusBarState = state;
- refreshSystemBarByLockTaskFeatures();
+ mStatusBarState2 = state2;
+ mLockTaskMode = lockTaskMode;
+ refreshSystemBar();
}
@VisibleForTesting
@@ -228,47 +240,77 @@
return mStatusBarState;
}
+ @VisibleForTesting
+ protected int getStatusBarState2() {
+ return mStatusBarState2;
+ }
+
+ @VisibleForTesting
+ protected int getLockTaskMode() {
+ return mLockTaskMode;
+ }
+
/**
- * Refreshes certain system bar views when lock task mode feature is changed based on
- * {@link StatusBarManager} flags.
+ * Refreshes system bar views and sets the visibility of certain components based on
+ * {@link StatusBarManager} flags and lock task mode.
+ * <ul>
+ * <li>Home button will be disabled when {@code StatusBarManager.DISABLE_HOME} is set.
+ * <li>Phone call button will be disable in lock task mode.
+ * <li>App grid button will be disable when {@code StatusBarManager.DISABLE_HOME} is set.
+ * <li>Notification button will be disable when
+ * {@code StatusBarManager.DISABLE_NOTIFICATION_ICONS} is set.
+ * <li>Quick settings and user switcher will be hidden when in lock task mode or when
+ * {@code StatusBarManager.DISABLE2_QUICK_SETTINGS} is set.
+ * </ul>
*/
- public void refreshSystemBarByLockTaskFeatures() {
- boolean locked = isLockTaskModeLocked();
- int lockTaskModeVisibility = locked ? View.GONE : View.VISIBLE;
+ public void refreshSystemBar() {
boolean homeDisabled = ((mStatusBarState & StatusBarManager.DISABLE_HOME) > 0);
boolean notificationDisabled =
((mStatusBarState & StatusBarManager.DISABLE_NOTIFICATION_ICONS) > 0);
+ boolean locked = (mLockTaskMode == ActivityManager.LOCK_TASK_MODE_LOCKED);
+ boolean qcDisabled =
+ ((mStatusBarState2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) > 0) || locked;
+ boolean systemIconsDisabled =
+ ((mStatusBarState2 & StatusBarManager.DISABLE2_SYSTEM_ICONS) > 0) || locked;
+
+ setDisabledSystemBarButton(R.id.home, homeDisabled, "home");
+ setDisabledSystemBarButton(R.id.phone_nav, locked, "phone_nav");
+ setDisabledSystemBarButton(R.id.grid_nav, homeDisabled, "grid_nav");
+ setDisabledSystemBarButton(R.id.notifications, notificationDisabled, "notifications");
+
+ setDisabledSystemBarContainer(R.id.qc_entry_points_container, qcDisabled,
+ "qc_entry_points_container");
+ setDisabledSystemBarContainer(R.id.user_name_container, qcDisabled,
+ "user_name_container");
+ setDisabledSystemBarContainer(R.id.read_only_icons_container, systemIconsDisabled,
+ "read_only_icons_container");
+
if (DEBUG) {
- Log.d(TAG, "refreshSystemBarByLockTaskFeatures: locked?: " + locked
+ Log.d(TAG, "refreshSystemBar: locked?: " + locked
+ " homeDisabled: " + homeDisabled
- + " notificationDisabled: " + notificationDisabled);
- }
-
- setLockTaskDisabledContainer(R.id.qc_entry_points_container, lockTaskModeVisibility);
- setLockTaskDisabledContainer(R.id.user_name_container, lockTaskModeVisibility);
-
- // Check navigation icons
- setLockTaskDisabledButton(R.id.home, homeDisabled);
- setLockTaskDisabledButton(R.id.phone_nav, locked);
- setLockTaskDisabledButton(R.id.grid_nav, homeDisabled);
- setLockTaskDisabledButton(R.id.notifications, notificationDisabled);
- }
-
- private boolean isLockTaskModeLocked() {
- return mContext.getSystemService(ActivityManager.class).getLockTaskModeState()
- == ActivityManager.LOCK_TASK_MODE_LOCKED;
- }
-
- private void setLockTaskDisabledButton(int viewId, boolean disabled) {
- for (CarSystemBarView barView : getAllAvailableSystemBarViews()) {
- barView.setLockTaskDisabledButton(viewId, disabled, () ->
- showAdminSupportDetailsDialog());
+ + " notificationDisabled: " + notificationDisabled
+ + " qcDisabled: " + qcDisabled
+ + " systemIconsDisabled: " + systemIconsDisabled);
}
}
- private void setLockTaskDisabledContainer(int viewId, @View.Visibility int visibility) {
+ private int getLockTaskModeState() {
+ return mContext.getSystemService(ActivityManager.class).getLockTaskModeState();
+ }
+
+ private void setDisabledSystemBarButton(int viewId, boolean disabled,
+ @Nullable String buttonName) {
for (CarSystemBarView barView : getAllAvailableSystemBarViews()) {
- barView.setLockTaskDisabledContainer(viewId, visibility);
+ barView.setDisabledSystemBarButton(viewId, disabled,
+ () -> showAdminSupportDetailsDialog(), buttonName);
+ }
+ }
+
+ private void setDisabledSystemBarContainer(int viewId, boolean disabled,
+ @Nullable String viewName) {
+ for (CarSystemBarView barView : getAllAvailableSystemBarViews()) {
+ barView.setVisibilityByViewId(viewId, viewName,
+ disabled ? View.INVISIBLE : View.VISIBLE);
}
}
diff --git a/src/com/android/systemui/car/systembar/CarSystemBarView.java b/src/com/android/systemui/car/systembar/CarSystemBarView.java
index 9d2def2..ea4bafb 100644
--- a/src/com/android/systemui/car/systembar/CarSystemBarView.java
+++ b/src/com/android/systemui/car/systembar/CarSystemBarView.java
@@ -17,6 +17,7 @@
package com.android.systemui.car.systembar;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
@@ -51,6 +52,7 @@
}
private static final String TAG = CarSystemBarView.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int BUTTON_TYPE_NAVIGATION = 0;
public static final int BUTTON_TYPE_KEYGUARD = 1;
@@ -241,30 +243,28 @@
/**
* Sets the system bar view's disabled state and runnable when disabled.
*/
- public void setLockTaskDisabledButton(int viewId, boolean disabled, Runnable runnable) {
+ public void setDisabledSystemBarButton(int viewId, boolean disabled, Runnable runnable,
+ @Nullable String buttonName) {
CarSystemBarButton button = findViewById(viewId);
if (button != null) {
+ if (DEBUG) {
+ Log.d(TAG, "setDisabledSystemBarButton for: " + buttonName + " to: " + disabled);
+ }
button.setDisabled(disabled, runnable);
}
}
/**
- * Sets the system bar ViewGroup container's visibility
+ * Sets the system bar specific View container's visibility. ViewName is used just for
+ * debugging.
*/
- public void setLockTaskDisabledContainer(int viewId, @View.Visibility int visibility) {
+ public void setVisibilityByViewId(int viewId, @Nullable String viewName,
+ @View.Visibility int visibility) {
View v = findViewById(viewId);
- if (v == null) {
- Log.e(TAG, "setLockTaskViewVisibility for: " + viewId + " not found");
- return;
+ if (v != null) {
+ if (DEBUG) Log.d(TAG, "setVisibilityByViewId for: " + viewName + " to: " + visibility);
+ v.setVisibility(visibility);
}
- if (v instanceof ViewGroup) {
- ViewGroup group = (ViewGroup) v;
- for (int i = 0; i < group.getChildCount(); i++) {
- group.getChildAt(i).setVisibility(visibility);
- }
- return;
- }
- v.setVisibility(visibility);
}
/**
diff --git a/src/com/android/systemui/car/systembar/PrivacyChipViewController.java b/src/com/android/systemui/car/systembar/PrivacyChipViewController.java
index 48a5b4d..f6a8ee7 100644
--- a/src/com/android/systemui/car/systembar/PrivacyChipViewController.java
+++ b/src/com/android/systemui/car/systembar/PrivacyChipViewController.java
@@ -19,6 +19,8 @@
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
import static android.hardware.SensorPrivacyManager.Sources.QS_TILE;
+import android.car.Car;
+import android.car.user.CarUserManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -64,6 +66,7 @@
private Context mContext;
private MicPrivacyChip mPrivacyChip;
+ private CarUserManager mCarUserManager;
private Runnable mQsTileNotifyUpdateRunnable;
private final SensorPrivacyManager.OnSensorPrivacyChangedListener
mOnSensorPrivacyChangedListener = (sensor, sensorPrivacyEnabled) -> {
@@ -125,6 +128,7 @@
}
}
};
+ private boolean mUserLifecycleListenerRegistered;
private int mCurrentUserId;
private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() {
@Override
@@ -143,19 +147,19 @@
setUser(currentUserId);
}
};
- private final BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (mPrivacyChip == null) {
- return;
- }
- int currentUserId = mCarDeviceProvisionedController.getCurrentUser();
- if (mCurrentUserId == currentUserId) {
- return;
- }
- setUser(currentUserId);
- }
- };
+ private final CarUserManager.UserLifecycleListener mUserLifecycleListener =
+ new CarUserManager.UserLifecycleListener() {
+ @Override
+ public void onEvent(CarUserManager.UserLifecycleEvent event) {
+ if (mPrivacyChip == null) {
+ return;
+ }
+ if (event.getEventType()
+ == CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING) {
+ setUser(event.getUserHandle().getIdentifier());
+ }
+ }
+ };
@Inject
public PrivacyChipViewController(Context context, PrivacyItemController privacyItemController,
@@ -214,6 +218,7 @@
mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable();
mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable();
mPrivacyItemController.addCallback(mPicCallback);
+ mUserLifecycleListenerRegistered = false;
registerForUserChangeEvents();
setUser(mCarDeviceProvisionedController.getCurrentUser());
}
@@ -228,7 +233,12 @@
mPrivacyItemController.removeCallback(mPicCallback);
mBroadcastDispatcher.unregisterReceiver(mUserUpdateReceiver);
- mBroadcastDispatcher.unregisterReceiver(mUserChangeReceiver);
+ if (mUserLifecycleListenerRegistered) {
+ if (mCarUserManager != null) {
+ mCarUserManager.removeListener(mUserLifecycleListener);
+ }
+ mUserLifecycleListenerRegistered = false;
+ }
mSensorPrivacyManager.removeSensorPrivacyListener(MICROPHONE,
mOnSensorPrivacyChangedListener);
mPrivacyChip = null;
@@ -256,9 +266,15 @@
private void registerForUserChangeEvents() {
// Register for user switching
- IntentFilter userChangeFilter = new IntentFilter(Intent.ACTION_USER_FOREGROUND);
- mBroadcastDispatcher.registerReceiver(mUserChangeReceiver, userChangeFilter,
- /* executor= */ null, UserHandle.ALL);
+ mCarServiceProvider.addListener(car -> {
+ mCarUserManager = (CarUserManager) car.getCarManager(Car.CAR_USER_SERVICE);
+ if (mCarUserManager != null && !mUserLifecycleListenerRegistered) {
+ mCarUserManager.addListener(Runnable::run, mUserLifecycleListener);
+ mUserLifecycleListenerRegistered = true;
+ } else {
+ Log.e(TAG, "CarUserManager could not be obtained.");
+ }
+ });
// Also register for user info changing
IntentFilter filter = new IntentFilter();
diff --git a/src/com/android/systemui/car/systemdialogs/SystemDialogsViewController.java b/src/com/android/systemui/car/systemdialogs/SystemDialogsViewController.java
new file mode 100644
index 0000000..56cd49d
--- /dev/null
+++ b/src/com/android/systemui/car/systemdialogs/SystemDialogsViewController.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.systemdialogs;
+
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+import static android.view.WindowInsets.Type.statusBars;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.statusbar.policy.SecurityController;
+
+import javax.inject.Inject;
+
+/**
+ * A controller that can create and control the visibility of various system dialogs.
+ */
+@SysUISingleton
+public class SystemDialogsViewController {
+ private final Context mContext;
+ private final SecurityController mSecurityController;
+ private final AlertDialog.OnClickListener mOnDeviceMonitoringConfirmed;
+
+ @Inject
+ public SystemDialogsViewController(
+ Context context,
+ SecurityController securityController) {
+ mContext = context;
+ mSecurityController = securityController;
+ mOnDeviceMonitoringConfirmed = (dialog, which) -> {
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
+ dialog.dismiss();
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+ };
+ }
+
+ protected void showDeviceMonitoringDialog() {
+ AlertDialog dialog = new AlertDialog.Builder(mContext,
+ com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert)
+ .setView(createDialogView())
+ .setPositiveButton(R.string.ok, mOnDeviceMonitoringConfirmed)
+ .create();
+
+ applyCarSysUIDialogFlags(dialog);
+ dialog.show();
+ }
+
+ private View createDialogView() {
+ View dialogView = LayoutInflater.from(mContext)
+ .inflate(R.layout.device_management_dialog, null, false);
+
+ CharSequence deviceOwnerOrganization = mSecurityController.getDeviceOwnerOrganizationName();
+ boolean isDeviceManaged = mSecurityController.isDeviceManaged();
+
+ // device management section
+ TextView deviceManagementSubtitle =
+ dialogView.requireViewById(R.id.device_management_subtitle);
+ deviceManagementSubtitle.setText(getDeviceMonitoringTitle(deviceOwnerOrganization));
+
+ CharSequence managementMessage = getDeviceMonitoringMessage(deviceOwnerOrganization);
+ if (managementMessage == null) {
+ dialogView.requireViewById(R.id.device_management_disclosures).setVisibility(View.GONE);
+ } else {
+ dialogView.requireViewById(R.id.device_management_disclosures)
+ .setVisibility(View.VISIBLE);
+ TextView deviceManagementWarning =
+ dialogView.requireViewById(R.id.device_management_warning);
+ deviceManagementWarning.setText(managementMessage);
+ }
+
+ // CA certificate section
+ CharSequence caCertsMessage = getCaCertsMessage(isDeviceManaged);
+ if (caCertsMessage == null) {
+ dialogView.requireViewById(R.id.ca_certs_disclosures).setVisibility(View.GONE);
+ } else {
+ dialogView.requireViewById(R.id.ca_certs_disclosures).setVisibility(View.VISIBLE);
+ TextView caCertsWarning = dialogView.requireViewById(R.id.ca_certs_warning);
+ caCertsWarning.setText(caCertsMessage);
+ }
+
+ // network logging section
+ CharSequence networkLoggingMessage = getNetworkLoggingMessage(isDeviceManaged);
+ if (networkLoggingMessage == null) {
+ dialogView.requireViewById(R.id.network_logging_disclosures).setVisibility(View.GONE);
+ } else {
+ dialogView.requireViewById(R.id.network_logging_disclosures)
+ .setVisibility(View.VISIBLE);
+ TextView networkLoggingWarning =
+ dialogView.requireViewById(R.id.network_logging_warning);
+ networkLoggingWarning.setText(networkLoggingMessage);
+ }
+
+ // VPN section
+ CharSequence vpnMessage = getVpnMessage(isDeviceManaged);
+ if (vpnMessage == null) {
+ dialogView.requireViewById(R.id.vpn_disclosures).setVisibility(View.GONE);
+ } else {
+ dialogView.requireViewById(R.id.vpn_disclosures).setVisibility(View.VISIBLE);
+ TextView vpnWarning = dialogView.requireViewById(R.id.vpn_warning);
+ vpnWarning.setText(vpnMessage);
+ }
+
+ return dialogView;
+ }
+
+ private CharSequence getDeviceMonitoringTitle(CharSequence deviceOwnerOrganization) {
+ if (deviceOwnerOrganization != null && isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_title_financed_device,
+ deviceOwnerOrganization);
+ } else {
+ return mContext.getString(R.string.monitoring_title_device_owned);
+ }
+ }
+
+ private CharSequence getDeviceMonitoringMessage(CharSequence deviceOwnerOrganization) {
+ if (deviceOwnerOrganization != null) {
+ if (isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_financed_description_named_management,
+ deviceOwnerOrganization, deviceOwnerOrganization);
+ } else {
+ return mContext.getString(
+ R.string.monitoring_description_named_management, deviceOwnerOrganization);
+ }
+ }
+ return mContext.getString(R.string.monitoring_description_management);
+ }
+
+ @Nullable
+ private CharSequence getCaCertsMessage(boolean isDeviceManaged) {
+ boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
+ boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
+ if (!(hasCACerts || hasCACertsInWorkProfile)) return null;
+ if (isDeviceManaged) {
+ return mContext.getString(R.string.monitoring_description_management_ca_certificate);
+ }
+ if (hasCACertsInWorkProfile) {
+ return mContext.getString(
+ R.string.monitoring_description_managed_profile_ca_certificate);
+ }
+ return mContext.getString(R.string.monitoring_description_ca_certificate);
+ }
+
+ @Nullable
+ private CharSequence getNetworkLoggingMessage(boolean isDeviceManaged) {
+ boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
+ if (!isNetworkLoggingEnabled) return null;
+ if (isDeviceManaged) {
+ return mContext.getString(R.string.monitoring_description_management_network_logging);
+ } else {
+ return mContext.getString(
+ R.string.monitoring_description_managed_profile_network_logging);
+ }
+ }
+
+ @Nullable
+ private CharSequence getVpnMessage(boolean isDeviceManaged) {
+ boolean hasWorkProfile = mSecurityController.hasWorkProfile();
+ String vpnName = mSecurityController.getPrimaryVpnName();
+ String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
+ if (vpnName == null && vpnNameWorkProfile == null) return null;
+ if (isDeviceManaged) {
+ if (vpnName != null && vpnNameWorkProfile != null) {
+ return mContext.getString(R.string.monitoring_description_two_named_vpns,
+ vpnName, vpnNameWorkProfile);
+ } else {
+ return mContext.getString(R.string.monitoring_description_named_vpn,
+ vpnName != null ? vpnName : vpnNameWorkProfile);
+ }
+ } else {
+ if (vpnName != null && vpnNameWorkProfile != null) {
+ return mContext.getString(R.string.monitoring_description_two_named_vpns,
+ vpnName, vpnNameWorkProfile);
+ } else if (vpnNameWorkProfile != null) {
+ return mContext.getString(R.string.monitoring_description_managed_profile_named_vpn,
+ vpnNameWorkProfile);
+ } else if (hasWorkProfile) {
+ return mContext.getString(
+ R.string.monitoring_description_personal_profile_named_vpn, vpnName);
+ } else {
+ return mContext.getString(R.string.monitoring_description_named_vpn, vpnName);
+ }
+ }
+ }
+
+ private boolean isFinancedDevice() {
+ return mSecurityController.isDeviceManaged()
+ && mSecurityController.getDeviceOwnerType(
+ mSecurityController.getDeviceOwnerComponentOnAnyUser())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ }
+
+ private void applyCarSysUIDialogFlags(AlertDialog dialog) {
+ Window window = dialog.getWindow();
+ window.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+ window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
+ | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ window.getAttributes().setFitInsetsTypes(
+ window.getAttributes().getFitInsetsTypes() & ~statusBars());
+ }
+}
diff --git a/src/com/android/systemui/car/systemdialogs/SystemDialogsViewMediator.java b/src/com/android/systemui/car/systemdialogs/SystemDialogsViewMediator.java
new file mode 100644
index 0000000..727cf38
--- /dev/null
+++ b/src/com/android/systemui/car/systemdialogs/SystemDialogsViewMediator.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.car.systemdialogs;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.UserHandle;
+
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.car.window.OverlayViewMediator;
+import com.android.systemui.dagger.SysUISingleton;
+
+import javax.inject.Inject;
+
+/**
+ * A view mediator that listens to events that trigger various system dialogs.
+ *
+ * NOTE: Even though {@link com.android.systemui.statusbar.phone.StatusBar} listens to these
+ * events in core Android, a separate view mediator is created in AAOS since the event listeners
+ * need to exist independent of OEM SystemBar configuration (that is, we cannot assume the existence
+ * of any particular SystemBars in AAOS).
+ */
+@SysUISingleton
+public class SystemDialogsViewMediator implements OverlayViewMediator {
+ private final BroadcastDispatcher mBroadcastDispatcher;
+ private final SystemDialogsViewController mSystemDialogsViewController;
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) {
+ mSystemDialogsViewController.showDeviceMonitoringDialog();
+ }
+ }
+ };
+
+ @Inject
+ public SystemDialogsViewMediator(
+ BroadcastDispatcher broadcastDispatcher,
+ SystemDialogsViewController systemDialogsViewController) {
+ mBroadcastDispatcher = broadcastDispatcher;
+ mSystemDialogsViewController = systemDialogsViewController;
+ }
+
+ @Override
+ public void registerListeners() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG);
+ mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, /* executor= */ null,
+ UserHandle.ALL);
+ }
+
+ @Override
+ public void setUpOverlayContentViewControllers() {
+ // no-op.
+ }
+}
diff --git a/src/com/android/systemui/car/userswitcher/UserIconProvider.java b/src/com/android/systemui/car/userswitcher/UserIconProvider.java
index 9b938b3..679c9de 100644
--- a/src/com/android/systemui/car/userswitcher/UserIconProvider.java
+++ b/src/com/android/systemui/car/userswitcher/UserIconProvider.java
@@ -16,9 +16,7 @@
package com.android.systemui.car.userswitcher;
-import android.annotation.Nullable;
import android.annotation.UserIdInt;
-import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.content.res.Resources;
@@ -29,11 +27,9 @@
import android.os.UserManager;
import androidx.core.graphics.drawable.RoundedBitmapDrawable;
-import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
+import com.android.car.admin.ui.UserAvatarView;
import com.android.internal.util.UserIcons;
-import com.android.settingslib.Utils;
-import com.android.settingslib.drawable.UserIconDrawable;
import com.android.systemui.R;
/**
@@ -60,6 +56,51 @@
return new BitmapDrawable(res, icon);
}
+ /**
+ * Gets a user icon with badge if the user profile is managed.
+ *
+ * @param context to use for the avatar view
+ * @param userInfo User for which the icon is requested and badge is set
+ * @return {@link Drawable} with badge
+ */
+ public Drawable getDrawableWithBadge(Context context, UserInfo userInfo) {
+ return addBadge(context, getRoundedUserIcon(userInfo, context), userInfo.id);
+ }
+
+ /**
+ * Gets an icon with badge if the device is managed.
+ *
+ * @param context context
+ * @param drawable icon without badge
+ * @return {@link Drawable} with badge
+ */
+ public Drawable getDrawableWithBadge(Context context, Drawable drawable) {
+ return addBadge(context, drawable, UserHandle.USER_NULL);
+ }
+
+ private static Drawable addBadge(Context context, Drawable drawable, @UserIdInt int userId) {
+ int iconSize = drawable.getIntrinsicWidth();
+ UserAvatarView userAvatarView = new UserAvatarView(context);
+ float badgeToIconSizeRatio =
+ context.getResources().getDimension(R.dimen.car_user_switcher_managed_badge_size)
+ / context.getResources().getDimension(
+ R.dimen.car_user_switcher_image_avatar_size);
+ userAvatarView.setBadgeDiameter(iconSize * badgeToIconSizeRatio);
+ float badgePadding = context.getResources().getDimension(
+ R.dimen.car_user_switcher_managed_badge_margin);
+ userAvatarView.setBadgeMargin(badgePadding);
+ if (userId != UserHandle.USER_NULL) {
+ // When the userId is valid, add badge if the user is managed.
+ userAvatarView.setDrawableWithBadge(drawable, userId);
+ } else {
+ // When the userId is not valid, add badge if the device is managed.
+ userAvatarView.setDrawableWithBadge(drawable);
+ }
+ Drawable badgedIcon = userAvatarView.getUserIconDrawable();
+ badgedIcon.setBounds(0, 0, iconSize, iconSize);
+ return badgedIcon;
+ }
+
/** Returns a scaled, rounded, default icon for the Guest user */
public Drawable getRoundedGuestDefaultIcon(Resources resources) {
Bitmap icon = getGuestUserDefaultIcon(resources);
diff --git a/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java b/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
index 1e604bd..2e73c21 100644
--- a/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
+++ b/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewController.java
@@ -19,10 +19,10 @@
import static android.car.settings.CarSettings.Global.ENABLE_USER_SWITCH_DEVELOPER_MESSAGE;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.os.Handler;
+import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -34,7 +34,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.settingslib.drawable.CircleFramedDrawable;
import com.android.systemui.R;
import com.android.systemui.car.window.OverlayViewController;
import com.android.systemui.car.window.OverlayViewGlobalStateController;
@@ -56,8 +55,10 @@
private final Context mContext;
private final Resources mResources;
private final DelayableExecutor mMainExecutor;
+ private final ActivityManager mActivityManager;
private final UserManager mUserManager;
private final IWindowManager mWindowManagerService;
+ private final UserIconProvider mUserIconProvider = new UserIconProvider();
private final int mWindowShownTimeoutMs;
private final Runnable mWindowShownTimeoutCallback = () -> {
if (DEBUG) {
@@ -78,6 +79,7 @@
Context context,
@Main Resources resources,
@Main DelayableExecutor delayableExecutor,
+ ActivityManager activityManager,
UserManager userManager,
IWindowManager windowManagerService,
OverlayViewGlobalStateController overlayViewGlobalStateController) {
@@ -87,6 +89,7 @@
mContext = context;
mResources = resources;
mMainExecutor = delayableExecutor;
+ mActivityManager = activityManager;
mUserManager = userManager;
mWindowManagerService = windowManagerService;
mWindowShownTimeoutMs = mResources.getInteger(
@@ -147,12 +150,10 @@
}
private void drawUserIcon(int newUserId) {
- Bitmap bitmap = mUserManager.getUserIcon(newUserId);
- if (bitmap != null) {
- CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(mContext, bitmap);
- ((ImageView) getLayout().findViewById(R.id.user_loading_avatar))
- .setImageDrawable(drawable);
- }
+ Drawable userIcon = mUserIconProvider.getDrawableWithBadge(mContext,
+ mUserManager.getUserInfo(newUserId));
+ ((ImageView) getLayout().findViewById(R.id.user_loading_avatar))
+ .setImageDrawable(userIcon);
}
private void populateLoadingText(@UserIdInt int previousUserId, @UserIdInt int newUserId) {
@@ -167,7 +168,10 @@
mResources.getString(R.string.car_loading_profile_developer_message,
previousUserId, newUserId));
} else {
- msgView.setText(mResources.getString(R.string.car_loading_profile));
+ // Show the switchingFromUserMessage if it was set.
+ String switchingFromUserMessage = mActivityManager.getSwitchingFromUserMessage();
+ msgView.setText(switchingFromUserMessage != null ? switchingFromUserMessage
+ : mResources.getString(R.string.car_loading_profile));
}
}
}
diff --git a/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java b/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java
index cdf572b..b90f342 100644
--- a/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java
+++ b/src/com/android/systemui/car/volume/CarVolumeDialogImpl.java
@@ -114,7 +114,6 @@
private boolean mHovering;
private int mCurrentlyDisplayingGroupId;
private int mPreviouslyDisplayingGroupId;
- private boolean mShowing;
private boolean mDismissing;
private boolean mExpanded;
private View mExpandIcon;
@@ -267,7 +266,6 @@
mDialog = new CustomDialog(mContext);
mHovering = false;
- mShowing = false;
mDismissing = false;
mExpanded = false;
mWindow = mDialog.getWindow();
@@ -332,7 +330,7 @@
// Refresh the data set before showing.
mVolumeItemsAdapter.notifyDataSetChanged();
- if (mShowing) {
+ if (mDialog.isShowing()) {
if (mPreviouslyDisplayingGroupId == mCurrentlyDisplayingGroupId || mExpanded) {
return;
}
@@ -341,8 +339,8 @@
return;
}
- mShowing = true;
clearAllAndSetupDefaultCarVolumeLineItem(mCurrentlyDisplayingGroupId);
+ mDismissing = false;
mDialog.show();
Events.writeEvent(Events.EVENT_SHOW_DIALOG, reason, mKeyguard.isKeyguardLocked());
}
@@ -381,7 +379,7 @@
mHandler.removeMessages(H.DISMISS);
mHandler.removeMessages(H.SHOW);
- if (!mShowing || mDismissing) {
+ if (!mDialog.isShowing() || mDismissing) {
return;
}
@@ -396,7 +394,6 @@
Log.d(TAG, "mDialog.dismiss()");
}
mDialog.dismiss();
- mShowing = false;
mDismissing = false;
// if mExpandIcon is null that means user never clicked on the expanded arrow
// which implies that the dialog is still not expanded. In that case we do
diff --git a/src/com/android/systemui/car/window/OverlayPanelViewController.java b/src/com/android/systemui/car/window/OverlayPanelViewController.java
index 9ab6669..34cb557 100644
--- a/src/com/android/systemui/car/window/OverlayPanelViewController.java
+++ b/src/com/android/systemui/car/window/OverlayPanelViewController.java
@@ -96,10 +96,10 @@
private boolean mPanelVisible;
private boolean mPanelExpanded;
- private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
- private float mClosingVelocity = DEFAULT_FLING_VELOCITY;
+ protected float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
+ protected float mClosingVelocity = DEFAULT_FLING_VELOCITY;
- private boolean mIsAnimating;
+ protected boolean mIsAnimating;
private boolean mIsTracking;
public OverlayPanelViewController(
@@ -342,7 +342,7 @@
}
/** Returns the start position if we are in the middle of swiping. */
- private int getCurrentStartPosition(Rect clipBounds) {
+ protected int getCurrentStartPosition(Rect clipBounds) {
return mAnimateDirection > 0 ? clipBounds.bottom : clipBounds.top;
}
@@ -352,7 +352,7 @@
: 0;
}
- private void animate(float from, float to, float velocity, boolean isClosing) {
+ protected void animate(float from, float to, float velocity, boolean isClosing) {
if (mIsAnimating) {
return;
}
@@ -384,7 +384,7 @@
animator.start();
}
- private void resetPanelVisibility() {
+ protected void resetPanelVisibility() {
setPanelVisible(false);
getLayout().setClipBounds(null);
onCollapseAnimationEnd();
diff --git a/src/com/android/systemui/car/window/OverlayWindowModule.java b/src/com/android/systemui/car/window/OverlayWindowModule.java
index 9361c76..fc48ff8 100644
--- a/src/com/android/systemui/car/window/OverlayWindowModule.java
+++ b/src/com/android/systemui/car/window/OverlayWindowModule.java
@@ -21,6 +21,7 @@
import com.android.systemui.car.notification.BottomNotificationPanelViewMediator;
import com.android.systemui.car.notification.NotificationPanelViewMediator;
import com.android.systemui.car.notification.TopNotificationPanelViewMediator;
+import com.android.systemui.car.systemdialogs.SystemDialogsViewMediator;
import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator;
import com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator;
@@ -83,4 +84,11 @@
@ClassKey(HvacPanelOverlayViewMediator.class)
public abstract OverlayViewMediator bindHvacPanelOverlayViewMediator(
HvacPanelOverlayViewMediator overlayViewMediator);
+
+ /** Inject SystemDialogsViewMediator. */
+ @Binds
+ @IntoMap
+ @ClassKey(SystemDialogsViewMediator.class)
+ public abstract OverlayViewMediator bindSystemDialogsViewMediator(
+ SystemDialogsViewMediator sysui);
}
diff --git a/tests/res/layout/car_top_system_bar.xml b/tests/res/layout/car_top_system_bar.xml
index 98ec85a..55e1b9f 100644
--- a/tests/res/layout/car_top_system_bar.xml
+++ b/tests/res/layout/car_top_system_bar.xml
@@ -30,11 +30,18 @@
android:layout_weight="1"
android:layoutDirection="ltr">
- <com.android.systemui.car.systembar.CarSystemBarButton
+ <LinearLayout
+ android:id="@+id/qc_entry_points_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_alignParentStart="true"
+ />
+
+ <FrameLayout
android:id="@+id/clock_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:background="@drawable/system_bar_pill_rotary_background"
android:paddingStart="@dimen/car_padding_2"
android:paddingEnd="@dimen/car_padding_2"
android:layout_centerInParent="true">
@@ -47,13 +54,31 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.SystemBar.Clock"
systemui:amPmStyle="normal"
+ />
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/read_only_icons_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/clock_container"
/>
- </com.android.systemui.car.systembar.CarSystemBarButton>
+
+ <include layout="@layout/mic_privacy_chip"
+ android:layout_width="@dimen/privacy_chip_width"
+ android:layout_height="match_parent"
+ android:layout_centerVertical="true"
+ android:layout_toRightOf="@id/read_only_icons_container"
+ android:layout_toLeftOf="@id/user_name_container" />
+
+ <FrameLayout
+ android:id="@+id/user_name_container"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:layout_marginTop="@dimen/car_padding_2"
+ />
</RelativeLayout>
- <include layout="@layout/mic_privacy_chip"
- android:layout_width="@dimen/privacy_chip_width"
- android:layout_height="match_parent"
- android:layout_centerVertical="true"
- android:layout_toRightOf="@id/clock_container"
- android:layout_toLeftOf="@id/user_name_container" />
</com.android.systemui.car.systembar.CarSystemBarView>
diff --git a/tests/src/com/android/systemui/car/qc/ProfileSwitcherTest.java b/tests/src/com/android/systemui/car/qc/ProfileSwitcherTest.java
index 939253a..e07d2a4 100644
--- a/tests/src/com/android/systemui/car/qc/ProfileSwitcherTest.java
+++ b/tests/src/com/android/systemui/car/qc/ProfileSwitcherTest.java
@@ -24,11 +24,13 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.car.user.CarUserManager;
import android.car.user.UserCreationResult;
import android.car.user.UserSwitchResult;
@@ -71,6 +73,8 @@
@Mock
private UserManager mUserManager;
@Mock
+ private DevicePolicyManager mDevicePolicyManager;
+ @Mock
private CarUserManager mCarUserManager;
@Before
@@ -79,6 +83,8 @@
when(mUserManager.getAliveUsers()).thenReturn(mAliveUsers);
when(mUserManager.getUserSwitchability(any())).thenReturn(SWITCHABILITY_STATUS_OK);
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(false);
+ when(mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()).thenReturn(false);
AsyncFuture<UserSwitchResult> switchResultFuture = mock(AsyncFuture.class);
UserSwitchResult switchResult = mock(UserSwitchResult.class);
@@ -86,7 +92,8 @@
when(switchResultFuture.get(anyLong(), any())).thenReturn(switchResult);
when(mCarUserManager.switchUser(anyInt())).thenReturn(switchResultFuture);
- mProfileSwitcher = new ProfileSwitcher(mContext, mUserManager, mCarUserManager);
+ mProfileSwitcher = new ProfileSwitcher(mContext, mUserManager, mDevicePolicyManager,
+ mCarUserManager);
}
@Test
@@ -100,9 +107,9 @@
UserInfo otherUser = generateUser(1001, "Other User", /* supportsSwitch= */ true,
/* isGuest= */ false);
mAliveUsers.add(otherUser);
- QCList list = getQCList();
- assertThat(list.getRows().size()).isEqualTo(1);
- assertThat(list.getRows().get(0).getTitle()).isEqualTo("Current User");
+ List<QCRow> rows = getProfileRows();
+ assertThat(rows).hasSize(1);
+ assertThat(rows.get(0).getTitle()).isEqualTo("Current User");
}
@Test
@@ -113,14 +120,14 @@
/* isGuest= */ false);
mAliveUsers.add(user1);
mAliveUsers.add(user2);
- QCList list = getQCList();
+ List<QCRow> rows = getProfileRows();
// Expect four rows - one for each user, one for the guest user, and one for add user
- assertThat(list.getRows().size()).isEqualTo(4);
- assertThat(list.getRows().get(0).getTitle()).isEqualTo("User1");
- assertThat(list.getRows().get(1).getTitle()).isEqualTo("User2");
- assertThat(list.getRows().get(2).getTitle()).isEqualTo(
+ assertThat(rows).hasSize(4);
+ assertThat(rows.get(0).getTitle()).isEqualTo("User1");
+ assertThat(rows.get(1).getTitle()).isEqualTo("User2");
+ assertThat(rows.get(2).getTitle()).isEqualTo(
mContext.getString(R.string.car_guest));
- assertThat(list.getRows().get(3).getTitle()).isEqualTo(
+ assertThat(rows.get(3).getTitle()).isEqualTo(
mContext.getString(R.string.car_add_user));
}
@@ -132,13 +139,13 @@
/* isGuest= */ false);
mAliveUsers.add(user1);
mAliveUsers.add(user2);
- QCList list = getQCList();
+ List<QCRow> rows = getProfileRows();
// Expect three rows - one for the valid user, one for the guest user, and one for add user
- assertThat(list.getRows().size()).isEqualTo(3);
- assertThat(list.getRows().get(0).getTitle()).isEqualTo("User1");
- assertThat(list.getRows().get(1).getTitle()).isEqualTo(
+ assertThat(rows).hasSize(3);
+ assertThat(rows.get(0).getTitle()).isEqualTo("User1");
+ assertThat(rows.get(1).getTitle()).isEqualTo(
mContext.getString(R.string.car_guest));
- assertThat(list.getRows().get(2).getTitle()).isEqualTo(
+ assertThat(rows.get(2).getTitle()).isEqualTo(
mContext.getString(R.string.car_add_user));
}
@@ -150,13 +157,51 @@
/* isGuest= */ true);
mAliveUsers.add(user1);
mAliveUsers.add(user2);
- QCList list = getQCList();
+ List<QCRow> rows = getProfileRows();
// Expect three rows - one for the valid user, one for the guest user, and one for add user
- assertThat(list.getRows().size()).isEqualTo(3);
- assertThat(list.getRows().get(0).getTitle()).isEqualTo("User1");
- assertThat(list.getRows().get(1).getTitle()).isEqualTo(
+ assertThat(rows).hasSize(3);
+ assertThat(rows.get(0).getTitle()).isEqualTo("User1");
+ assertThat(rows.get(1).getTitle()).isEqualTo(
mContext.getString(R.string.car_guest));
- assertThat(list.getRows().get(2).getTitle()).isEqualTo(
+ assertThat(rows.get(2).getTitle()).isEqualTo(
+ mContext.getString(R.string.car_add_user));
+ }
+
+ @Test
+ public void switchAllowed_addUserDisallowed_returnsValidRows() {
+ when(mUserManager.hasUserRestrictionForUser(eq(UserManager.DISALLOW_ADD_USER),
+ any())).thenReturn(true);
+ UserInfo user1 = generateUser(1000, "User1", /* supportsSwitch= */ true,
+ /* isGuest= */ false);
+ UserInfo user2 = generateUser(1001, "User2", /* supportsSwitch= */ true,
+ /* isGuest= */ false);
+ mAliveUsers.add(user1);
+ mAliveUsers.add(user2);
+ List<QCRow> rows = getProfileRows();
+ // Expect three rows - one for each user and one for the guest user
+ assertThat(rows).hasSize(3);
+ assertThat(rows.get(0).getTitle()).isEqualTo("User1");
+ assertThat(rows.get(1).getTitle()).isEqualTo("User2");
+ assertThat(rows.get(2).getTitle()).isEqualTo(
+ mContext.getString(R.string.car_guest));
+ }
+
+ @Test
+ public void switchAllowed_deviceManaged_returnsValidRows() {
+ when(mDevicePolicyManager.isDeviceManaged()).thenReturn(true);
+ UserInfo user1 = generateUser(1000, "User1", /* supportsSwitch= */ true,
+ /* isGuest= */ false);
+ mAliveUsers.add(user1);
+ List<QCRow> rows = getProfileRows();
+ // Expect four rows - one for the device owner message, one for the user,
+ // one for the guest user, and one for add user
+ assertThat(rows).hasSize(4);
+ assertThat(rows.get(0).getSubtitle()).isEqualTo(
+ mContext.getString(R.string.do_disclosure_generic));
+ assertThat(rows.get(1).getTitle()).isEqualTo("User1");
+ assertThat(rows.get(2).getTitle()).isEqualTo(
+ mContext.getString(R.string.car_guest));
+ assertThat(rows.get(3).getTitle()).isEqualTo(
mContext.getString(R.string.car_add_user));
}
@@ -170,10 +215,10 @@
/* isGuest= */ false);
mAliveUsers.add(user1);
mAliveUsers.add(user2);
- QCList list = getQCList();
+ List<QCRow> rows = getProfileRows();
// Expect four rows - one for each user, one for the guest user, and one for add user
- assertThat(list.getRows().size()).isEqualTo(4);
- QCRow otherUserRow = list.getRows().get(1);
+ assertThat(rows).hasSize(4);
+ QCRow otherUserRow = rows.get(1);
otherUserRow.getActionHandler().onAction(otherUserRow, mContext, new Intent());
verify(mCarUserManager).switchUser(otherUserId);
}
@@ -194,20 +239,20 @@
UserInfo user1 = generateUser(currentUserId, "User1", /* supportsSwitch= */ true,
/* isGuest= */ false);
mAliveUsers.add(user1);
- QCList list = getQCList();
+ List<QCRow> rows = getProfileRows();
// Expect 3 rows - one for the user, one for the guest user, and one for add user
- assertThat(list.getRows().size()).isEqualTo(3);
- QCRow guestRow = list.getRows().get(1);
+ assertThat(rows).hasSize(3);
+ QCRow guestRow = rows.get(1);
guestRow.getActionHandler().onAction(guestRow, mContext, new Intent());
verify(mCarUserManager).createGuest(any());
verify(mCarUserManager).switchUser(guestUserId);
}
- private QCList getQCList() {
+ private List<QCRow> getProfileRows() {
QCItem item = mProfileSwitcher.getQCItem();
assertThat(item).isNotNull();
assertThat(item instanceof QCList).isTrue();
- return (QCList) item;
+ return ((QCList) item).getRows();
}
private UserInfo generateUser(int id, String name, boolean supportsSwitch, boolean isGuest) {
diff --git a/tests/src/com/android/systemui/car/systembar/CarSystemBarControllerTest.java b/tests/src/com/android/systemui/car/systembar/CarSystemBarControllerTest.java
index afbd5e7..4c888f4 100644
--- a/tests/src/com/android/systemui/car/systembar/CarSystemBarControllerTest.java
+++ b/tests/src/com/android/systemui/car/systembar/CarSystemBarControllerTest.java
@@ -16,11 +16,19 @@
package com.android.systemui.car.systembar;
+import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
+import static android.app.StatusBarManager.DISABLE_HOME;
+import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.ActivityManager;
+import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableResources;
@@ -62,8 +70,11 @@
private CarSystemBarController mCarSystemBar;
private CarSystemBarViewFactory mCarSystemBarViewFactory;
private TestableResources mTestableResources;
+ private Context mSpiedContext;
@Mock
+ private ActivityManager mActivityManager;
+ @Mock
private ButtonSelectionStateController mButtonSelectionStateController;
@Mock
private ButtonRoleHolderController mButtonRoleHolderController;
@@ -89,9 +100,11 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mCarSystemBarViewFactory = new CarSystemBarViewFactory(mContext, mFeatureFlags,
- mQuickControlsEntryPointsController, mReadOnlyIconsController);
mTestableResources = mContext.getOrCreateTestableResources();
+ mSpiedContext = spy(mContext);
+ when(mSpiedContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager);
+ mCarSystemBarViewFactory = new CarSystemBarViewFactory(mSpiedContext, mFeatureFlags,
+ mQuickControlsEntryPointsController, mReadOnlyIconsController);
// Needed to inflate top navigation bar.
mDependency.injectMockDependency(DarkIconDispatcher.class);
@@ -99,8 +112,9 @@
}
private CarSystemBarController createSystemBarController() {
- return new CarSystemBarController(mContext, mCarSystemBarViewFactory, mCarServiceProvider,
- mBroadcastDispatcher, mConfigurationController, mButtonSelectionStateController,
+ return new CarSystemBarController(mSpiedContext, mCarSystemBarViewFactory,
+ mCarServiceProvider, mBroadcastDispatcher, mConfigurationController,
+ mButtonSelectionStateController,
() -> mUserNameViewController, () -> mPrivacyChipViewController,
mButtonRoleHolderController,
new SystemBarConfigs(mTestableResources.getResources()),
@@ -573,4 +587,154 @@
assertThat(notifications.getUnseen()).isFalse();
}
+
+ @Test
+ public void testSetSystemBarStates_stateUpdated() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ clearSystemBarStates();
+
+ mCarSystemBar.setSystemBarStates(DISABLE_HOME, /* state2= */ 0);
+
+ assertThat(mCarSystemBar.getStatusBarState()).isEqualTo(DISABLE_HOME);
+ }
+
+ @Test
+ public void testSetSystemBarStates_state2Updated() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ clearSystemBarStates();
+
+ mCarSystemBar.setSystemBarStates(0, DISABLE2_QUICK_SETTINGS);
+
+ assertThat(mCarSystemBar.getStatusBarState2()).isEqualTo(DISABLE2_QUICK_SETTINGS);
+ }
+
+ @Test
+ public void testRefreshSystemBar_homeDisabled() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView bottomBar = mCarSystemBar.getBottomBar(/* isSetUp= */ true);
+ clearSystemBarStates();
+ CarSystemBarButton button = bottomBar.findViewById(R.id.home);
+ assertThat(button.getDisabled()).isFalse();
+
+ mCarSystemBar.setSystemBarStates(DISABLE_HOME, /* state2= */ 0);
+
+ assertThat(button.getDisabled()).isTrue();
+ }
+
+ @Test
+ public void testRefreshSystemBar_phoneNavDisabled() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView bottomBar = mCarSystemBar.getBottomBar(/* isSetUp= */ true);
+ clearSystemBarStates();
+ CarSystemBarButton button = bottomBar.findViewById(R.id.phone_nav);
+ assertThat(button.getDisabled()).isFalse();
+
+ setLockTaskModeLocked(/* locked= */true);
+
+ assertThat(button.getDisabled()).isTrue();
+ }
+
+ @Test
+ public void testRefreshSystemBar_appGridisabled() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView bottomBar = mCarSystemBar.getBottomBar(/* isSetUp= */ true);
+ clearSystemBarStates();
+ CarSystemBarButton button = bottomBar.findViewById(R.id.grid_nav);
+ assertThat(button.getDisabled()).isFalse();
+
+ mCarSystemBar.setSystemBarStates(DISABLE_HOME, /* state2= */ 0);
+
+ assertThat(button.getDisabled()).isTrue();
+ }
+
+ @Test
+ public void testRefreshSystemBar_notificationDisabled() {
+ mTestableResources.addOverride(R.bool.config_enableBottomSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView bottomBar = mCarSystemBar.getBottomBar(/* isSetUp= */ true);
+ clearSystemBarStates();
+ CarSystemBarButton button = bottomBar.findViewById(R.id.notifications);
+ assertThat(button.getDisabled()).isFalse();
+
+ mCarSystemBar.setSystemBarStates(DISABLE_NOTIFICATION_ICONS, /* state2= */ 0);
+
+ assertThat(button.getDisabled()).isTrue();
+ }
+
+ @Test
+ public void testRefreshSystemBar_disableQcFlagOn_qcHidden() {
+ mTestableResources.addOverride(R.bool.config_enableTopSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+
+ CarSystemBarView topBar = mCarSystemBar.getTopBar(/* isSetUp= */ true);
+ View qcView = topBar.findViewById(R.id.qc_entry_points_container);
+ clearSystemBarStates();
+ assertThat(qcView.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mCarSystemBar.setSystemBarStates(0, DISABLE2_QUICK_SETTINGS);
+
+ assertThat(qcView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void testRefreshSystemBar_lockTaskModeOn_qcHidden() {
+ mTestableResources.addOverride(R.bool.config_enableTopSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView topBar = mCarSystemBar.getTopBar(/* isSetUp= */ true);
+ View qcView = topBar.findViewById(R.id.qc_entry_points_container);
+ clearSystemBarStates();
+ assertThat(qcView.getVisibility()).isEqualTo(View.VISIBLE);
+
+ setLockTaskModeLocked(/* locked= */ true);
+
+ assertThat(qcView.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void testRefreshSystemBar_disableQcFlagOn_userSwitcherHidden() {
+ mTestableResources.addOverride(R.bool.config_enableTopSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView topBar = mCarSystemBar.getTopBar(/* isSetUp= */ true);
+
+ View userSwitcher = topBar.findViewById(R.id.user_name_container);
+ clearSystemBarStates();
+ assertThat(userSwitcher.getVisibility()).isEqualTo(View.VISIBLE);
+
+ mCarSystemBar.setSystemBarStates(0, DISABLE2_QUICK_SETTINGS);
+
+ assertThat(userSwitcher.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ @Test
+ public void testRefreshSystemBar_lockTaskModeOn_userSwitcherHidden() {
+ mTestableResources.addOverride(R.bool.config_enableTopSystemBar, true);
+ mCarSystemBar = createSystemBarController();
+ CarSystemBarView topBar = mCarSystemBar.getTopBar(/* isSetUp= */ true);
+ View userSwitcher = topBar.findViewById(R.id.user_name_container);
+ clearSystemBarStates();
+ assertThat(userSwitcher.getVisibility()).isEqualTo(View.VISIBLE);
+
+ setLockTaskModeLocked(/* locked= */ true);
+
+ assertThat(userSwitcher.getVisibility()).isEqualTo(View.INVISIBLE);
+ }
+
+ private void clearSystemBarStates() {
+ if (mCarSystemBar != null) {
+ mCarSystemBar.setSystemBarStates(/* state= */ 0, /* state2= */ 0);
+ }
+ setLockTaskModeLocked(false);
+ }
+
+ private void setLockTaskModeLocked(boolean locked) {
+ when(mActivityManager.getLockTaskModeState()).thenReturn(locked
+ ? ActivityManager.LOCK_TASK_MODE_LOCKED
+ : ActivityManager.LOCK_TASK_MODE_NONE);
+ mCarSystemBar.setSystemBarStates(/* state= */ 0, /* state2= */ 0);
+ }
}
diff --git a/tests/src/com/android/systemui/car/systembar/CarSystemBarTest.java b/tests/src/com/android/systemui/car/systembar/CarSystemBarTest.java
index eadb323..d101395 100644
--- a/tests/src/com/android/systemui/car/systembar/CarSystemBarTest.java
+++ b/tests/src/com/android/systemui/car/systembar/CarSystemBarTest.java
@@ -25,6 +25,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -379,6 +381,22 @@
assertThat(mCarSystemBar.isNavBarTransientShown()).isFalse();
}
+ @Test
+ public void disable_wrongDisplayId_notSetStatusBarState() {
+ int randomDisplay = Display.DEFAULT_DISPLAY + 10;
+
+ mCarSystemBar.disable(randomDisplay, 0, 0, false);
+
+ verify(mCarSystemBarController, never()).setSystemBarStates(anyInt(), anyInt());
+ }
+
+ @Test
+ public void disable_correctDisplayId_setSystemBarStates() {
+ mCarSystemBar.disable(Display.DEFAULT_DISPLAY, 0, 0, false);
+
+ verify(mCarSystemBarController).setSystemBarStates(0, 0);
+ }
+
private void waitForDelayableExecutor() {
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
diff --git a/tests/src/com/android/systemui/car/systembar/PrivacyChipViewControllerTest.java b/tests/src/com/android/systemui/car/systembar/PrivacyChipViewControllerTest.java
index d751ac8..6baae34 100644
--- a/tests/src/com/android/systemui/car/systembar/PrivacyChipViewControllerTest.java
+++ b/tests/src/com/android/systemui/car/systembar/PrivacyChipViewControllerTest.java
@@ -23,7 +23,6 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
@@ -36,7 +35,6 @@
import android.car.user.CarUserManager;
import android.content.BroadcastReceiver;
import android.content.Intent;
-import android.content.IntentFilter;
import android.hardware.SensorPrivacyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -300,8 +298,7 @@
.thenReturn(true);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- argThat((IntentFilter filter) -> filter.hasAction(Intent.ACTION_USER_INFO_CHANGED)),
- any(), any());
+ any(), any(), any());
reset(mExecutor);
when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(1);
mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext,
@@ -318,13 +315,15 @@
when(mSensorPrivacyManager.isSensorPrivacyEnabled(MICROPHONE, /* userId= */ 1))
.thenReturn(false);
mPrivacyChipViewController.addPrivacyChipView(mFrameLayout);
- verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverArgumentCaptor.capture(),
- argThat((IntentFilter filter) -> filter.hasAction(Intent.ACTION_USER_FOREGROUND)),
- any(), any());
+ verify(mCarUserManager).addListener(any(), mUserLifecycleListenerArgumentCaptor.capture());
+ CarUserManager.UserLifecycleEvent event = new CarUserManager.UserLifecycleEvent(
+ CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING, /* from= */ 0,
+ /* to= */ 1);
+ CarUserManager.UserLifecycleListener userLifecycleListener =
+ mUserLifecycleListenerArgumentCaptor.getValue();
+ assertThat(userLifecycleListener).isNotNull();
reset(mExecutor);
- when(mCarDeviceProvisionedController.getCurrentUser()).thenReturn(1);
- mBroadcastReceiverArgumentCaptor.getValue().onReceive(mContext,
- new Intent(Intent.ACTION_USER_FOREGROUND));
+ userLifecycleListener.onEvent(event);
verify(mExecutor).execute(mRunnableArgumentCaptor.capture());
mRunnableArgumentCaptor.getValue().run();
diff --git a/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java b/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
index afd22b5..ed7c60b 100644
--- a/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
+++ b/tests/src/com/android/systemui/car/userswitcher/UserSwitchTransitionViewControllerTest.java
@@ -16,13 +16,17 @@
package com.android.systemui.car.userswitcher;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
-import android.content.Context;
+import android.app.ActivityManager;
+import android.content.pm.UserInfo;
import android.os.UserManager;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -31,6 +35,7 @@
import android.view.IWindowManager;
import android.view.LayoutInflater;
import android.view.ViewGroup;
+import android.widget.TextView;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -56,10 +61,16 @@
private UserSwitchTransitionViewController mCarUserSwitchingDialogController;
private TestableResources mTestableResources;
private FakeExecutor mExecutor;
+ private FakeSystemClock mClock;
+ private ViewGroup mViewGroup;
+ @Mock
+ private ActivityManager mMockActivityManager;
@Mock
private OverlayViewGlobalStateController mOverlayViewGlobalStateController;
@Mock
private IWindowManager mWindowManagerService;
+ @Mock
+ private UserManager mMockUserManager;
@Before
public void setUp() {
@@ -70,18 +81,22 @@
mContext,
mTestableResources.getResources(),
mExecutor,
- (UserManager) mContext.getSystemService(Context.USER_SERVICE),
+ mMockActivityManager,
+ mMockUserManager,
mWindowManagerService,
mOverlayViewGlobalStateController
);
- mCarUserSwitchingDialogController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
- R.layout.sysui_overlay_window, /* root= */ null));
+ mockGetUserInfo(TEST_USER_1);
+ mockGetUserInfo(TEST_USER_2);
+ mViewGroup = (ViewGroup) LayoutInflater.from(mContext).inflate(
+ R.layout.sysui_overlay_window, /* root= */ null);
+ mCarUserSwitchingDialogController.inflate(mViewGroup);
}
@Test
public void onHandleShow_newUserSelected_showsDialog() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
@@ -90,11 +105,35 @@
}
@Test
- public void onHandleShow_alreadyShowing_ignoresRequest() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ public void onHandleShow_showsDefaultLoadingMessage() {
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_2);
+
+ TextView textView = mViewGroup.findViewById(R.id.user_loading);
+ assertThat(textView.getText().toString()).isEqualTo(
+ mTestableResources.getResources().getString(R.string.car_loading_profile));
+ }
+
+ @Test
+ public void onHandleShow_showsUserSwitchingMessage() {
+ String message = "Hello world!";
+ when(mMockActivityManager.getSwitchingFromUserMessage()).thenReturn(message);
+
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
+ mExecutor.advanceClockToLast();
+ mExecutor.runAllReady();
+
+ TextView textView = mViewGroup.findViewById(R.id.user_loading);
+ assertThat(textView.getText().toString()).isEqualTo(message);
+ }
+
+ @Test
+ public void onHandleShow_alreadyShowing_ignoresRequest() {
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
+ mExecutor.advanceClockToLast();
+ mExecutor.runAllReady();
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_2);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
@@ -105,13 +144,13 @@
@Test
public void onHandleShow_sameUserSelected_ignoresRequest() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
mCarUserSwitchingDialogController.handleHide();
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
@@ -122,7 +161,7 @@
@Test
public void onHide_currentlyShowing_hidesDialog() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
mCarUserSwitchingDialogController.handleHide();
@@ -135,7 +174,7 @@
@Test
public void onHide_notShowing_ignoresRequest() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
mCarUserSwitchingDialogController.handleHide();
@@ -152,7 +191,7 @@
@Test
public void onWindowShownTimeoutPassed_viewNotHidden_hidesUserSwitchTransitionView() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
reset(mOverlayViewGlobalStateController);
@@ -165,7 +204,7 @@
@Test
public void onWindowShownTimeoutPassed_viewHidden_doesNotHideUserSwitchTransitionViewAgain() {
- mCarUserSwitchingDialogController.handleShow(/* currentUserId= */ TEST_USER_1);
+ mCarUserSwitchingDialogController.handleShow(/* newUserId= */ TEST_USER_1);
mExecutor.advanceClockToLast();
mExecutor.runAllReady();
mCarUserSwitchingDialogController.handleHide();
@@ -178,4 +217,9 @@
eq(mCarUserSwitchingDialogController), any());
}, mCarUserSwitchingDialogController.getWindowShownTimeoutMs() + 10);
}
+
+ private void mockGetUserInfo(int userId) {
+ when(mMockUserManager.getUserInfo(userId))
+ .thenReturn(new UserInfo(userId, "USER_" + userId, /* flags= */ 0));
+ }
}