Merge "Create dialog to show detecting Batmobile device & unlocking" into qt-qpr1-dev
am: cf885ef399
Change-Id: I3e6e03dedbdc6801483285d631586431937e92ca
diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml
index 195d4fe..261b9f5 100644
--- a/packages/CarSystemUI/AndroidManifest.xml
+++ b/packages/CarSystemUI/AndroidManifest.xml
@@ -21,4 +21,8 @@
coreApp="true">
<!-- This permission is required to monitor car power state. -->
<uses-permission android:name="android.car.permission.CAR_POWER" />
+ <!-- This permission is required to get the trusted device list of a user. -->
+ <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/>
+ <!-- This permission is required to get bluetooth broadcast. -->
+ <uses-permission android:name="android.permission.BLUETOOTH" />
</manifest>
diff --git a/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml
new file mode 100644
index 0000000..bec6ba7
--- /dev/null
+++ b/packages/CarSystemUI/res/drawable/unlock_dialog_background.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/unlock_dialog_background_color"/>
+ <padding
+ android:bottom="@*android:dimen/car_padding_2"
+ android:left="@*android:dimen/car_padding_2"
+ android:right="@*android:dimen/car_padding_2"
+ android:top="@*android:dimen/car_padding_2"/>
+ <corners
+ android:radius="@dimen/unlock_dialog_radius"/>
+</shape>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
new file mode 100644
index 0000000..2d9901c
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center">
+
+ <LinearLayout
+ android:layout_width="@dimen/unlock_dialog_width"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:layout_gravity="center"
+ android:orientation="vertical"
+ android:background="@drawable/unlock_dialog_background"
+ android:padding="@*android:dimen/car_padding_2">
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+ <ProgressBar
+ android:layout_gravity="center"
+ android:layout_width="@dimen/unlock_dialog_progress_bar_size"
+ android:layout_height="@dimen/unlock_dialog_progress_bar_size" />
+ <ImageView
+ android:id="@+id/avatar"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/unlock_dialog_avatar_size"
+ android:layout_height="@dimen/unlock_dialog_avatar_size"
+ android:scaleType="fitCenter"/>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/user_name"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/unlock_dialog_default_user_name"
+ android:textSize="@*android:dimen/car_body1_size"
+ android:textColor="@android:color/white"/>
+
+ <TextView
+ android:id="@+id/unlocking_text"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginTop="@*android:dimen/car_padding_1"
+ android:text="@string/unlock_dialog_message_default"
+ android:textSize="@*android:dimen/car_body4_size"
+ android:textColor="@color/unlock_dialog_message_text_default"/>
+
+ <Button
+ android:id="@+id/enter_pin_button"
+ android:layout_marginTop="@*android:dimen/car_padding_1"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:text="@string/unlock_dialog_button_text_pin"
+ style="@style/UnlockDialogButton"/>
+ </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/CarSystemUI/res/values/colors_car.xml b/packages/CarSystemUI/res/values/colors_car.xml
index 69ab3f3..0a3f7aa 100644
--- a/packages/CarSystemUI/res/values/colors_car.xml
+++ b/packages/CarSystemUI/res/values/colors_car.xml
@@ -26,4 +26,9 @@
<color name="car_user_switcher_add_user_background_color">@*android:color/car_dark_blue_grey_600</color>
<color name="car_user_switcher_add_user_add_sign_color">@*android:color/car_body1_light</color>
+ <!-- colors for unlock dialog -->
+ <color name="unlock_dialog_background_color">#ff282a2d</color>
+ <color name="unlock_dialog_message_text_default">@*android:color/car_grey_400</color>
+ <color name="unlock_dialog_enter_pin_text_color">#ff66b5ff</color>
+
</resources>
diff --git a/packages/CarSystemUI/res/values/dimens_car.xml b/packages/CarSystemUI/res/values/dimens_car.xml
index 42a7649..9cb09c9 100644
--- a/packages/CarSystemUI/res/values/dimens_car.xml
+++ b/packages/CarSystemUI/res/values/dimens_car.xml
@@ -43,4 +43,10 @@
<!-- This must be the negative of car_user_switcher_container_height for the animation. -->
<dimen name="car_user_switcher_container_anim_height">-420dp</dimen>
+ <!-- dimensions for the unlock dialog -->
+ <dimen name="unlock_dialog_width">500dp</dimen>
+ <dimen name="unlock_dialog_radius">16dp</dimen>
+ <dimen name="unlock_dialog_avatar_size">100dp</dimen>
+ <dimen name="unlock_dialog_progress_bar_size">140dp</dimen>
+
</resources>
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index be2cb0d8..862ba75 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -31,5 +31,7 @@
<!--Percentage of the screen height, from the bottom, that a notification panel being peeked
at will result in remaining closed the panel if released-->
<integer name="notification_settle_close_percentage">80</integer>
+ <!-- The delay before the unlock dialog pops up -->
+ <integer name="unlock_dialog_delay_ms">3000</integer>
</resources>
diff --git a/packages/CarSystemUI/res/values/strings_car.xml b/packages/CarSystemUI/res/values/strings_car.xml
index 83e91c5..717692e 100644
--- a/packages/CarSystemUI/res/values/strings_car.xml
+++ b/packages/CarSystemUI/res/values/strings_car.xml
@@ -29,4 +29,19 @@
<string name="user_add_user_message_setup">When you add a new user, that person needs to set up their space.</string>
<!-- Message to inform user that the newly created user will have permissions to update apps for all other users. [CHAR LIMIT=100] -->
<string name="user_add_user_message_update">Any user can update apps for all other users.</string>
+ <!-- Default messages displayed on the unlock dialog before unlock advertising started. [CHAR LIMIT=30]-->
+ <string name="unlock_dialog_message_default">Waiting\u2026</string>
+ <!-- Message to inform user that the IHU is looking for trusted device. [CHAR LIMIT=30] -->
+ <string name="unlock_dialog_message_start">Looking for trusted device\u2026</string>
+
+ <!-- Cancel Button text for user who has PIN as security lock. [CHAR LIMIT=30] -->
+ <string name="unlock_dialog_button_text_pin">Enter PIN instead</string>
+ <!-- Cancel Button text for user who has Pattern as security lock. [CHAR LIMIT=30] -->
+ <string name="unlock_dialog_button_text_pattern">Enter Pattern instead</string>
+ <!-- Cancel Button text for user who has Password as security lock. [CHAR LIMIT=30] -->
+ <string name="unlock_dialog_button_text_password">Enter Password instead</string>
+ <!-- Default user name shows on unlock dialog -->
+ <string name="unlock_dialog_default_user_name">Default Name</string>
+ <!-- Default title for unlock dialog -->
+ <string name="unlock_dialog_title">Unlock Dialogue</string>
</resources>
diff --git a/packages/CarSystemUI/res/values/styles.xml b/packages/CarSystemUI/res/values/styles.xml
index 371bebd..a9423bf 100644
--- a/packages/CarSystemUI/res/values/styles.xml
+++ b/packages/CarSystemUI/res/values/styles.xml
@@ -46,4 +46,12 @@
<item name="android:layout_width">96dp</item>
<item name="android:background">@drawable/nav_button_background</item>
</style>
+
+ <style name="UnlockDialogButton">
+ <item name="android:background">?android:attr/selectableItemBackground</item>
+ <item name="android:textAlignment">center</item>
+ <item name="android:textColor">@color/unlock_dialog_enter_pin_text_color</item>
+ <item name="android:paddingHorizontal">16dp</item>
+ <item name="android:textAllCaps">false</item>
+ </style>
</resources>
\ No newline at end of file
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index da42d4f..aa5e852 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -26,6 +26,7 @@
import android.car.drivingstate.CarDrivingStateEvent;
import android.car.drivingstate.CarUxRestrictionsManager;
import android.car.hardware.power.CarPowerManager.CarPowerStateListener;
+import android.car.trust.CarTrustAgentEnrollmentManager;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Rect;
@@ -1027,8 +1028,12 @@
UserSwitcherController userSwitcherController =
Dependency.get(UserSwitcherController.class);
if (userSwitcherController.useFullscreenUserSwitcher()) {
+ Car car = Car.createCar(mContext);
+ CarTrustAgentEnrollmentManager enrollmentManager = (CarTrustAgentEnrollmentManager) car
+ .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
mFullscreenUserSwitcher = new FullscreenUserSwitcher(this,
- mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub), mContext);
+ mStatusBarWindow.findViewById(R.id.fullscreen_user_switcher_stub),
+ enrollmentManager, mContext);
} else {
super.createUserSwitcher();
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
new file mode 100644
index 0000000..78bb1bc
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.car;
+
+import android.app.admin.DevicePolicyManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.R;
+
+/**
+ * A helper class displays an unlock dialog and receives broadcast about detecting trusted device
+ * & unlocking state to show the appropriate message on the dialog.
+ */
+class CarTrustAgentUnlockDialogHelper extends BroadcastReceiver{
+ private static final String TAG = CarTrustAgentUnlockDialogHelper.class.getSimpleName();
+
+ private final Context mContext;
+ private final WindowManager mWindowManager;
+ private final UserManager mUserManager;
+ private final WindowManager.LayoutParams mParams;
+ /**
+ * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an
+ * activity.
+ */
+ private final View mUnlockDialog;
+ private final TextView mUnlockingText;
+ private final Button mButton;
+ private final IntentFilter mFilter;
+ private int mUid;
+ private boolean mIsDialogShowing;
+ private OnHideListener mOnHideListener;
+
+ CarTrustAgentUnlockDialogHelper(Context context) {
+ mContext = context;
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mWindowManager = mContext.getSystemService(WindowManager.class);
+ mParams = createLayoutParams();
+ mFilter = getIntentFilter();
+
+ mParams.packageName = mContext.getPackageName();
+ mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));
+
+ mUnlockDialog = LayoutInflater.from(mContext).inflate(
+ R.layout.trust_agent_unlock_dialog, null);
+ mUnlockDialog.setLayoutParams(mParams);
+
+ mUnlockingText = mUnlockDialog.findViewById(R.id.unlocking_text);
+ mButton = mUnlockDialog.findViewById(R.id.enter_pin_button);
+ mButton.setOnClickListener(v -> {
+ hideUnlockDialog(/* notifyOnHideListener= */true);
+ // TODO(b/138250105) Stop unlock advertising
+ });
+
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (bluetoothAdapter != null
+ && bluetoothAdapter.getLeState() == BluetoothAdapter.STATE_BLE_ON) {
+ mUnlockingText.setText(R.string.unlock_dialog_message_start);
+ }
+ }
+
+ /**
+ * This filter is listening on:
+ * {@link BluetoothAdapter#ACTION_BLE_STATE_CHANGED} for starting unlock advertising;
+ * {@link Intent#ACTION_USER_UNLOCKED} for IHU unlocked
+ */
+ private IntentFilter getIntentFilter() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+ filter.addAction(Intent.ACTION_USER_UNLOCKED);
+ return filter;
+ }
+
+ /**
+ * Show dialog for the given user
+ */
+ void showUnlockDialog(int uid, OnHideListener listener) {
+ showUnlockDialogAfterDelay(uid, 0, listener);
+ }
+
+ /**
+ * Show dialog for the given user after the certain time of delay has elapsed
+ *
+ * @param uid the user to unlock
+ * @param listener listener that listens to dialog hide
+ */
+ void showUnlockDialogAfterDelay(int uid, OnHideListener listener) {
+ long delayMillis = mContext.getResources().getInteger(R.integer.unlock_dialog_delay_ms);
+ showUnlockDialogAfterDelay(uid, delayMillis, listener);
+ }
+
+ /**
+ * Show dialog for the given user after the supplied delay has elapsed
+ */
+ private void showUnlockDialogAfterDelay(int uid, long delayMillis, OnHideListener listener) {
+ setUid(uid);
+ mOnHideListener = listener;
+ if (!mIsDialogShowing) {
+ logd("Receiver registered");
+ mContext.registerReceiverAsUser(this, UserHandle.ALL, mFilter,
+ /* broadcastPermission= */ null,
+ /* scheduler= */ null);
+ new Handler().postDelayed(() -> {
+ if (!mUserManager.isUserUnlocked(uid)) {
+ logd("Showed unlock dialog for user: " + uid + " after " + delayMillis
+ + " delay.");
+ mWindowManager.addView(mUnlockDialog, mParams);
+ }
+ }, delayMillis);
+ }
+ mIsDialogShowing = true;
+ }
+
+ private void setUid(int uid) {
+ mUid = uid;
+ TextView userName = mUnlockDialog.findViewById(R.id.user_name);
+ userName.setText(mUserManager.getUserInfo(mUid).name);
+ ImageView avatar = mUnlockDialog.findViewById(R.id.avatar);
+ avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
+ setButtonText();
+ }
+
+ private void hideUnlockDialog(boolean notifyOnHideListener) {
+ if (!mIsDialogShowing) {
+ return;
+ }
+ mWindowManager.removeView(mUnlockDialog);
+ logd("Receiver unregistered");
+ mContext.unregisterReceiver(this);
+ if (notifyOnHideListener && mOnHideListener != null) {
+ mOnHideListener.onHide();
+ }
+ mIsDialogShowing = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+ switch (action) {
+ case BluetoothAdapter.ACTION_BLE_STATE_CHANGED:
+ logd("Received ACTION_BLE_STATE_CHANGED");
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
+ if (state == BluetoothAdapter.STATE_BLE_ON) {
+ logd("Received BLE_ON");
+ mUnlockingText.setText(R.string.unlock_dialog_message_start);
+ }
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ int uid = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ if (uid == mUid) {
+ logd("IHU unlocked");
+ hideUnlockDialog(/* notifyOnHideListener= */false);
+ } else {
+ Log.e(TAG, "Received ACTION_USER_UNLOCKED for unexpected uid: " + uid);
+ }
+ break;
+ default:
+ Log.e(TAG, "Encountered unexpected action when attempting to set "
+ + "unlock state message: " + action);
+ }
+ }
+
+ // Set button text based on security lock type
+ private void setButtonText() {
+ LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext);
+ int passwordQuality = lockPatternUtils.getActivePasswordQuality(mUid);
+ switch (passwordQuality) {
+ // PIN
+ case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
+ // Pattern
+ case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
+ mButton.setText(R.string.unlock_dialog_button_text_pattern);
+ break;
+ // Password
+ case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
+ mButton.setText(R.string.unlock_dialog_button_text_password);
+ break;
+ default:
+ Log.e(TAG, "Encountered unexpected security type when attempting to set "
+ + "button text:" + passwordQuality);
+ }
+ }
+
+ private WindowManager.LayoutParams createLayoutParams() {
+ return new WindowManager.LayoutParams(
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+ WindowManager.LayoutParams.FLAG_FULLSCREEN
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
+ PixelFormat.TRANSLUCENT
+ );
+ }
+
+ private void logd(String message) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, message);
+ }
+ }
+
+ /**
+ * Listener used to notify when the dialog is hidden
+ */
+ interface OnHideListener {
+ void onHide();
+ }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 0a167d9..7cd6adb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -18,29 +18,60 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.car.trust.CarTrustAgentEnrollmentManager;
+import android.car.userlib.CarUserManagerHelper;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
import android.view.View;
import android.view.ViewStub;
import androidx.recyclerview.widget.GridLayoutManager;
import com.android.systemui.R;
+import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
/**
* Manages the fullscreen user switcher.
*/
public class FullscreenUserSwitcher {
+ private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
+ // Because user 0 is headless, user count for single user is 2
+ private static final int NUMBER_OF_BACKGROUND_USERS = 1;
private final UserGridRecyclerView mUserGridView;
private final View mParent;
private final int mShortAnimDuration;
private final CarStatusBar mStatusBar;
+ private final Context mContext;
+ private final UserManager mUserManager;
+ private final CarTrustAgentEnrollmentManager mEnrollmentManager;
+ private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
+ private UserGridRecyclerView.UserRecord mSelectedUser;
+ private CarUserManagerHelper mCarUserManagerHelper;
+ private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
+ }
+ showDialogForInitialUser();
+ mContext.unregisterReceiver(mUserUnlockReceiver);
+ }
+ };
- public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
+
+ public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
+ CarTrustAgentEnrollmentManager enrollmentManager, Context context) {
mStatusBar = statusBar;
mParent = containerStub.inflate();
- // Hide the user grid by default. It will only be made visible by clicking on a cancel
- // button in a bouncer.
- hide();
+ mEnrollmentManager = enrollmentManager;
+ mContext = context;
+
View container = mParent.findViewById(R.id.container);
// Initialize user grid.
@@ -50,9 +81,51 @@
mUserGridView.setLayoutManager(layoutManager);
mUserGridView.buildAdapter();
mUserGridView.setUserSelectionListener(this::onUserSelected);
+ mCarUserManagerHelper = new CarUserManagerHelper(context);
+ mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
+ mUserManager = mContext.getSystemService(UserManager.class);
mShortAnimDuration = container.getResources()
.getInteger(android.R.integer.config_shortAnimTime);
+ IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+ if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
+ // User0 is unlocked, switched to the initial user
+ showDialogForInitialUser();
+ } else {
+ // listen to USER_UNLOCKED
+ mContext.registerReceiverAsUser(mUserUnlockReceiver,
+ UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
+ filter,
+ /* broadcastPermission= */ null,
+ /* scheduler */ null);
+ }
+ }
+
+ private void showDialogForInitialUser() {
+ int initialUser = mCarUserManagerHelper.getInitialUser();
+ UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
+ mSelectedUser = new UserRecord(initialUserInfo,
+ /* isStartGuestSession= */ false,
+ /* isAddUser= */ false,
+ /* isForeground= */ true);
+ // For single user without trusted device, hide the user switcher.
+ if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
+ dismissUserSwitcher();
+ return;
+ }
+ // Show unlock dialog for initial user
+ if (hasTrustedDevice(initialUser)) {
+ mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
+ () -> dismissUserSwitcher());
+ }
+ }
+
+ /**
+ * Check if there is only one possible user to login in.
+ * In a Multi-User system there is always one background user (user 0)
+ */
+ private boolean hasMultipleUsers() {
+ return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
}
/**
@@ -77,14 +150,33 @@
}
/**
- * Every time user clicks on an item in the switcher, we hide the switcher, either
- * gradually or immediately.
+ * Every time user clicks on an item in the switcher, if the clicked user has no trusted device,
+ * we hide the switcher, either gradually or immediately.
*
- * We dismiss the entire keyguard if user clicked on the foreground user (user we're already
- * logged in as).
+ * If the user has trusted device, we show an unlock dialog to notify user the unlock state.
+ * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
+ *
+ * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground
+ * user (user we're already logged in as).
*/
private void onUserSelected(UserGridRecyclerView.UserRecord record) {
- if (record.mIsForeground) {
+ mSelectedUser = record;
+ if (hasTrustedDevice(record.mInfo.id)) {
+ mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, () -> dismissUserSwitcher());
+ return;
+ }
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
+ }
+ dismissUserSwitcher();
+ }
+
+ private void dismissUserSwitcher() {
+ if (mSelectedUser == null) {
+ Log.e(TAG, "Request to dismiss user switcher, but no user selected");
+ return;
+ }
+ if (mSelectedUser.mIsForeground) {
hide();
mStatusBar.dismissKeyguard();
return;
@@ -106,4 +198,8 @@
});
}
+
+ private boolean hasTrustedDevice(int uid) {
+ return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
+ }
}