blob: 31aced02b15e32d957d518dd24845164a1573907 [file] [log] [blame]
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -07001/*
Brad Stenning8d1a51c2018-11-20 17:34:16 -08002 * Copyright (C) 2018 The Android Open Source Project
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -07003 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
Brad Stenning8d1a51c2018-11-20 17:34:16 -080014 * limitations under the License.
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070015 */
16
Rakesh Iyer2790a372016-01-22 15:33:39 -080017package com.android.systemui.statusbar.car;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070018
Rakesh Iyer33d6ce42017-05-30 11:02:18 -070019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Keun young Park9e395bb2019-10-11 20:00:22 -070021import android.car.Car;
Erin Yan8182bfd2019-08-14 12:03:12 -070022import android.car.trust.CarTrustAgentEnrollmentManager;
23import android.car.userlib.CarUserManagerHelper;
24import android.content.BroadcastReceiver;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070025import android.content.Context;
Erin Yan8182bfd2019-08-14 12:03:12 -070026import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.UserInfo;
29import android.os.UserHandle;
30import android.os.UserManager;
31import android.util.Log;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070032import android.view.View;
33import android.view.ViewStub;
34
Aurimas Liutikasfd52c142018-04-17 09:50:46 -070035import androidx.recyclerview.widget.GridLayoutManager;
jovanakb8030512018-04-11 15:20:16 -070036
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070037import com.android.systemui.R;
Erin Yan5eb60a22019-09-04 11:55:30 -070038import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
Erin Yan8182bfd2019-08-14 12:03:12 -070039import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070040
41/**
42 * Manages the fullscreen user switcher.
43 */
44public class FullscreenUserSwitcher {
Erin Yan8182bfd2019-08-14 12:03:12 -070045 private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
46 // Because user 0 is headless, user count for single user is 2
47 private static final int NUMBER_OF_BACKGROUND_USERS = 1;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070048 private final UserGridRecyclerView mUserGridView;
jovanak604ad512018-08-14 18:41:27 -070049 private final View mParent;
Rakesh Iyer33d6ce42017-05-30 11:02:18 -070050 private final int mShortAnimDuration;
jovanak604ad512018-08-14 18:41:27 -070051 private final CarStatusBar mStatusBar;
Erin Yan8182bfd2019-08-14 12:03:12 -070052 private final Context mContext;
53 private final UserManager mUserManager;
Keun young Park9e395bb2019-10-11 20:00:22 -070054 private CarTrustAgentEnrollmentManager mEnrollmentManager;
Erin Yan8182bfd2019-08-14 12:03:12 -070055 private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
56 private UserGridRecyclerView.UserRecord mSelectedUser;
57 private CarUserManagerHelper mCarUserManagerHelper;
58 private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
59 @Override
60 public void onReceive(Context context, Intent intent) {
61 if (Log.isLoggable(TAG, Log.DEBUG)) {
62 Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
63 }
64 showDialogForInitialUser();
65 mContext.unregisterReceiver(mUserUnlockReceiver);
66 }
67 };
Keun young Park9e395bb2019-10-11 20:00:22 -070068 private final Car mCar;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070069
Keun young Park9e395bb2019-10-11 20:00:22 -070070 public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub, Context context) {
jovanak0535abc2018-04-10 15:14:50 -070071 mStatusBar = statusBar;
Rakesh Iyer33d6ce42017-05-30 11:02:18 -070072 mParent = containerStub.inflate();
Erin Yan8182bfd2019-08-14 12:03:12 -070073 mContext = context;
74
jovanak604ad512018-08-14 18:41:27 -070075 View container = mParent.findViewById(R.id.container);
76
77 // Initialize user grid.
78 mUserGridView = container.findViewById(R.id.user_grid);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070079 GridLayoutManager layoutManager = new GridLayoutManager(context,
Brad Stenning8d1a51c2018-11-20 17:34:16 -080080 context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
Heemin Seog0d5e0182019-03-13 13:49:24 -070081 mUserGridView.setLayoutManager(layoutManager);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070082 mUserGridView.buildAdapter();
jovanak0535abc2018-04-10 15:14:50 -070083 mUserGridView.setUserSelectionListener(this::onUserSelected);
Erin Yan8182bfd2019-08-14 12:03:12 -070084 mCarUserManagerHelper = new CarUserManagerHelper(context);
85 mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
86 mUserManager = mContext.getSystemService(UserManager.class);
Rakesh Iyer5b3278f2017-04-19 11:28:26 -070087
Keun young Park9e395bb2019-10-11 20:00:22 -070088 mCar = Car.createCar(mContext, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_DO_NOT_WAIT,
89 (car, ready) -> {
90 if (!ready) {
91 return;
92 }
93 mEnrollmentManager = (CarTrustAgentEnrollmentManager) car
94 .getCarManager(Car.CAR_TRUST_AGENT_ENROLLMENT_SERVICE);
95 });
96
jovanak604ad512018-08-14 18:41:27 -070097 mShortAnimDuration = container.getResources()
Brad Stenning8d1a51c2018-11-20 17:34:16 -080098 .getInteger(android.R.integer.config_shortAnimTime);
Erin Yan8182bfd2019-08-14 12:03:12 -070099 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
100 if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
101 // User0 is unlocked, switched to the initial user
102 showDialogForInitialUser();
103 } else {
104 // listen to USER_UNLOCKED
105 mContext.registerReceiverAsUser(mUserUnlockReceiver,
106 UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
107 filter,
108 /* broadcastPermission= */ null,
109 /* scheduler */ null);
110 }
111 }
112
113 private void showDialogForInitialUser() {
114 int initialUser = mCarUserManagerHelper.getInitialUser();
115 UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
116 mSelectedUser = new UserRecord(initialUserInfo,
117 /* isStartGuestSession= */ false,
118 /* isAddUser= */ false,
119 /* isForeground= */ true);
120 // For single user without trusted device, hide the user switcher.
121 if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
122 dismissUserSwitcher();
123 return;
124 }
125 // Show unlock dialog for initial user
126 if (hasTrustedDevice(initialUser)) {
127 mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
Erin Yan5eb60a22019-09-04 11:55:30 -0700128 mOnHideListener);
Erin Yan8182bfd2019-08-14 12:03:12 -0700129 }
130 }
131
132 /**
133 * Check if there is only one possible user to login in.
134 * In a Multi-User system there is always one background user (user 0)
135 */
136 private boolean hasMultipleUsers() {
137 return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700138 }
139
jovanak604ad512018-08-14 18:41:27 -0700140 /**
141 * Makes user grid visible.
142 */
jovanak0535abc2018-04-10 15:14:50 -0700143 public void show() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700144 mParent.setVisibility(View.VISIBLE);
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700145 }
146
jovanak604ad512018-08-14 18:41:27 -0700147 /**
148 * Hides the user grid.
149 */
150 public void hide() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700151 mParent.setVisibility(View.INVISIBLE);
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700152 }
153
jovanak604ad512018-08-14 18:41:27 -0700154 /**
155 * @return {@code true} if user grid is visible, {@code false} otherwise.
156 */
157 public boolean isVisible() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700158 return mParent.getVisibility() == View.VISIBLE;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700159 }
160
jovanak604ad512018-08-14 18:41:27 -0700161 /**
Erin Yan8182bfd2019-08-14 12:03:12 -0700162 * Every time user clicks on an item in the switcher, if the clicked user has no trusted device,
163 * we hide the switcher, either gradually or immediately.
jovanak604ad512018-08-14 18:41:27 -0700164 *
Erin Yan8182bfd2019-08-14 12:03:12 -0700165 * If the user has trusted device, we show an unlock dialog to notify user the unlock state.
166 * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
167 *
168 * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground
169 * user (user we're already logged in as).
jovanak604ad512018-08-14 18:41:27 -0700170 */
171 private void onUserSelected(UserGridRecyclerView.UserRecord record) {
Erin Yan8182bfd2019-08-14 12:03:12 -0700172 mSelectedUser = record;
173 if (hasTrustedDevice(record.mInfo.id)) {
Erin Yan5eb60a22019-09-04 11:55:30 -0700174 mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
Erin Yan8182bfd2019-08-14 12:03:12 -0700175 return;
176 }
177 if (Log.isLoggable(TAG, Log.DEBUG)) {
178 Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
179 }
180 dismissUserSwitcher();
181 }
182
183 private void dismissUserSwitcher() {
184 if (mSelectedUser == null) {
185 Log.e(TAG, "Request to dismiss user switcher, but no user selected");
186 return;
187 }
188 if (mSelectedUser.mIsForeground) {
jovanak604ad512018-08-14 18:41:27 -0700189 hide();
190 mStatusBar.dismissKeyguard();
191 return;
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700192 }
jovanak604ad512018-08-14 18:41:27 -0700193 // Switching is about to happen, since it takes time, fade out the switcher gradually.
194 fadeOut();
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700195 }
196
jovanak604ad512018-08-14 18:41:27 -0700197 private void fadeOut() {
198 mUserGridView.animate()
jovanak9c177202018-04-20 12:35:09 -0700199 .alpha(0.0f)
200 .setDuration(mShortAnimDuration)
201 .setListener(new AnimatorListenerAdapter() {
202 @Override
203 public void onAnimationEnd(Animator animation) {
jovanak604ad512018-08-14 18:41:27 -0700204 hide();
205 mUserGridView.setAlpha(1.0f);
jovanak9c177202018-04-20 12:35:09 -0700206 }
207 });
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700208
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700209 }
Erin Yan8182bfd2019-08-14 12:03:12 -0700210
211 private boolean hasTrustedDevice(int uid) {
Keun young Park9e395bb2019-10-11 20:00:22 -0700212 if (mEnrollmentManager == null) { // car service not ready, so it cannot be available.
213 return false;
214 }
Erin Yan8182bfd2019-08-14 12:03:12 -0700215 return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
216 }
Erin Yan5eb60a22019-09-04 11:55:30 -0700217
218 private OnHideListener mOnHideListener = new OnHideListener() {
219 @Override
220 public void onHide(boolean dismissUserSwitcher) {
221 if (dismissUserSwitcher) {
222 dismissUserSwitcher();
223 } else {
224 // Re-draw the parent view, otherwise the unlock dialog will not be removed from
225 // the screen immediately.
226 mParent.invalidate();
227 }
228
229 }
230 };
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800231}