blob: f2d2faed6a42c53b52047c58df81fb5a364b50ec [file] [log] [blame]
Adrian Roos00a0b1f2014-07-16 16:44:49 +02001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
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
14 * limitations under the License
15 */
16
17package com.android.systemui.statusbar.policy;
18
Xiaohui Chenec015d52016-05-05 16:28:35 -070019import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
Lucas Dupin8968f6a2019-08-09 17:41:15 -070020import static com.android.systemui.DejankUtils.whitelistIpcs;
Xiaohui Chenec015d52016-05-05 16:28:35 -070021
Adrian Roos00a0b1f2014-07-16 16:44:49 +020022import android.app.ActivityManager;
Adrian Roos50052442014-07-17 23:35:18 +020023import android.app.Dialog;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020024import android.content.BroadcastReceiver;
25import android.content.Context;
Adrian Roos50052442014-07-17 23:35:18 +020026import android.content.DialogInterface;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020027import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.UserInfo;
Jason Monk2daf62c2014-07-31 14:35:06 -040030import android.database.ContentObserver;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020031import android.graphics.Bitmap;
Jason Monk5a655022017-05-24 13:55:23 -040032import android.graphics.PorterDuff.Mode;
Adrian Roosccdff622014-08-06 00:07:18 +020033import android.graphics.drawable.Drawable;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020034import android.os.AsyncTask;
Jason Monk2daf62c2014-07-31 14:35:06 -040035import android.os.Handler;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020036import android.os.RemoteException;
Adrian Roose9c7d432014-07-17 18:27:38 +020037import android.os.UserHandle;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020038import android.os.UserManager;
Jason Monk2daf62c2014-07-31 14:35:06 -040039import android.provider.Settings;
Fyodor Kupolov523c4042016-02-24 15:03:13 -080040import android.telephony.PhoneStateListener;
41import android.telephony.TelephonyManager;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020042import android.util.Log;
Adrian Roose9c7d432014-07-17 18:27:38 +020043import android.util.SparseArray;
Adrian Roos88b11932015-07-22 14:59:48 -070044import android.util.SparseBooleanArray;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020045import android.view.View;
46import android.view.ViewGroup;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020047import android.widget.BaseAdapter;
48
Jason Monk88529052016-11-04 13:29:58 -040049import com.android.internal.annotations.VisibleForTesting;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070050import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010051import com.android.internal.util.UserIcons;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070052import com.android.settingslib.RestrictedLockUtilsInternal;
Jason Monk5a655022017-05-24 13:55:23 -040053import com.android.settingslib.Utils;
Evan Laird97eaacb2018-09-04 15:06:37 -040054import com.android.systemui.Dumpable;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010055import com.android.systemui.GuestResumeSessionReceiver;
Jason Monk24f8a752017-06-30 11:31:03 -040056import com.android.systemui.Prefs;
57import com.android.systemui.Prefs.Key;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010058import com.android.systemui.R;
Winson3c2c34b2016-04-04 17:47:41 -070059import com.android.systemui.SystemUISecondaryUserService;
Dave Mankofff4736812019-10-18 17:25:50 -040060import com.android.systemui.dagger.qualifiers.MainHandler;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070061import com.android.systemui.plugins.ActivityStarter;
Jason Monke5b770e2017-03-03 21:49:29 -050062import com.android.systemui.plugins.qs.DetailAdapter;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010063import com.android.systemui.qs.tiles.UserDetailView;
64import com.android.systemui.statusbar.phone.SystemUIDialog;
65
Adrian Roos00a0b1f2014-07-16 16:44:49 +020066import java.io.FileDescriptor;
67import java.io.PrintWriter;
68import java.lang.ref.WeakReference;
69import java.util.ArrayList;
70import java.util.List;
71
Jason Monk196d6392018-12-20 13:25:34 -050072import javax.inject.Inject;
Jason Monk196d6392018-12-20 13:25:34 -050073import javax.inject.Singleton;
74
Adrian Roos00a0b1f2014-07-16 16:44:49 +020075/**
76 * Keeps a list of all users on the device for user switching.
77 */
Jason Monk196d6392018-12-20 13:25:34 -050078@Singleton
Evan Laird97eaacb2018-09-04 15:06:37 -040079public class UserSwitcherController implements Dumpable {
Adrian Roos00a0b1f2014-07-16 16:44:49 +020080
81 private static final String TAG = "UserSwitcherController";
Adrian Roos50052442014-07-17 23:35:18 +020082 private static final boolean DEBUG = false;
Jason Monk2daf62c2014-07-31 14:35:06 -040083 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
84 "lockscreenSimpleUserSwitcher";
Adrian Roos88b11932015-07-22 14:59:48 -070085 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020086
Amith Yamasanid81f8272015-07-23 17:15:45 -070087 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
88
Evan Rosky13a58a92016-07-27 15:51:09 -070089 protected final Context mContext;
90 protected final UserManager mUserManager;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020091 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020092 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
93 = new GuestResumeSessionReceiver();
Lucas Dupinc8f16e82019-09-17 18:24:50 -040094 private final KeyguardStateController mKeyguardStateController;
Evan Rosky13a58a92016-07-27 15:51:09 -070095 protected final Handler mHandler;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000096 private final ActivityStarter mActivityStarter;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020097
98 private ArrayList<UserRecord> mUsers = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020099 private Dialog mExitGuestDialog;
Adrian Roos0c6763a2014-09-08 19:11:00 +0200100 private Dialog mAddUserDialog;
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700101 private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
Evan Rosky37c76802016-06-30 17:59:39 -0700102 private boolean mResumeUserOnGuestLogout = true;
Adrian Roosccdff622014-08-06 00:07:18 +0200103 private boolean mSimpleUserSwitcher;
104 private boolean mAddUsersWhenLocked;
Adrian Roos88b11932015-07-22 14:59:48 -0700105 private boolean mPauseRefreshUsers;
Winson3c2c34b2016-04-04 17:47:41 -0700106 private int mSecondaryUser = UserHandle.USER_NULL;
107 private Intent mSecondaryUserServiceIntent;
Adrian Roos88b11932015-07-22 14:59:48 -0700108 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200109
Jason Monk196d6392018-12-20 13:25:34 -0500110 @Inject
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400111 public UserSwitcherController(Context context, KeyguardStateController keyguardStateController,
Dave Mankofff4736812019-10-18 17:25:50 -0400112 @MainHandler Handler handler, ActivityStarter activityStarter) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200113 mContext = context;
Christophe Koessler32d5d502019-05-23 11:18:30 -0700114 if (!UserManager.isGuestUserEphemeral()) {
115 mGuestResumeSessionReceiver.register(context);
116 }
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400117 mKeyguardStateController = keyguardStateController;
Adrian Roos88b11932015-07-22 14:59:48 -0700118 mHandler = handler;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000119 mActivityStarter = activityStarter;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200120 mUserManager = UserManager.get(context);
121 IntentFilter filter = new IntentFilter();
122 filter.addAction(Intent.ACTION_USER_ADDED);
123 filter.addAction(Intent.ACTION_USER_REMOVED);
124 filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
125 filter.addAction(Intent.ACTION_USER_SWITCHED);
Amith Yamasaniad2e4bf2016-04-26 14:35:54 -0700126 filter.addAction(Intent.ACTION_USER_STOPPED);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800127 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700128 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Adrian Roose9c7d432014-07-17 18:27:38 +0200129 null /* permission */, null /* scheduler */);
Jason Monk2daf62c2014-07-31 14:35:06 -0400130
Winson3c2c34b2016-04-04 17:47:41 -0700131 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
132
Amith Yamasanid81f8272015-07-23 17:15:45 -0700133 filter = new IntentFilter();
Amith Yamasani5891a342015-07-24 13:50:58 -0700134 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Amith Yamasanid81f8272015-07-23 17:15:45 -0700135 PERMISSION_SELF, null /* scheduler */);
Adrian Roosccdff622014-08-06 00:07:18 +0200136
Jason Monk2daf62c2014-07-31 14:35:06 -0400137 mContext.getContentResolver().registerContentObserver(
138 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
Adrian Roosccdff622014-08-06 00:07:18 +0200139 mSettingsObserver);
140 mContext.getContentResolver().registerContentObserver(
141 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
142 mSettingsObserver);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800143 mContext.getContentResolver().registerContentObserver(
144 Settings.Global.getUriFor(
145 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED),
146 true, mSettingsObserver);
Adrian Roosccdff622014-08-06 00:07:18 +0200147 // Fetch initial values.
148 mSettingsObserver.onChange(false);
149
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400150 keyguardStateController.addCallback(mCallback);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800151 listenForCallState();
Jason Monk2daf62c2014-07-31 14:35:06 -0400152
Adrian Roose9c7d432014-07-17 18:27:38 +0200153 refreshUsers(UserHandle.USER_NULL);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200154 }
155
Adrian Roose9c7d432014-07-17 18:27:38 +0200156 /**
157 * Refreshes users from UserManager.
158 *
159 * The pictures are only loaded if they have not been loaded yet.
160 *
161 * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
162 */
Amith Yamasani95ab7842014-08-11 17:09:26 -0700163 @SuppressWarnings("unchecked")
Adrian Roose9c7d432014-07-17 18:27:38 +0200164 private void refreshUsers(int forcePictureLoadForId) {
Adrian Roos88b11932015-07-22 14:59:48 -0700165 if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
166 if (forcePictureLoadForId != UserHandle.USER_NULL) {
167 mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
168 }
169
170 if (mPauseRefreshUsers) {
171 return;
172 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000173
Adrian Roosd390b892016-05-05 10:50:49 -0400174 boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
Adrian Roosc5db3902014-11-21 13:54:04 +0000175 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
176 final int N = mUsers.size();
177 for (int i = 0; i < N; i++) {
178 UserRecord r = mUsers.get(i);
Adrian Roosd390b892016-05-05 10:50:49 -0400179 if (r == null || r.picture == null || r.info == null || forceAllUsers
180 || mForcePictureLoadForUserId.get(r.info.id)) {
Adrian Roosc5db3902014-11-21 13:54:04 +0000181 continue;
Adrian Roose9c7d432014-07-17 18:27:38 +0200182 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000183 bitmaps.put(r.info.id, r.picture);
Adrian Roose9c7d432014-07-17 18:27:38 +0200184 }
Adrian Roos88b11932015-07-22 14:59:48 -0700185 mForcePictureLoadForUserId.clear();
Adrian Roose9c7d432014-07-17 18:27:38 +0200186
Adrian Roosccdff622014-08-06 00:07:18 +0200187 final boolean addUsersWhenLocked = mAddUsersWhenLocked;
Adrian Roose9c7d432014-07-17 18:27:38 +0200188 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
189 @SuppressWarnings("unchecked")
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200190 @Override
Adrian Roose9c7d432014-07-17 18:27:38 +0200191 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
192 final SparseArray<Bitmap> bitmaps = params[0];
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200193 List<UserInfo> infos = mUserManager.getUsers(true);
194 if (infos == null) {
195 return null;
196 }
197 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
198 int currentId = ActivityManager.getCurrentUser();
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800199 boolean canSwitchUsers = mUserManager.canSwitchUsers();
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700200 UserInfo currentUserInfo = null;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200201 UserRecord guestRecord = null;
202
203 for (UserInfo info : infos) {
204 boolean isCurrent = currentId == info.id;
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700205 if (isCurrent) {
206 currentUserInfo = info;
207 }
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800208 boolean switchToEnabled = canSwitchUsers || isCurrent;
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100209 if (info.isEnabled()) {
210 if (info.isGuest()) {
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800211 // Tapping guest icon triggers remove and a user switch therefore
212 // the icon shouldn't be enabled even if the user is current
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100213 guestRecord = new UserRecord(info, null /* picture */,
214 true /* isGuest */, isCurrent, false /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800215 false /* isRestricted */, canSwitchUsers);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100216 } else if (info.supportsSwitchToByUser()) {
217 Bitmap picture = bitmaps.get(info.id);
218 if (picture == null) {
219 picture = mUserManager.getUserIcon(info.id);
Adrian Rooscba0faa2014-11-17 17:41:28 +0100220
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100221 if (picture != null) {
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700222 int avatarSize = mContext.getResources()
223 .getDimensionPixelSize(R.dimen.max_avatar_size);
224 picture = Bitmap.createScaledBitmap(
225 picture, avatarSize, avatarSize, true);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100226 }
Adrian Rooscba0faa2014-11-17 17:41:28 +0100227 }
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100228 int index = isCurrent ? 0 : records.size();
229 records.add(index, new UserRecord(info, picture, false /* isGuest */,
230 isCurrent, false /* isAddUser */, false /* isRestricted */,
231 switchToEnabled));
Dan Sandler4d75c072014-07-17 16:01:28 -0400232 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200233 }
234 }
Jason Monk24f8a752017-06-30 11:31:03 -0400235 if (records.size() > 1 || guestRecord != null) {
236 Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
237 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200238
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000239 boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
240 UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700241 boolean currentUserCanCreateUsers = currentUserInfo != null
242 && (currentUserInfo.isAdmin()
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000243 || currentUserInfo.id == UserHandle.USER_SYSTEM)
244 && systemCanCreateUsers;
245 boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
246 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Adrian Roosccdff622014-08-06 00:07:18 +0200247 && guestRecord == null;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000248 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Amith Yamasani95ab7842014-08-11 17:09:26 -0700249 && mUserManager.canAddMoreUsers();
Adrian Roosccdff622014-08-06 00:07:18 +0200250 boolean createIsRestricted = !addUsersWhenLocked;
251
Jason Monk2daf62c2014-07-31 14:35:06 -0400252 if (!mSimpleUserSwitcher) {
253 if (guestRecord == null) {
Adrian Roosccdff622014-08-06 00:07:18 +0200254 if (canCreateGuest) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000255 guestRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200256 true /* isGuest */, false /* isCurrent */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800257 false /* isAddUser */, createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000258 checkIfAddUserDisallowedByAdminOnly(guestRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000259 records.add(guestRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200260 }
Jason Monk2daf62c2014-07-31 14:35:06 -0400261 } else {
Adrian Roosbed6e3b2014-08-21 20:31:41 +0200262 int index = guestRecord.isCurrent ? 0 : records.size();
263 records.add(index, guestRecord);
Jason Monk2daf62c2014-07-31 14:35:06 -0400264 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200265 }
266
Jason Monk092be7d2014-09-02 15:26:13 -0400267 if (!mSimpleUserSwitcher && canCreateUser) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000268 UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200269 false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800270 createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000271 checkIfAddUserDisallowedByAdminOnly(addUserRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000272 records.add(addUserRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200273 }
274
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200275 return records;
276 }
277
278 @Override
279 protected void onPostExecute(ArrayList<UserRecord> userRecords) {
280 if (userRecords != null) {
281 mUsers = userRecords;
282 notifyAdapters();
283 }
284 }
Adrian Roosccdff622014-08-06 00:07:18 +0200285 }.execute((SparseArray) bitmaps);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200286 }
287
Adrian Roos88b11932015-07-22 14:59:48 -0700288 private void pauseRefreshUsers() {
289 if (!mPauseRefreshUsers) {
290 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
291 mPauseRefreshUsers = true;
292 }
293 }
294
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200295 private void notifyAdapters() {
296 for (int i = mAdapters.size() - 1; i >= 0; i--) {
297 BaseUserAdapter adapter = mAdapters.get(i).get();
298 if (adapter != null) {
299 adapter.notifyDataSetChanged();
300 } else {
301 mAdapters.remove(i);
302 }
303 }
304 }
305
Jason Monk2daf62c2014-07-31 14:35:06 -0400306 public boolean isSimpleUserSwitcher() {
307 return mSimpleUserSwitcher;
308 }
309
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700310 public boolean useFullscreenUserSwitcher() {
311 // Use adb to override:
312 // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
313 // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
314 // Restart SystemUI or adb reboot.
315 final int DEFAULT = -1;
316 final int overrideUseFullscreenUserSwitcher =
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700317 whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(),
318 "enable_fullscreen_user_switcher", DEFAULT));
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700319 if (overrideUseFullscreenUserSwitcher != DEFAULT) {
320 return overrideUseFullscreenUserSwitcher != 0;
321 }
322 // Otherwise default to the build setting.
Xiyuan Xia40f9dab2015-08-17 13:19:30 -0700323 return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700324 }
325
Evan Rosky37c76802016-06-30 17:59:39 -0700326 public void setResumeUserOnGuestLogout(boolean resume) {
327 mResumeUserOnGuestLogout = resume;
328 }
329
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800330 public void logoutCurrentUser() {
331 int currentUser = ActivityManager.getCurrentUser();
332 if (currentUser != UserHandle.USER_SYSTEM) {
Fyodor Kupolovb5690bc32016-03-02 17:50:58 -0800333 pauseRefreshUsers();
334 ActivityManager.logoutCurrentUser();
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800335 }
336 }
337
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700338 public void removeUserId(int userId) {
339 if (userId == UserHandle.USER_SYSTEM) {
340 Log.w(TAG, "User " + userId + " could not removed.");
341 return;
342 }
343 if (ActivityManager.getCurrentUser() == userId) {
344 switchToUserId(UserHandle.USER_SYSTEM);
345 }
346 if (mUserManager.removeUser(userId)) {
347 refreshUsers(UserHandle.USER_NULL);
348 }
349 }
350
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200351 public void switchTo(UserRecord record) {
352 int id;
353 if (record.isGuest && record.info == null) {
354 // No guest user. Create one.
Adrian Roosf99727c2014-09-17 16:15:07 +0200355 UserInfo guest = mUserManager.createGuest(
356 mContext, mContext.getString(R.string.guest_nickname));
357 if (guest == null) {
358 // Couldn't create guest, most likely because there already exists one, we just
359 // haven't reloaded the user list yet.
360 return;
361 }
362 id = guest.id;
Adrian Roosccdff622014-08-06 00:07:18 +0200363 } else if (record.isAddUser) {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200364 showAddUserDialog();
365 return;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200366 } else {
367 id = record.info.id;
368 }
369
Evan Rosky13a58a92016-07-27 15:51:09 -0700370 int currUserId = ActivityManager.getCurrentUser();
371 if (currUserId == id) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200372 if (record.isGuest) {
Adrian Roos50052442014-07-17 23:35:18 +0200373 showExitGuestDialog(id);
Adrian Roose9c7d432014-07-17 18:27:38 +0200374 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200375 return;
376 }
377
Evan Rosky13a58a92016-07-27 15:51:09 -0700378 if (UserManager.isGuestUserEphemeral()) {
379 // If switching from guest, we want to bring up the guest exit dialog instead of switching
380 UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
381 if (currUserInfo != null && currUserInfo.isGuest()) {
382 showExitGuestDialog(currUserId, record.resolveId());
383 return;
384 }
385 }
386
Adrian Roose9c7d432014-07-17 18:27:38 +0200387 switchToUserId(id);
388 }
389
Fyodor Kupolovcaab2662016-03-01 18:39:27 -0800390 public void switchTo(int userId) {
391 final int count = mUsers.size();
392 for (int i = 0; i < count; ++i) {
393 UserRecord record = mUsers.get(i);
394 if (record.info != null && record.info.id == userId) {
395 switchTo(record);
396 return;
397 }
398 }
399
400 Log.e(TAG, "Couldn't switch to user, id=" + userId);
401 }
402
Daniel Nishi8ba446c2016-06-20 18:56:35 -0700403 public int getSwitchableUserCount() {
404 int count = 0;
405 final int N = mUsers.size();
406 for (int i = 0; i < N; ++i) {
407 UserRecord record = mUsers.get(i);
Fabian Kozynski0f46c342019-03-25 13:23:38 -0400408 if (record.info != null && record.info.supportsSwitchToByUser()) {
Daniel Nishi8ba446c2016-06-20 18:56:35 -0700409 count++;
410 }
411 }
412 return count;
413 }
414
Evan Rosky13a58a92016-07-27 15:51:09 -0700415 protected void switchToUserId(int id) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200416 try {
Adrian Roos88b11932015-07-22 14:59:48 -0700417 pauseRefreshUsers();
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800418 ActivityManager.getService().switchUser(id);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200419 } catch (RemoteException e) {
420 Log.e(TAG, "Couldn't switch user.", e);
421 }
422 }
423
Adrian Roos50052442014-07-17 23:35:18 +0200424 private void showExitGuestDialog(int id) {
Evan Rosky13a58a92016-07-27 15:51:09 -0700425 int newId = UserHandle.USER_SYSTEM;
426 if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
427 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
428 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
429 newId = info.id;
430 }
431 }
432 showExitGuestDialog(id, newId);
433 }
434
435 protected void showExitGuestDialog(int id, int targetId) {
Adrian Roos50052442014-07-17 23:35:18 +0200436 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
437 mExitGuestDialog.cancel();
438 }
Evan Rosky13a58a92016-07-27 15:51:09 -0700439 mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
Adrian Roos50052442014-07-17 23:35:18 +0200440 mExitGuestDialog.show();
441 }
442
Muyuan Li92c91412016-05-04 17:06:29 -0700443 public void showAddUserDialog() {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200444 if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
445 mAddUserDialog.cancel();
446 }
447 mAddUserDialog = new AddUserDialog(mContext);
448 mAddUserDialog.show();
449 }
450
Evan Rosky13a58a92016-07-27 15:51:09 -0700451 protected void exitGuest(int id, int targetId) {
452 switchToUserId(targetId);
Adrian Roose9c7d432014-07-17 18:27:38 +0200453 mUserManager.removeUser(id);
454 }
455
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800456 private void listenForCallState() {
Jason Monk705a4bc2016-06-30 15:04:11 -0400457 TelephonyManager.from(mContext).listen(mPhoneStateListener,
458 PhoneStateListener.LISTEN_CALL_STATE);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800459 }
460
Jason Monk705a4bc2016-06-30 15:04:11 -0400461 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
462 private int mCallState;
463
464 @Override
465 public void onCallStateChanged(int state, String incomingNumber) {
466 if (mCallState == state) return;
467 if (DEBUG) Log.v(TAG, "Call state changed: " + state);
468 mCallState = state;
Jason Monk705a4bc2016-06-30 15:04:11 -0400469 refreshUsers(UserHandle.USER_NULL);
470 }
471 };
472
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200473 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
474 @Override
475 public void onReceive(Context context, Intent intent) {
Adrian Roos50052442014-07-17 23:35:18 +0200476 if (DEBUG) {
477 Log.v(TAG, "Broadcast: a=" + intent.getAction()
478 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
479 }
Adrian Roos88b11932015-07-22 14:59:48 -0700480
481 boolean unpauseRefreshUsers = false;
482 int forcePictureLoadForId = UserHandle.USER_NULL;
483
Julia Reynolds049095a2018-03-18 14:54:27 -0400484 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
Adrian Roos50052442014-07-17 23:35:18 +0200485 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
486 mExitGuestDialog.cancel();
487 mExitGuestDialog = null;
488 }
489
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200490 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700491 final UserInfo userInfo = mUserManager.getUserInfo(currentId);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200492 final int N = mUsers.size();
493 for (int i = 0; i < N; i++) {
494 UserRecord record = mUsers.get(i);
Adrian Roose9c7d432014-07-17 18:27:38 +0200495 if (record.info == null) continue;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200496 boolean shouldBeCurrent = record.info.id == currentId;
497 if (record.isCurrent != shouldBeCurrent) {
498 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
499 }
Adrian Roos70441462014-07-22 16:03:12 +0200500 if (shouldBeCurrent && !record.isGuest) {
501 mLastNonGuestUser = record.info.id;
502 }
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700503 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {
Adrian Roosccdff622014-08-06 00:07:18 +0200504 // Immediately remove restricted records in case the AsyncTask is too slow.
505 mUsers.remove(i);
506 i--;
507 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200508 }
509 notifyAdapters();
Xiaohui Chen860397f2015-07-22 15:03:48 -0700510
Winson3c2c34b2016-04-04 17:47:41 -0700511 // Disconnect from the old secondary user's service
512 if (mSecondaryUser != UserHandle.USER_NULL) {
513 context.stopServiceAsUser(mSecondaryUserServiceIntent,
514 UserHandle.of(mSecondaryUser));
515 mSecondaryUser = UserHandle.USER_NULL;
516 }
517 // Connect to the new secondary user's service (purely to ensure that a persistent
518 // SystemUI application is created for that user)
Xiaohui Chenec015d52016-05-05 16:28:35 -0700519 if (userInfo != null && userInfo.id != UserHandle.USER_SYSTEM) {
Winson3c2c34b2016-04-04 17:47:41 -0700520 context.startServiceAsUser(mSecondaryUserServiceIntent,
521 UserHandle.of(userInfo.id));
522 mSecondaryUser = userInfo.id;
523 }
Adrian Roos88b11932015-07-22 14:59:48 -0700524 unpauseRefreshUsers = true;
525 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200526 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
527 UserHandle.USER_NULL);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800528 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
529 // Unlocking the system user may require a refresh
530 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
531 if (userId != UserHandle.USER_SYSTEM) {
532 return;
533 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200534 }
535 refreshUsers(forcePictureLoadForId);
Adrian Roos88b11932015-07-22 14:59:48 -0700536 if (unpauseRefreshUsers) {
537 mUnpauseRefreshUsers.run();
538 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200539 }
540 };
541
Adrian Roos88b11932015-07-22 14:59:48 -0700542 private final Runnable mUnpauseRefreshUsers = new Runnable() {
543 @Override
544 public void run() {
545 mHandler.removeCallbacks(this);
546 mPauseRefreshUsers = false;
547 refreshUsers(UserHandle.USER_NULL);
548 }
549 };
550
Adrian Roosccdff622014-08-06 00:07:18 +0200551 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
Jason Monk2daf62c2014-07-31 14:35:06 -0400552 public void onChange(boolean selfChange) {
553 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
554 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
Adrian Roosccdff622014-08-06 00:07:18 +0200555 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
556 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
Jason Monk2daf62c2014-07-31 14:35:06 -0400557 refreshUsers(UserHandle.USER_NULL);
558 };
559 };
560
Evan Laird97eaacb2018-09-04 15:06:37 -0400561 @Override
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200562 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
563 pw.println("UserSwitcherController state:");
Adrian Roos70441462014-07-22 16:03:12 +0200564 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200565 pw.print(" mUsers.size="); pw.println(mUsers.size());
566 for (int i = 0; i < mUsers.size(); i++) {
567 final UserRecord u = mUsers.get(i);
568 pw.print(" "); pw.println(u.toString());
569 }
570 }
571
Adrian Roos57cf5702014-09-03 15:56:30 +0200572 public String getCurrentUserName(Context context) {
573 if (mUsers.isEmpty()) return null;
574 UserRecord item = mUsers.get(0);
575 if (item == null || item.info == null) return null;
576 if (item.isGuest) return context.getString(R.string.guest_nickname);
577 return item.info.name;
578 }
579
Adrian Roosd390b892016-05-05 10:50:49 -0400580 public void onDensityOrFontScaleChanged() {
581 refreshUsers(UserHandle.USER_ALL);
582 }
583
Jason Monk88529052016-11-04 13:29:58 -0400584 @VisibleForTesting
585 public void addAdapter(WeakReference<BaseUserAdapter> adapter) {
586 mAdapters.add(adapter);
587 }
588
589 @VisibleForTesting
Jason Monk88529052016-11-04 13:29:58 -0400590 public ArrayList<UserRecord> getUsers() {
591 return mUsers;
592 }
593
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200594 public static abstract class BaseUserAdapter extends BaseAdapter {
595
596 final UserSwitcherController mController;
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400597 private final KeyguardStateController mKeyguardStateController;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200598
599 protected BaseUserAdapter(UserSwitcherController controller) {
600 mController = controller;
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400601 mKeyguardStateController = controller.mKeyguardStateController;
Jason Monk88529052016-11-04 13:29:58 -0400602 controller.addAdapter(new WeakReference<>(this));
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200603 }
604
Jason Monke5b770e2017-03-03 21:49:29 -0500605 public int getUserCount() {
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400606 boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
607 && mKeyguardStateController.isMethodSecure()
608 && !mKeyguardStateController.canDismissLockScreen();
Jason Monke5b770e2017-03-03 21:49:29 -0500609 if (!secureKeyguardShowing) {
610 return mController.getUsers().size();
611 }
612 // The lock screen is secure and showing. Filter out restricted records.
613 final int N = mController.getUsers().size();
614 int count = 0;
615 for (int i = 0; i < N; i++) {
616 if (mController.getUsers().get(i).isGuest) continue;
617 if (mController.getUsers().get(i).isRestricted) {
618 break;
619 } else {
620 count++;
621 }
622 }
623 return count;
624 }
625
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200626 @Override
627 public int getCount() {
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400628 boolean secureKeyguardShowing = mKeyguardStateController.isShowing()
629 && mKeyguardStateController.isMethodSecure()
630 && !mKeyguardStateController.canDismissLockScreen();
Adrian Roosccdff622014-08-06 00:07:18 +0200631 if (!secureKeyguardShowing) {
Jason Monk88529052016-11-04 13:29:58 -0400632 return mController.getUsers().size();
Adrian Roosccdff622014-08-06 00:07:18 +0200633 }
634 // The lock screen is secure and showing. Filter out restricted records.
Jason Monk88529052016-11-04 13:29:58 -0400635 final int N = mController.getUsers().size();
Adrian Roosccdff622014-08-06 00:07:18 +0200636 int count = 0;
637 for (int i = 0; i < N; i++) {
Jason Monk88529052016-11-04 13:29:58 -0400638 if (mController.getUsers().get(i).isRestricted) {
Adrian Roosccdff622014-08-06 00:07:18 +0200639 break;
640 } else {
641 count++;
642 }
643 }
644 return count;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200645 }
646
647 @Override
648 public UserRecord getItem(int position) {
Jason Monk88529052016-11-04 13:29:58 -0400649 return mController.getUsers().get(position);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200650 }
651
652 @Override
653 public long getItemId(int position) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200654 return position;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200655 }
656
657 public void switchTo(UserRecord record) {
658 mController.switchTo(record);
659 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200660
661 public String getName(Context context, UserRecord item) {
662 if (item.isGuest) {
663 if (item.isCurrent) {
664 return context.getString(R.string.guest_exit_guest);
665 } else {
666 return context.getString(
667 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
668 }
Adrian Roosccdff622014-08-06 00:07:18 +0200669 } else if (item.isAddUser) {
670 return context.getString(R.string.user_add_user);
Adrian Roose9c7d432014-07-17 18:27:38 +0200671 } else {
672 return item.info.name;
673 }
674 }
Adrian Roos723632e2014-07-23 21:13:21 +0200675
Adrian Roosccdff622014-08-06 00:07:18 +0200676 public Drawable getDrawable(Context context, UserRecord item) {
677 if (item.isAddUser) {
678 return context.getDrawable(R.drawable.ic_add_circle_qs);
679 }
Tony Mak213955e2017-11-23 16:57:08 +0800680 Drawable icon = UserIcons.getDefaultUserIcon(
681 context.getResources(), item.resolveId(), /* light= */ false);
Jason Monk5a655022017-05-24 13:55:23 -0400682 if (item.isGuest) {
Jason Changb4e879d2018-04-11 11:17:58 +0800683 icon.setColorFilter(Utils.getColorAttrDefaultColor(context,
684 android.R.attr.colorForeground),
Jason Monk5a655022017-05-24 13:55:23 -0400685 Mode.SRC_IN);
686 }
687 return icon;
Adrian Roosccdff622014-08-06 00:07:18 +0200688 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100689
690 public void refresh() {
691 mController.refreshUsers(UserHandle.USER_NULL);
692 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200693 }
694
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000695 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700696 EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000697 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700698 if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000699 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000700 record.isDisabledByAdmin = true;
Sudheer Shankaad143c02016-03-31 00:24:05 +0000701 record.enforcedAdmin = admin;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000702 } else {
703 record.isDisabledByAdmin = false;
704 record.enforcedAdmin = null;
705 }
706 }
707
708 public void startActivity(Intent intent) {
709 mActivityStarter.startActivity(intent, true);
710 }
711
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200712 public static final class UserRecord {
713 public final UserInfo info;
714 public final Bitmap picture;
715 public final boolean isGuest;
716 public final boolean isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200717 public final boolean isAddUser;
718 /** If true, the record is only visible to the owner and only when unlocked. */
719 public final boolean isRestricted;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000720 public boolean isDisabledByAdmin;
721 public EnforcedAdmin enforcedAdmin;
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800722 public boolean isSwitchToEnabled;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200723
Adrian Roosccdff622014-08-06 00:07:18 +0200724 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800725 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200726 this.info = info;
727 this.picture = picture;
728 this.isGuest = isGuest;
729 this.isCurrent = isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200730 this.isAddUser = isAddUser;
731 this.isRestricted = isRestricted;
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800732 this.isSwitchToEnabled = isSwitchToEnabled;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200733 }
734
735 public UserRecord copyWithIsCurrent(boolean _isCurrent) {
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800736 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
737 isSwitchToEnabled);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200738 }
739
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700740 public int resolveId() {
741 if (isGuest || info == null) {
742 return UserHandle.USER_NULL;
743 }
744 return info.id;
745 }
746
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200747 public String toString() {
748 StringBuilder sb = new StringBuilder();
749 sb.append("UserRecord(");
750 if (info != null) {
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800751 sb.append("name=\"").append(info.name).append("\" id=").append(info.id);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200752 } else {
Adrian Roosccdff622014-08-06 00:07:18 +0200753 if (isGuest) {
754 sb.append("<add guest placeholder>");
755 } else if (isAddUser) {
756 sb.append("<add user placeholder>");
757 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200758 }
Adrian Roosccdff622014-08-06 00:07:18 +0200759 if (isGuest) sb.append(" <isGuest>");
760 if (isAddUser) sb.append(" <isAddUser>");
761 if (isCurrent) sb.append(" <isCurrent>");
762 if (picture != null) sb.append(" <hasPicture>");
763 if (isRestricted) sb.append(" <isRestricted>");
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000764 if (isDisabledByAdmin) {
765 sb.append(" <isDisabledByAdmin>");
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800766 sb.append(" enforcedAdmin=").append(enforcedAdmin);
767 }
768 if (isSwitchToEnabled) {
769 sb.append(" <isSwitchToEnabled>");
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000770 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200771 sb.append(')');
772 return sb.toString();
773 }
774 }
775
Jason Monk46767b72016-08-18 10:58:04 -0400776 public final DetailAdapter userDetailAdapter = new DetailAdapter() {
Jason Monk76c67aa2016-02-19 14:49:42 -0500777 private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200778
779 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500780 public CharSequence getTitle() {
781 return mContext.getString(R.string.quick_settings_user_title);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200782 }
783
784 @Override
785 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Adrian Roos19408922014-08-07 20:54:12 +0200786 UserDetailView v;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200787 if (!(convertView instanceof UserDetailView)) {
Adrian Roos19408922014-08-07 20:54:12 +0200788 v = UserDetailView.inflate(context, parent, false);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200789 v.createAndSetAdapter(UserSwitcherController.this);
Adrian Roos19408922014-08-07 20:54:12 +0200790 } else {
791 v = (UserDetailView) convertView;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200792 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100793 v.refreshAdapter();
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200794 return v;
795 }
796
797 @Override
798 public Intent getSettingsIntent() {
799 return USER_SETTINGS_INTENT;
800 }
801
802 @Override
803 public Boolean getToggleState() {
804 return null;
805 }
806
807 @Override
808 public void setToggleState(boolean state) {
809 }
Chris Wren457a21c2015-05-06 17:50:34 -0400810
811 @Override
812 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500813 return MetricsEvent.QS_USERDETAIL;
Chris Wren457a21c2015-05-06 17:50:34 -0400814 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200815 };
Adrian Roos50052442014-07-17 23:35:18 +0200816
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400817 private final KeyguardStateController.Callback mCallback =
818 new KeyguardStateController.Callback() {
819 @Override
820 public void onKeyguardShowingChanged() {
Jorim Jaggie549a8d2017-05-15 02:40:05 +0200821
Lucas Dupinc8f16e82019-09-17 18:24:50 -0400822 // When Keyguard is going away, we don't need to update our items immediately
823 // which
824 // helps making the transition faster.
825 if (!mKeyguardStateController.isShowing()) {
826 mHandler.post(UserSwitcherController.this::notifyAdapters);
827 } else {
828 notifyAdapters();
829 }
830 }
831 };
Adrian Roosccdff622014-08-06 00:07:18 +0200832
Adrian Roos50052442014-07-17 23:35:18 +0200833 private final class ExitGuestDialog extends SystemUIDialog implements
834 DialogInterface.OnClickListener {
835
836 private final int mGuestId;
Evan Rosky13a58a92016-07-27 15:51:09 -0700837 private final int mTargetId;
Adrian Roos50052442014-07-17 23:35:18 +0200838
Evan Rosky13a58a92016-07-27 15:51:09 -0700839 public ExitGuestDialog(Context context, int guestId, int targetId) {
Adrian Roos50052442014-07-17 23:35:18 +0200840 super(context);
841 setTitle(R.string.guest_exit_guest_dialog_title);
842 setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
843 setButton(DialogInterface.BUTTON_NEGATIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700844 context.getString(android.R.string.cancel), this);
Adrian Roos50052442014-07-17 23:35:18 +0200845 setButton(DialogInterface.BUTTON_POSITIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700846 context.getString(R.string.guest_exit_guest_dialog_remove), this);
Evan Lairdd4a44ff2017-11-21 16:38:22 -0500847 SystemUIDialog.setWindowOnTop(this);
Adrian Roos50052442014-07-17 23:35:18 +0200848 setCanceledOnTouchOutside(false);
849 mGuestId = guestId;
Evan Rosky13a58a92016-07-27 15:51:09 -0700850 mTargetId = targetId;
Adrian Roos50052442014-07-17 23:35:18 +0200851 }
852
853 @Override
854 public void onClick(DialogInterface dialog, int which) {
855 if (which == BUTTON_NEGATIVE) {
856 cancel();
857 } else {
858 dismiss();
Evan Rosky13a58a92016-07-27 15:51:09 -0700859 exitGuest(mGuestId, mTargetId);
Adrian Roos50052442014-07-17 23:35:18 +0200860 }
861 }
862 }
Adrian Roos0c6763a2014-09-08 19:11:00 +0200863
864 private final class AddUserDialog extends SystemUIDialog implements
865 DialogInterface.OnClickListener {
866
867 public AddUserDialog(Context context) {
868 super(context);
869 setTitle(R.string.user_add_user_title);
870 setMessage(context.getString(R.string.user_add_user_message_short));
871 setButton(DialogInterface.BUTTON_NEGATIVE,
872 context.getString(android.R.string.cancel), this);
873 setButton(DialogInterface.BUTTON_POSITIVE,
874 context.getString(android.R.string.ok), this);
Evan Lairdd4a44ff2017-11-21 16:38:22 -0500875 SystemUIDialog.setWindowOnTop(this);
Adrian Roos0c6763a2014-09-08 19:11:00 +0200876 }
877
878 @Override
879 public void onClick(DialogInterface dialog, int which) {
880 if (which == BUTTON_NEGATIVE) {
881 cancel();
882 } else {
883 dismiss();
Guang Zhuccbeb612014-10-21 14:33:50 -0700884 if (ActivityManager.isUserAMonkey()) {
885 return;
886 }
Xiaohui Chencfe64c82015-07-16 14:30:50 -0700887 UserInfo user = mUserManager.createUser(
Adrian Roosf99727c2014-09-17 16:15:07 +0200888 mContext.getString(R.string.user_new_user_name), 0 /* flags */);
889 if (user == null) {
890 // Couldn't create user, most likely because there are too many, but we haven't
891 // been able to reload the list yet.
892 return;
893 }
894 int id = user.id;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +0100895 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
Tony Mak213955e2017-11-23 16:57:08 +0800896 mContext.getResources(), id, /* light= */ false));
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +0100897 mUserManager.setUserIcon(id, icon);
Adrian Roos0c6763a2014-09-08 19:11:00 +0200898 switchToUserId(id);
899 }
900 }
901 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200902}