blob: 2e1e52049419c42c1ee896e5b57edffc61b42d7e [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;
Jason Monk196d6392018-12-20 13:25:34 -050021import static com.android.systemui.Dependency.MAIN_HANDLER_NAME;
Xiaohui Chenec015d52016-05-05 16:28:35 -070022
Adrian Roos00a0b1f2014-07-16 16:44:49 +020023import android.app.ActivityManager;
Adrian Roos50052442014-07-17 23:35:18 +020024import android.app.Dialog;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020025import android.content.BroadcastReceiver;
26import android.content.Context;
Adrian Roos50052442014-07-17 23:35:18 +020027import android.content.DialogInterface;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020028import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.UserInfo;
Jason Monk2daf62c2014-07-31 14:35:06 -040031import android.database.ContentObserver;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020032import android.graphics.Bitmap;
Jason Monk5a655022017-05-24 13:55:23 -040033import android.graphics.PorterDuff.Mode;
Adrian Roosccdff622014-08-06 00:07:18 +020034import android.graphics.drawable.Drawable;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020035import android.os.AsyncTask;
Jason Monk2daf62c2014-07-31 14:35:06 -040036import android.os.Handler;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020037import android.os.RemoteException;
Adrian Roose9c7d432014-07-17 18:27:38 +020038import android.os.UserHandle;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020039import android.os.UserManager;
Jason Monk2daf62c2014-07-31 14:35:06 -040040import android.provider.Settings;
Fyodor Kupolov523c4042016-02-24 15:03:13 -080041import android.telephony.PhoneStateListener;
42import android.telephony.TelephonyManager;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020043import android.util.Log;
Adrian Roose9c7d432014-07-17 18:27:38 +020044import android.util.SparseArray;
Adrian Roos88b11932015-07-22 14:59:48 -070045import android.util.SparseBooleanArray;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020046import android.view.View;
47import android.view.ViewGroup;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020048import android.widget.BaseAdapter;
49
Jason Monk88529052016-11-04 13:29:58 -040050import com.android.internal.annotations.VisibleForTesting;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070051import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010052import com.android.internal.util.UserIcons;
Philip P. Moltmann4e615e62018-08-28 14:57:49 -070053import com.android.settingslib.RestrictedLockUtilsInternal;
Jason Monk5a655022017-05-24 13:55:23 -040054import com.android.settingslib.Utils;
Evan Laird97eaacb2018-09-04 15:06:37 -040055import com.android.systemui.Dumpable;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010056import com.android.systemui.GuestResumeSessionReceiver;
Jason Monk24f8a752017-06-30 11:31:03 -040057import com.android.systemui.Prefs;
58import com.android.systemui.Prefs.Key;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010059import com.android.systemui.R;
Winson3c2c34b2016-04-04 17:47:41 -070060import com.android.systemui.SystemUISecondaryUserService;
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;
73import javax.inject.Named;
74import javax.inject.Singleton;
75
Adrian Roos00a0b1f2014-07-16 16:44:49 +020076/**
77 * Keeps a list of all users on the device for user switching.
78 */
Jason Monk196d6392018-12-20 13:25:34 -050079@Singleton
Evan Laird97eaacb2018-09-04 15:06:37 -040080public class UserSwitcherController implements Dumpable {
Adrian Roos00a0b1f2014-07-16 16:44:49 +020081
82 private static final String TAG = "UserSwitcherController";
Adrian Roos50052442014-07-17 23:35:18 +020083 private static final boolean DEBUG = false;
Jason Monk2daf62c2014-07-31 14:35:06 -040084 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
85 "lockscreenSimpleUserSwitcher";
Adrian Roos88b11932015-07-22 14:59:48 -070086 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020087
Amith Yamasanid81f8272015-07-23 17:15:45 -070088 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
89
Evan Rosky13a58a92016-07-27 15:51:09 -070090 protected final Context mContext;
91 protected final UserManager mUserManager;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020092 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020093 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
94 = new GuestResumeSessionReceiver();
Adrian Roosccdff622014-08-06 00:07:18 +020095 private final KeyguardMonitor mKeyguardMonitor;
Evan Rosky13a58a92016-07-27 15:51:09 -070096 protected final Handler mHandler;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000097 private final ActivityStarter mActivityStarter;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020098
99 private ArrayList<UserRecord> mUsers = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +0200100 private Dialog mExitGuestDialog;
Adrian Roos0c6763a2014-09-08 19:11:00 +0200101 private Dialog mAddUserDialog;
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700102 private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
Evan Rosky37c76802016-06-30 17:59:39 -0700103 private boolean mResumeUserOnGuestLogout = true;
Adrian Roosccdff622014-08-06 00:07:18 +0200104 private boolean mSimpleUserSwitcher;
105 private boolean mAddUsersWhenLocked;
Adrian Roos88b11932015-07-22 14:59:48 -0700106 private boolean mPauseRefreshUsers;
Winson3c2c34b2016-04-04 17:47:41 -0700107 private int mSecondaryUser = UserHandle.USER_NULL;
108 private Intent mSecondaryUserServiceIntent;
Adrian Roos88b11932015-07-22 14:59:48 -0700109 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200110
Jason Monk196d6392018-12-20 13:25:34 -0500111 @Inject
Adrian Roos88b11932015-07-22 14:59:48 -0700112 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
Jason Monk196d6392018-12-20 13:25:34 -0500113 @Named(MAIN_HANDLER_NAME) Handler handler, ActivityStarter activityStarter) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200114 mContext = context;
Christophe Koessler32d5d502019-05-23 11:18:30 -0700115 if (!UserManager.isGuestUserEphemeral()) {
116 mGuestResumeSessionReceiver.register(context);
117 }
Adrian Roosccdff622014-08-06 00:07:18 +0200118 mKeyguardMonitor = keyguardMonitor;
Adrian Roos88b11932015-07-22 14:59:48 -0700119 mHandler = handler;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000120 mActivityStarter = activityStarter;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200121 mUserManager = UserManager.get(context);
122 IntentFilter filter = new IntentFilter();
123 filter.addAction(Intent.ACTION_USER_ADDED);
124 filter.addAction(Intent.ACTION_USER_REMOVED);
125 filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
126 filter.addAction(Intent.ACTION_USER_SWITCHED);
Amith Yamasaniad2e4bf2016-04-26 14:35:54 -0700127 filter.addAction(Intent.ACTION_USER_STOPPED);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800128 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700129 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Adrian Roose9c7d432014-07-17 18:27:38 +0200130 null /* permission */, null /* scheduler */);
Jason Monk2daf62c2014-07-31 14:35:06 -0400131
Winson3c2c34b2016-04-04 17:47:41 -0700132 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
133
Amith Yamasanid81f8272015-07-23 17:15:45 -0700134 filter = new IntentFilter();
Amith Yamasani5891a342015-07-24 13:50:58 -0700135 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Amith Yamasanid81f8272015-07-23 17:15:45 -0700136 PERMISSION_SELF, null /* scheduler */);
Adrian Roosccdff622014-08-06 00:07:18 +0200137
Jason Monk2daf62c2014-07-31 14:35:06 -0400138 mContext.getContentResolver().registerContentObserver(
139 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
Adrian Roosccdff622014-08-06 00:07:18 +0200140 mSettingsObserver);
141 mContext.getContentResolver().registerContentObserver(
142 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
143 mSettingsObserver);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800144 mContext.getContentResolver().registerContentObserver(
145 Settings.Global.getUriFor(
146 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED),
147 true, mSettingsObserver);
Adrian Roosccdff622014-08-06 00:07:18 +0200148 // Fetch initial values.
149 mSettingsObserver.onChange(false);
150
151 keyguardMonitor.addCallback(mCallback);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800152 listenForCallState();
Jason Monk2daf62c2014-07-31 14:35:06 -0400153
Adrian Roose9c7d432014-07-17 18:27:38 +0200154 refreshUsers(UserHandle.USER_NULL);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200155 }
156
Adrian Roose9c7d432014-07-17 18:27:38 +0200157 /**
158 * Refreshes users from UserManager.
159 *
160 * The pictures are only loaded if they have not been loaded yet.
161 *
162 * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
163 */
Amith Yamasani95ab7842014-08-11 17:09:26 -0700164 @SuppressWarnings("unchecked")
Adrian Roose9c7d432014-07-17 18:27:38 +0200165 private void refreshUsers(int forcePictureLoadForId) {
Adrian Roos88b11932015-07-22 14:59:48 -0700166 if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
167 if (forcePictureLoadForId != UserHandle.USER_NULL) {
168 mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
169 }
170
171 if (mPauseRefreshUsers) {
172 return;
173 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000174
Adrian Roosd390b892016-05-05 10:50:49 -0400175 boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
Adrian Roosc5db3902014-11-21 13:54:04 +0000176 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
177 final int N = mUsers.size();
178 for (int i = 0; i < N; i++) {
179 UserRecord r = mUsers.get(i);
Adrian Roosd390b892016-05-05 10:50:49 -0400180 if (r == null || r.picture == null || r.info == null || forceAllUsers
181 || mForcePictureLoadForUserId.get(r.info.id)) {
Adrian Roosc5db3902014-11-21 13:54:04 +0000182 continue;
Adrian Roose9c7d432014-07-17 18:27:38 +0200183 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000184 bitmaps.put(r.info.id, r.picture);
Adrian Roose9c7d432014-07-17 18:27:38 +0200185 }
Adrian Roos88b11932015-07-22 14:59:48 -0700186 mForcePictureLoadForUserId.clear();
Adrian Roose9c7d432014-07-17 18:27:38 +0200187
Adrian Roosccdff622014-08-06 00:07:18 +0200188 final boolean addUsersWhenLocked = mAddUsersWhenLocked;
Adrian Roose9c7d432014-07-17 18:27:38 +0200189 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
190 @SuppressWarnings("unchecked")
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200191 @Override
Adrian Roose9c7d432014-07-17 18:27:38 +0200192 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
193 final SparseArray<Bitmap> bitmaps = params[0];
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200194 List<UserInfo> infos = mUserManager.getUsers(true);
195 if (infos == null) {
196 return null;
197 }
198 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
199 int currentId = ActivityManager.getCurrentUser();
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800200 boolean canSwitchUsers = mUserManager.canSwitchUsers();
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700201 UserInfo currentUserInfo = null;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200202 UserRecord guestRecord = null;
203
204 for (UserInfo info : infos) {
205 boolean isCurrent = currentId == info.id;
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700206 if (isCurrent) {
207 currentUserInfo = info;
208 }
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800209 boolean switchToEnabled = canSwitchUsers || isCurrent;
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100210 if (info.isEnabled()) {
211 if (info.isGuest()) {
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800212 // Tapping guest icon triggers remove and a user switch therefore
213 // the icon shouldn't be enabled even if the user is current
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100214 guestRecord = new UserRecord(info, null /* picture */,
215 true /* isGuest */, isCurrent, false /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800216 false /* isRestricted */, canSwitchUsers);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100217 } else if (info.supportsSwitchToByUser()) {
218 Bitmap picture = bitmaps.get(info.id);
219 if (picture == null) {
220 picture = mUserManager.getUserIcon(info.id);
Adrian Rooscba0faa2014-11-17 17:41:28 +0100221
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100222 if (picture != null) {
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700223 int avatarSize = mContext.getResources()
224 .getDimensionPixelSize(R.dimen.max_avatar_size);
225 picture = Bitmap.createScaledBitmap(
226 picture, avatarSize, avatarSize, true);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100227 }
Adrian Rooscba0faa2014-11-17 17:41:28 +0100228 }
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100229 int index = isCurrent ? 0 : records.size();
230 records.add(index, new UserRecord(info, picture, false /* isGuest */,
231 isCurrent, false /* isAddUser */, false /* isRestricted */,
232 switchToEnabled));
Dan Sandler4d75c072014-07-17 16:01:28 -0400233 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200234 }
235 }
Jason Monk24f8a752017-06-30 11:31:03 -0400236 if (records.size() > 1 || guestRecord != null) {
237 Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true);
238 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200239
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000240 boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
241 UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700242 boolean currentUserCanCreateUsers = currentUserInfo != null
243 && (currentUserInfo.isAdmin()
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000244 || currentUserInfo.id == UserHandle.USER_SYSTEM)
245 && systemCanCreateUsers;
246 boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
247 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Adrian Roosccdff622014-08-06 00:07:18 +0200248 && guestRecord == null;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000249 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Amith Yamasani95ab7842014-08-11 17:09:26 -0700250 && mUserManager.canAddMoreUsers();
Adrian Roosccdff622014-08-06 00:07:18 +0200251 boolean createIsRestricted = !addUsersWhenLocked;
252
Jason Monk2daf62c2014-07-31 14:35:06 -0400253 if (!mSimpleUserSwitcher) {
254 if (guestRecord == null) {
Adrian Roosccdff622014-08-06 00:07:18 +0200255 if (canCreateGuest) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000256 guestRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200257 true /* isGuest */, false /* isCurrent */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800258 false /* isAddUser */, createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000259 checkIfAddUserDisallowedByAdminOnly(guestRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000260 records.add(guestRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200261 }
Jason Monk2daf62c2014-07-31 14:35:06 -0400262 } else {
Adrian Roosbed6e3b2014-08-21 20:31:41 +0200263 int index = guestRecord.isCurrent ? 0 : records.size();
264 records.add(index, guestRecord);
Jason Monk2daf62c2014-07-31 14:35:06 -0400265 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200266 }
267
Jason Monk092be7d2014-09-02 15:26:13 -0400268 if (!mSimpleUserSwitcher && canCreateUser) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000269 UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200270 false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800271 createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000272 checkIfAddUserDisallowedByAdminOnly(addUserRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000273 records.add(addUserRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200274 }
275
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200276 return records;
277 }
278
279 @Override
280 protected void onPostExecute(ArrayList<UserRecord> userRecords) {
281 if (userRecords != null) {
282 mUsers = userRecords;
283 notifyAdapters();
284 }
285 }
Adrian Roosccdff622014-08-06 00:07:18 +0200286 }.execute((SparseArray) bitmaps);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200287 }
288
Adrian Roos88b11932015-07-22 14:59:48 -0700289 private void pauseRefreshUsers() {
290 if (!mPauseRefreshUsers) {
291 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
292 mPauseRefreshUsers = true;
293 }
294 }
295
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200296 private void notifyAdapters() {
297 for (int i = mAdapters.size() - 1; i >= 0; i--) {
298 BaseUserAdapter adapter = mAdapters.get(i).get();
299 if (adapter != null) {
300 adapter.notifyDataSetChanged();
301 } else {
302 mAdapters.remove(i);
303 }
304 }
305 }
306
Jason Monk2daf62c2014-07-31 14:35:06 -0400307 public boolean isSimpleUserSwitcher() {
308 return mSimpleUserSwitcher;
309 }
310
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700311 public boolean useFullscreenUserSwitcher() {
312 // Use adb to override:
313 // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
314 // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
315 // Restart SystemUI or adb reboot.
316 final int DEFAULT = -1;
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700317 // TODO(b/140061064)
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700318 final int overrideUseFullscreenUserSwitcher =
Lucas Dupin8968f6a2019-08-09 17:41:15 -0700319 whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(),
320 "enable_fullscreen_user_switcher", DEFAULT));
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700321 if (overrideUseFullscreenUserSwitcher != DEFAULT) {
322 return overrideUseFullscreenUserSwitcher != 0;
323 }
324 // Otherwise default to the build setting.
Xiyuan Xia40f9dab2015-08-17 13:19:30 -0700325 return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700326 }
327
Evan Rosky37c76802016-06-30 17:59:39 -0700328 public void setResumeUserOnGuestLogout(boolean resume) {
329 mResumeUserOnGuestLogout = resume;
330 }
331
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800332 public void logoutCurrentUser() {
333 int currentUser = ActivityManager.getCurrentUser();
334 if (currentUser != UserHandle.USER_SYSTEM) {
Fyodor Kupolovb5690bc32016-03-02 17:50:58 -0800335 pauseRefreshUsers();
336 ActivityManager.logoutCurrentUser();
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800337 }
338 }
339
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700340 public void removeUserId(int userId) {
341 if (userId == UserHandle.USER_SYSTEM) {
342 Log.w(TAG, "User " + userId + " could not removed.");
343 return;
344 }
345 if (ActivityManager.getCurrentUser() == userId) {
346 switchToUserId(UserHandle.USER_SYSTEM);
347 }
348 if (mUserManager.removeUser(userId)) {
349 refreshUsers(UserHandle.USER_NULL);
350 }
351 }
352
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200353 public void switchTo(UserRecord record) {
354 int id;
355 if (record.isGuest && record.info == null) {
356 // No guest user. Create one.
Adrian Roosf99727c2014-09-17 16:15:07 +0200357 UserInfo guest = mUserManager.createGuest(
358 mContext, mContext.getString(R.string.guest_nickname));
359 if (guest == null) {
360 // Couldn't create guest, most likely because there already exists one, we just
361 // haven't reloaded the user list yet.
362 return;
363 }
364 id = guest.id;
Adrian Roosccdff622014-08-06 00:07:18 +0200365 } else if (record.isAddUser) {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200366 showAddUserDialog();
367 return;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200368 } else {
369 id = record.info.id;
370 }
371
Evan Rosky13a58a92016-07-27 15:51:09 -0700372 int currUserId = ActivityManager.getCurrentUser();
373 if (currUserId == id) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200374 if (record.isGuest) {
Adrian Roos50052442014-07-17 23:35:18 +0200375 showExitGuestDialog(id);
Adrian Roose9c7d432014-07-17 18:27:38 +0200376 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200377 return;
378 }
379
Evan Rosky13a58a92016-07-27 15:51:09 -0700380 if (UserManager.isGuestUserEphemeral()) {
381 // If switching from guest, we want to bring up the guest exit dialog instead of switching
382 UserInfo currUserInfo = mUserManager.getUserInfo(currUserId);
383 if (currUserInfo != null && currUserInfo.isGuest()) {
384 showExitGuestDialog(currUserId, record.resolveId());
385 return;
386 }
387 }
388
Adrian Roose9c7d432014-07-17 18:27:38 +0200389 switchToUserId(id);
390 }
391
Fyodor Kupolovcaab2662016-03-01 18:39:27 -0800392 public void switchTo(int userId) {
393 final int count = mUsers.size();
394 for (int i = 0; i < count; ++i) {
395 UserRecord record = mUsers.get(i);
396 if (record.info != null && record.info.id == userId) {
397 switchTo(record);
398 return;
399 }
400 }
401
402 Log.e(TAG, "Couldn't switch to user, id=" + userId);
403 }
404
Daniel Nishi8ba446c2016-06-20 18:56:35 -0700405 public int getSwitchableUserCount() {
406 int count = 0;
407 final int N = mUsers.size();
408 for (int i = 0; i < N; ++i) {
409 UserRecord record = mUsers.get(i);
Fabian Kozynski0f46c342019-03-25 13:23:38 -0400410 if (record.info != null && record.info.supportsSwitchToByUser()) {
Daniel Nishi8ba446c2016-06-20 18:56:35 -0700411 count++;
412 }
413 }
414 return count;
415 }
416
Evan Rosky13a58a92016-07-27 15:51:09 -0700417 protected void switchToUserId(int id) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200418 try {
Adrian Roos88b11932015-07-22 14:59:48 -0700419 pauseRefreshUsers();
Sudheer Shankadc589ac2016-11-10 15:30:17 -0800420 ActivityManager.getService().switchUser(id);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200421 } catch (RemoteException e) {
422 Log.e(TAG, "Couldn't switch user.", e);
423 }
424 }
425
Adrian Roos50052442014-07-17 23:35:18 +0200426 private void showExitGuestDialog(int id) {
Evan Rosky13a58a92016-07-27 15:51:09 -0700427 int newId = UserHandle.USER_SYSTEM;
428 if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
429 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
430 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
431 newId = info.id;
432 }
433 }
434 showExitGuestDialog(id, newId);
435 }
436
437 protected void showExitGuestDialog(int id, int targetId) {
Adrian Roos50052442014-07-17 23:35:18 +0200438 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
439 mExitGuestDialog.cancel();
440 }
Evan Rosky13a58a92016-07-27 15:51:09 -0700441 mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId);
Adrian Roos50052442014-07-17 23:35:18 +0200442 mExitGuestDialog.show();
443 }
444
Muyuan Li92c91412016-05-04 17:06:29 -0700445 public void showAddUserDialog() {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200446 if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
447 mAddUserDialog.cancel();
448 }
449 mAddUserDialog = new AddUserDialog(mContext);
450 mAddUserDialog.show();
451 }
452
Evan Rosky13a58a92016-07-27 15:51:09 -0700453 protected void exitGuest(int id, int targetId) {
454 switchToUserId(targetId);
Adrian Roose9c7d432014-07-17 18:27:38 +0200455 mUserManager.removeUser(id);
456 }
457
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800458 private void listenForCallState() {
Jason Monk705a4bc2016-06-30 15:04:11 -0400459 TelephonyManager.from(mContext).listen(mPhoneStateListener,
460 PhoneStateListener.LISTEN_CALL_STATE);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800461 }
462
Jason Monk705a4bc2016-06-30 15:04:11 -0400463 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
464 private int mCallState;
465
466 @Override
467 public void onCallStateChanged(int state, String incomingNumber) {
468 if (mCallState == state) return;
469 if (DEBUG) Log.v(TAG, "Call state changed: " + state);
470 mCallState = state;
Jason Monk705a4bc2016-06-30 15:04:11 -0400471 refreshUsers(UserHandle.USER_NULL);
472 }
473 };
474
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200475 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
476 @Override
477 public void onReceive(Context context, Intent intent) {
Adrian Roos50052442014-07-17 23:35:18 +0200478 if (DEBUG) {
479 Log.v(TAG, "Broadcast: a=" + intent.getAction()
480 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
481 }
Adrian Roos88b11932015-07-22 14:59:48 -0700482
483 boolean unpauseRefreshUsers = false;
484 int forcePictureLoadForId = UserHandle.USER_NULL;
485
Julia Reynolds049095a2018-03-18 14:54:27 -0400486 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
Adrian Roos50052442014-07-17 23:35:18 +0200487 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
488 mExitGuestDialog.cancel();
489 mExitGuestDialog = null;
490 }
491
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200492 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700493 final UserInfo userInfo = mUserManager.getUserInfo(currentId);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200494 final int N = mUsers.size();
495 for (int i = 0; i < N; i++) {
496 UserRecord record = mUsers.get(i);
Adrian Roose9c7d432014-07-17 18:27:38 +0200497 if (record.info == null) continue;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200498 boolean shouldBeCurrent = record.info.id == currentId;
499 if (record.isCurrent != shouldBeCurrent) {
500 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
501 }
Adrian Roos70441462014-07-22 16:03:12 +0200502 if (shouldBeCurrent && !record.isGuest) {
503 mLastNonGuestUser = record.info.id;
504 }
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700505 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {
Adrian Roosccdff622014-08-06 00:07:18 +0200506 // Immediately remove restricted records in case the AsyncTask is too slow.
507 mUsers.remove(i);
508 i--;
509 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200510 }
511 notifyAdapters();
Xiaohui Chen860397f2015-07-22 15:03:48 -0700512
Winson3c2c34b2016-04-04 17:47:41 -0700513 // Disconnect from the old secondary user's service
514 if (mSecondaryUser != UserHandle.USER_NULL) {
515 context.stopServiceAsUser(mSecondaryUserServiceIntent,
516 UserHandle.of(mSecondaryUser));
517 mSecondaryUser = UserHandle.USER_NULL;
518 }
519 // Connect to the new secondary user's service (purely to ensure that a persistent
520 // SystemUI application is created for that user)
Xiaohui Chenec015d52016-05-05 16:28:35 -0700521 if (userInfo != null && userInfo.id != UserHandle.USER_SYSTEM) {
Winson3c2c34b2016-04-04 17:47:41 -0700522 context.startServiceAsUser(mSecondaryUserServiceIntent,
523 UserHandle.of(userInfo.id));
524 mSecondaryUser = userInfo.id;
525 }
Adrian Roos88b11932015-07-22 14:59:48 -0700526 unpauseRefreshUsers = true;
527 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200528 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
529 UserHandle.USER_NULL);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800530 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
531 // Unlocking the system user may require a refresh
532 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
533 if (userId != UserHandle.USER_SYSTEM) {
534 return;
535 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200536 }
537 refreshUsers(forcePictureLoadForId);
Adrian Roos88b11932015-07-22 14:59:48 -0700538 if (unpauseRefreshUsers) {
539 mUnpauseRefreshUsers.run();
540 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200541 }
542 };
543
Adrian Roos88b11932015-07-22 14:59:48 -0700544 private final Runnable mUnpauseRefreshUsers = new Runnable() {
545 @Override
546 public void run() {
547 mHandler.removeCallbacks(this);
548 mPauseRefreshUsers = false;
549 refreshUsers(UserHandle.USER_NULL);
550 }
551 };
552
Adrian Roosccdff622014-08-06 00:07:18 +0200553 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
Jason Monk2daf62c2014-07-31 14:35:06 -0400554 public void onChange(boolean selfChange) {
555 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
556 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
Adrian Roosccdff622014-08-06 00:07:18 +0200557 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
558 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
Jason Monk2daf62c2014-07-31 14:35:06 -0400559 refreshUsers(UserHandle.USER_NULL);
560 };
561 };
562
Evan Laird97eaacb2018-09-04 15:06:37 -0400563 @Override
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200564 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
565 pw.println("UserSwitcherController state:");
Adrian Roos70441462014-07-22 16:03:12 +0200566 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200567 pw.print(" mUsers.size="); pw.println(mUsers.size());
568 for (int i = 0; i < mUsers.size(); i++) {
569 final UserRecord u = mUsers.get(i);
570 pw.print(" "); pw.println(u.toString());
571 }
572 }
573
Adrian Roos57cf5702014-09-03 15:56:30 +0200574 public String getCurrentUserName(Context context) {
575 if (mUsers.isEmpty()) return null;
576 UserRecord item = mUsers.get(0);
577 if (item == null || item.info == null) return null;
578 if (item.isGuest) return context.getString(R.string.guest_nickname);
579 return item.info.name;
580 }
581
Adrian Roosd390b892016-05-05 10:50:49 -0400582 public void onDensityOrFontScaleChanged() {
583 refreshUsers(UserHandle.USER_ALL);
584 }
585
Jason Monk88529052016-11-04 13:29:58 -0400586 @VisibleForTesting
587 public void addAdapter(WeakReference<BaseUserAdapter> adapter) {
588 mAdapters.add(adapter);
589 }
590
591 @VisibleForTesting
Jason Monk88529052016-11-04 13:29:58 -0400592 public ArrayList<UserRecord> getUsers() {
593 return mUsers;
594 }
595
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200596 public static abstract class BaseUserAdapter extends BaseAdapter {
597
598 final UserSwitcherController mController;
Jason Monk9c7844c2017-01-18 15:21:53 -0500599 private final KeyguardMonitor mKeyguardMonitor;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200600
601 protected BaseUserAdapter(UserSwitcherController controller) {
602 mController = controller;
Jason Monk03561a82018-12-21 12:14:58 -0500603 mKeyguardMonitor = controller.mKeyguardMonitor;
Jason Monk88529052016-11-04 13:29:58 -0400604 controller.addAdapter(new WeakReference<>(this));
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200605 }
606
Jason Monke5b770e2017-03-03 21:49:29 -0500607 public int getUserCount() {
608 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
609 && mKeyguardMonitor.isSecure()
610 && !mKeyguardMonitor.canSkipBouncer();
611 if (!secureKeyguardShowing) {
612 return mController.getUsers().size();
613 }
614 // The lock screen is secure and showing. Filter out restricted records.
615 final int N = mController.getUsers().size();
616 int count = 0;
617 for (int i = 0; i < N; i++) {
618 if (mController.getUsers().get(i).isGuest) continue;
619 if (mController.getUsers().get(i).isRestricted) {
620 break;
621 } else {
622 count++;
623 }
624 }
625 return count;
626 }
627
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200628 @Override
629 public int getCount() {
Jason Monk9c7844c2017-01-18 15:21:53 -0500630 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
631 && mKeyguardMonitor.isSecure()
632 && !mKeyguardMonitor.canSkipBouncer();
Adrian Roosccdff622014-08-06 00:07:18 +0200633 if (!secureKeyguardShowing) {
Jason Monk88529052016-11-04 13:29:58 -0400634 return mController.getUsers().size();
Adrian Roosccdff622014-08-06 00:07:18 +0200635 }
636 // The lock screen is secure and showing. Filter out restricted records.
Jason Monk88529052016-11-04 13:29:58 -0400637 final int N = mController.getUsers().size();
Adrian Roosccdff622014-08-06 00:07:18 +0200638 int count = 0;
639 for (int i = 0; i < N; i++) {
Jason Monk88529052016-11-04 13:29:58 -0400640 if (mController.getUsers().get(i).isRestricted) {
Adrian Roosccdff622014-08-06 00:07:18 +0200641 break;
642 } else {
643 count++;
644 }
645 }
646 return count;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200647 }
648
649 @Override
650 public UserRecord getItem(int position) {
Jason Monk88529052016-11-04 13:29:58 -0400651 return mController.getUsers().get(position);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200652 }
653
654 @Override
655 public long getItemId(int position) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200656 return position;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200657 }
658
659 public void switchTo(UserRecord record) {
660 mController.switchTo(record);
661 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200662
663 public String getName(Context context, UserRecord item) {
664 if (item.isGuest) {
665 if (item.isCurrent) {
666 return context.getString(R.string.guest_exit_guest);
667 } else {
668 return context.getString(
669 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
670 }
Adrian Roosccdff622014-08-06 00:07:18 +0200671 } else if (item.isAddUser) {
672 return context.getString(R.string.user_add_user);
Adrian Roose9c7d432014-07-17 18:27:38 +0200673 } else {
674 return item.info.name;
675 }
676 }
Adrian Roos723632e2014-07-23 21:13:21 +0200677
Adrian Roosccdff622014-08-06 00:07:18 +0200678 public Drawable getDrawable(Context context, UserRecord item) {
679 if (item.isAddUser) {
680 return context.getDrawable(R.drawable.ic_add_circle_qs);
681 }
Tony Mak213955e2017-11-23 16:57:08 +0800682 Drawable icon = UserIcons.getDefaultUserIcon(
683 context.getResources(), item.resolveId(), /* light= */ false);
Jason Monk5a655022017-05-24 13:55:23 -0400684 if (item.isGuest) {
Jason Changb4e879d2018-04-11 11:17:58 +0800685 icon.setColorFilter(Utils.getColorAttrDefaultColor(context,
686 android.R.attr.colorForeground),
Jason Monk5a655022017-05-24 13:55:23 -0400687 Mode.SRC_IN);
688 }
689 return icon;
Adrian Roosccdff622014-08-06 00:07:18 +0200690 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100691
692 public void refresh() {
693 mController.refreshUsers(UserHandle.USER_NULL);
694 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200695 }
696
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000697 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700698 EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000699 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
Philip P. Moltmann4e615e62018-08-28 14:57:49 -0700700 if (admin != null && !RestrictedLockUtilsInternal.hasBaseUserRestriction(mContext,
Sudheer Shankaad143c02016-03-31 00:24:05 +0000701 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000702 record.isDisabledByAdmin = true;
Sudheer Shankaad143c02016-03-31 00:24:05 +0000703 record.enforcedAdmin = admin;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000704 } else {
705 record.isDisabledByAdmin = false;
706 record.enforcedAdmin = null;
707 }
708 }
709
710 public void startActivity(Intent intent) {
711 mActivityStarter.startActivity(intent, true);
712 }
713
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200714 public static final class UserRecord {
715 public final UserInfo info;
716 public final Bitmap picture;
717 public final boolean isGuest;
718 public final boolean isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200719 public final boolean isAddUser;
720 /** If true, the record is only visible to the owner and only when unlocked. */
721 public final boolean isRestricted;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000722 public boolean isDisabledByAdmin;
723 public EnforcedAdmin enforcedAdmin;
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800724 public boolean isSwitchToEnabled;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200725
Adrian Roosccdff622014-08-06 00:07:18 +0200726 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800727 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200728 this.info = info;
729 this.picture = picture;
730 this.isGuest = isGuest;
731 this.isCurrent = isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200732 this.isAddUser = isAddUser;
733 this.isRestricted = isRestricted;
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800734 this.isSwitchToEnabled = isSwitchToEnabled;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200735 }
736
737 public UserRecord copyWithIsCurrent(boolean _isCurrent) {
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800738 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
739 isSwitchToEnabled);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200740 }
741
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700742 public int resolveId() {
743 if (isGuest || info == null) {
744 return UserHandle.USER_NULL;
745 }
746 return info.id;
747 }
748
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200749 public String toString() {
750 StringBuilder sb = new StringBuilder();
751 sb.append("UserRecord(");
752 if (info != null) {
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800753 sb.append("name=\"").append(info.name).append("\" id=").append(info.id);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200754 } else {
Adrian Roosccdff622014-08-06 00:07:18 +0200755 if (isGuest) {
756 sb.append("<add guest placeholder>");
757 } else if (isAddUser) {
758 sb.append("<add user placeholder>");
759 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200760 }
Adrian Roosccdff622014-08-06 00:07:18 +0200761 if (isGuest) sb.append(" <isGuest>");
762 if (isAddUser) sb.append(" <isAddUser>");
763 if (isCurrent) sb.append(" <isCurrent>");
764 if (picture != null) sb.append(" <hasPicture>");
765 if (isRestricted) sb.append(" <isRestricted>");
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000766 if (isDisabledByAdmin) {
767 sb.append(" <isDisabledByAdmin>");
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800768 sb.append(" enforcedAdmin=").append(enforcedAdmin);
769 }
770 if (isSwitchToEnabled) {
771 sb.append(" <isSwitchToEnabled>");
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000772 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200773 sb.append(')');
774 return sb.toString();
775 }
776 }
777
Jason Monk46767b72016-08-18 10:58:04 -0400778 public final DetailAdapter userDetailAdapter = new DetailAdapter() {
Jason Monk76c67aa2016-02-19 14:49:42 -0500779 private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200780
781 @Override
Jason Monkc06fbb12016-01-08 14:12:18 -0500782 public CharSequence getTitle() {
783 return mContext.getString(R.string.quick_settings_user_title);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200784 }
785
786 @Override
787 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Adrian Roos19408922014-08-07 20:54:12 +0200788 UserDetailView v;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200789 if (!(convertView instanceof UserDetailView)) {
Adrian Roos19408922014-08-07 20:54:12 +0200790 v = UserDetailView.inflate(context, parent, false);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200791 v.createAndSetAdapter(UserSwitcherController.this);
Adrian Roos19408922014-08-07 20:54:12 +0200792 } else {
793 v = (UserDetailView) convertView;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200794 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100795 v.refreshAdapter();
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200796 return v;
797 }
798
799 @Override
800 public Intent getSettingsIntent() {
801 return USER_SETTINGS_INTENT;
802 }
803
804 @Override
805 public Boolean getToggleState() {
806 return null;
807 }
808
809 @Override
810 public void setToggleState(boolean state) {
811 }
Chris Wren457a21c2015-05-06 17:50:34 -0400812
813 @Override
814 public int getMetricsCategory() {
Chris Wrenf6e9228b2016-01-26 18:04:35 -0500815 return MetricsEvent.QS_USERDETAIL;
Chris Wren457a21c2015-05-06 17:50:34 -0400816 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200817 };
Adrian Roos50052442014-07-17 23:35:18 +0200818
Adrian Roosccdff622014-08-06 00:07:18 +0200819 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
820 @Override
Jason Monkaa573e92017-01-27 17:00:29 -0500821 public void onKeyguardShowingChanged() {
Jorim Jaggie549a8d2017-05-15 02:40:05 +0200822
823 // When Keyguard is going away, we don't need to update our items immediately which
824 // helps making the transition faster.
825 if (!mKeyguardMonitor.isShowing()) {
826 mHandler.post(UserSwitcherController.this::notifyAdapters);
827 } else {
828 notifyAdapters();
829 }
Adrian Roosccdff622014-08-06 00:07:18 +0200830 }
831 };
832
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}