blob: 0f7c1ee8ea7e1e82375f88fc06b5186c5b469dcd [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;
Erin Yan8182bfd2019-08-14 12:03:12 -070021import android.car.trust.CarTrustAgentEnrollmentManager;
22import android.car.userlib.CarUserManagerHelper;
23import android.content.BroadcastReceiver;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070024import android.content.Context;
Erin Yan8182bfd2019-08-14 12:03:12 -070025import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.pm.UserInfo;
28import android.os.UserHandle;
29import android.os.UserManager;
30import android.util.Log;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070031import android.view.View;
32import android.view.ViewStub;
33
Aurimas Liutikasfd52c142018-04-17 09:50:46 -070034import androidx.recyclerview.widget.GridLayoutManager;
jovanakb8030512018-04-11 15:20:16 -070035
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070036import com.android.systemui.R;
Erin Yan5eb60a22019-09-04 11:55:30 -070037import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
Erin Yan8182bfd2019-08-14 12:03:12 -070038import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070039
40/**
41 * Manages the fullscreen user switcher.
42 */
43public class FullscreenUserSwitcher {
Erin Yan8182bfd2019-08-14 12:03:12 -070044 private static final String TAG = FullscreenUserSwitcher.class.getSimpleName();
45 // Because user 0 is headless, user count for single user is 2
46 private static final int NUMBER_OF_BACKGROUND_USERS = 1;
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070047 private final UserGridRecyclerView mUserGridView;
jovanak604ad512018-08-14 18:41:27 -070048 private final View mParent;
Rakesh Iyer33d6ce42017-05-30 11:02:18 -070049 private final int mShortAnimDuration;
jovanak604ad512018-08-14 18:41:27 -070050 private final CarStatusBar mStatusBar;
Erin Yan8182bfd2019-08-14 12:03:12 -070051 private final Context mContext;
52 private final UserManager mUserManager;
53 private final CarTrustAgentEnrollmentManager mEnrollmentManager;
54 private CarTrustAgentUnlockDialogHelper mUnlockDialogHelper;
55 private UserGridRecyclerView.UserRecord mSelectedUser;
56 private CarUserManagerHelper mCarUserManagerHelper;
57 private final BroadcastReceiver mUserUnlockReceiver = new BroadcastReceiver() {
58 @Override
59 public void onReceive(Context context, Intent intent) {
60 if (Log.isLoggable(TAG, Log.DEBUG)) {
61 Log.d(TAG, "user 0 is unlocked, SharedPreference is accessible.");
62 }
63 showDialogForInitialUser();
64 mContext.unregisterReceiver(mUserUnlockReceiver);
65 }
66 };
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -070067
Erin Yan8182bfd2019-08-14 12:03:12 -070068
69 public FullscreenUserSwitcher(CarStatusBar statusBar, ViewStub containerStub,
70 CarTrustAgentEnrollmentManager enrollmentManager, 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 mEnrollmentManager = enrollmentManager;
74 mContext = context;
75
jovanak604ad512018-08-14 18:41:27 -070076 View container = mParent.findViewById(R.id.container);
77
78 // Initialize user grid.
79 mUserGridView = container.findViewById(R.id.user_grid);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070080 GridLayoutManager layoutManager = new GridLayoutManager(context,
Brad Stenning8d1a51c2018-11-20 17:34:16 -080081 context.getResources().getInteger(R.integer.user_fullscreen_switcher_num_col));
Heemin Seog0d5e0182019-03-13 13:49:24 -070082 mUserGridView.setLayoutManager(layoutManager);
Aarthi Balachanderd8bf2492018-03-30 11:15:59 -070083 mUserGridView.buildAdapter();
jovanak0535abc2018-04-10 15:14:50 -070084 mUserGridView.setUserSelectionListener(this::onUserSelected);
Erin Yan8182bfd2019-08-14 12:03:12 -070085 mCarUserManagerHelper = new CarUserManagerHelper(context);
86 mUnlockDialogHelper = new CarTrustAgentUnlockDialogHelper(mContext);
87 mUserManager = mContext.getSystemService(UserManager.class);
Rakesh Iyer5b3278f2017-04-19 11:28:26 -070088
jovanak604ad512018-08-14 18:41:27 -070089 mShortAnimDuration = container.getResources()
Brad Stenning8d1a51c2018-11-20 17:34:16 -080090 .getInteger(android.R.integer.config_shortAnimTime);
Erin Yan8182bfd2019-08-14 12:03:12 -070091 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
92 if (mUserManager.isUserUnlocked(UserHandle.USER_SYSTEM)) {
93 // User0 is unlocked, switched to the initial user
94 showDialogForInitialUser();
95 } else {
96 // listen to USER_UNLOCKED
97 mContext.registerReceiverAsUser(mUserUnlockReceiver,
98 UserHandle.getUserHandleForUid(UserHandle.USER_SYSTEM),
99 filter,
100 /* broadcastPermission= */ null,
101 /* scheduler */ null);
102 }
103 }
104
105 private void showDialogForInitialUser() {
106 int initialUser = mCarUserManagerHelper.getInitialUser();
107 UserInfo initialUserInfo = mUserManager.getUserInfo(initialUser);
108 mSelectedUser = new UserRecord(initialUserInfo,
109 /* isStartGuestSession= */ false,
110 /* isAddUser= */ false,
111 /* isForeground= */ true);
112 // For single user without trusted device, hide the user switcher.
113 if (!hasMultipleUsers() && !hasTrustedDevice(initialUser)) {
114 dismissUserSwitcher();
115 return;
116 }
117 // Show unlock dialog for initial user
118 if (hasTrustedDevice(initialUser)) {
119 mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
Erin Yan5eb60a22019-09-04 11:55:30 -0700120 mOnHideListener);
Erin Yan8182bfd2019-08-14 12:03:12 -0700121 }
122 }
123
124 /**
125 * Check if there is only one possible user to login in.
126 * In a Multi-User system there is always one background user (user 0)
127 */
128 private boolean hasMultipleUsers() {
129 return mUserManager.getUserCount() > NUMBER_OF_BACKGROUND_USERS + 1;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700130 }
131
jovanak604ad512018-08-14 18:41:27 -0700132 /**
133 * Makes user grid visible.
134 */
jovanak0535abc2018-04-10 15:14:50 -0700135 public void show() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700136 mParent.setVisibility(View.VISIBLE);
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700137 }
138
jovanak604ad512018-08-14 18:41:27 -0700139 /**
140 * Hides the user grid.
141 */
142 public void hide() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700143 mParent.setVisibility(View.INVISIBLE);
Aarthi Balachander0a427ef2018-07-13 15:00:58 -0700144 }
145
jovanak604ad512018-08-14 18:41:27 -0700146 /**
147 * @return {@code true} if user grid is visible, {@code false} otherwise.
148 */
149 public boolean isVisible() {
Brad Stenning3b0d7642019-03-28 11:04:30 -0700150 return mParent.getVisibility() == View.VISIBLE;
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700151 }
152
jovanak604ad512018-08-14 18:41:27 -0700153 /**
Erin Yan8182bfd2019-08-14 12:03:12 -0700154 * Every time user clicks on an item in the switcher, if the clicked user has no trusted device,
155 * we hide the switcher, either gradually or immediately.
jovanak604ad512018-08-14 18:41:27 -0700156 *
Erin Yan8182bfd2019-08-14 12:03:12 -0700157 * If the user has trusted device, we show an unlock dialog to notify user the unlock state.
158 * When the unlock dialog is dismissed by user, we hide the unlock dialog and the switcher.
159 *
160 * We dismiss the entire keyguard when we hide the switcher if user clicked on the foreground
161 * user (user we're already logged in as).
jovanak604ad512018-08-14 18:41:27 -0700162 */
163 private void onUserSelected(UserGridRecyclerView.UserRecord record) {
Erin Yan8182bfd2019-08-14 12:03:12 -0700164 mSelectedUser = record;
165 if (hasTrustedDevice(record.mInfo.id)) {
Erin Yan5eb60a22019-09-04 11:55:30 -0700166 mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
Erin Yan8182bfd2019-08-14 12:03:12 -0700167 return;
168 }
169 if (Log.isLoggable(TAG, Log.DEBUG)) {
170 Log.d(TAG, "no trusted device enrolled for uid: " + record.mInfo.id);
171 }
172 dismissUserSwitcher();
173 }
174
175 private void dismissUserSwitcher() {
176 if (mSelectedUser == null) {
177 Log.e(TAG, "Request to dismiss user switcher, but no user selected");
178 return;
179 }
180 if (mSelectedUser.mIsForeground) {
jovanak604ad512018-08-14 18:41:27 -0700181 hide();
182 mStatusBar.dismissKeyguard();
183 return;
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700184 }
jovanak604ad512018-08-14 18:41:27 -0700185 // Switching is about to happen, since it takes time, fade out the switcher gradually.
186 fadeOut();
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700187 }
188
jovanak604ad512018-08-14 18:41:27 -0700189 private void fadeOut() {
190 mUserGridView.animate()
jovanak9c177202018-04-20 12:35:09 -0700191 .alpha(0.0f)
192 .setDuration(mShortAnimDuration)
193 .setListener(new AnimatorListenerAdapter() {
194 @Override
195 public void onAnimationEnd(Animator animation) {
jovanak604ad512018-08-14 18:41:27 -0700196 hide();
197 mUserGridView.setAlpha(1.0f);
jovanak9c177202018-04-20 12:35:09 -0700198 }
199 });
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700200
Rakesh Iyer33d6ce42017-05-30 11:02:18 -0700201 }
Erin Yan8182bfd2019-08-14 12:03:12 -0700202
203 private boolean hasTrustedDevice(int uid) {
204 return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
205 }
Erin Yan5eb60a22019-09-04 11:55:30 -0700206
207 private OnHideListener mOnHideListener = new OnHideListener() {
208 @Override
209 public void onHide(boolean dismissUserSwitcher) {
210 if (dismissUserSwitcher) {
211 dismissUserSwitcher();
212 } else {
213 // Re-draw the parent view, otherwise the unlock dialog will not be removed from
214 // the screen immediately.
215 mParent.invalidate();
216 }
217
218 }
219 };
Brad Stenning8d1a51c2018-11-20 17:34:16 -0800220}