blob: 3dc1a4afd026deca13c4c4e5a8313ce0a4299805 [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
Adrian Roos00a0b1f2014-07-16 16:44:49 +020019import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
Adrian Roos50052442014-07-17 23:35:18 +020021import android.app.Dialog;
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -070022import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
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;
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
Tamas Berghammercbd3f0c2016-06-22 15:21:38 +010049import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010050import com.android.internal.util.UserIcons;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000051import com.android.settingslib.RestrictedLockUtils;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010052import com.android.systemui.GuestResumeSessionReceiver;
53import com.android.systemui.R;
Adrian Roose25c18d2016-06-17 15:59:49 -070054import com.android.systemui.SystemUI;
Winson3c2c34b2016-04-04 17:47:41 -070055import com.android.systemui.SystemUISecondaryUserService;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010056import com.android.systemui.qs.QSTile;
57import com.android.systemui.qs.tiles.UserDetailView;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000058import com.android.systemui.statusbar.phone.ActivityStarter;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010059import com.android.systemui.statusbar.phone.SystemUIDialog;
60
Adrian Roos00a0b1f2014-07-16 16:44:49 +020061import java.io.FileDescriptor;
62import java.io.PrintWriter;
63import java.lang.ref.WeakReference;
64import java.util.ArrayList;
65import java.util.List;
66
Sudheer Shanka1c7cda82015-12-31 14:46:02 +000067import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
68
Adrian Roos00a0b1f2014-07-16 16:44:49 +020069/**
70 * Keeps a list of all users on the device for user switching.
71 */
72public class UserSwitcherController {
73
74 private static final String TAG = "UserSwitcherController";
Adrian Roos50052442014-07-17 23:35:18 +020075 private static final boolean DEBUG = false;
Jason Monk2daf62c2014-07-31 14:35:06 -040076 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
77 "lockscreenSimpleUserSwitcher";
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -070078 private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
Xiaohui Chen860397f2015-07-22 15:03:48 -070079 private static final String ACTION_LOGOUT_USER = "com.android.systemui.LOGOUT_USER";
Adrian Roos88b11932015-07-22 14:59:48 -070080 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020081
Amith Yamasanid81f8272015-07-23 17:15:45 -070082 private static final int ID_REMOVE_GUEST = 1010;
Xiaohui Chen860397f2015-07-22 15:03:48 -070083 private static final int ID_LOGOUT_USER = 1011;
Amith Yamasanid81f8272015-07-23 17:15:45 -070084 private static final String TAG_REMOVE_GUEST = "remove_guest";
Xiaohui Chen860397f2015-07-22 15:03:48 -070085 private static final String TAG_LOGOUT_USER = "logout_user";
86
Amith Yamasanid81f8272015-07-23 17:15:45 -070087 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
88
Adrian Roos00a0b1f2014-07-16 16:44:49 +020089 private final Context mContext;
90 private final UserManager mUserManager;
91 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020092 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
93 = new GuestResumeSessionReceiver();
Adrian Roosccdff622014-08-06 00:07:18 +020094 private final KeyguardMonitor mKeyguardMonitor;
Adrian Roos88b11932015-07-22 14:59:48 -070095 private 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;
Adrian Roosccdff622014-08-06 00:07:18 +0200102 private boolean mSimpleUserSwitcher;
103 private boolean mAddUsersWhenLocked;
Adrian Roos88b11932015-07-22 14:59:48 -0700104 private boolean mPauseRefreshUsers;
Winson3c2c34b2016-04-04 17:47:41 -0700105 private int mSecondaryUser = UserHandle.USER_NULL;
106 private Intent mSecondaryUserServiceIntent;
Adrian Roos88b11932015-07-22 14:59:48 -0700107 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200108
Adrian Roos88b11932015-07-22 14:59:48 -0700109 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000110 Handler handler, ActivityStarter activityStarter) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200111 mContext = context;
Adrian Roos50052442014-07-17 23:35:18 +0200112 mGuestResumeSessionReceiver.register(context);
Adrian Roosccdff622014-08-06 00:07:18 +0200113 mKeyguardMonitor = keyguardMonitor;
Adrian Roos88b11932015-07-22 14:59:48 -0700114 mHandler = handler;
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000115 mActivityStarter = activityStarter;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200116 mUserManager = UserManager.get(context);
117 IntentFilter filter = new IntentFilter();
118 filter.addAction(Intent.ACTION_USER_ADDED);
119 filter.addAction(Intent.ACTION_USER_REMOVED);
120 filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
121 filter.addAction(Intent.ACTION_USER_SWITCHED);
Amith Yamasaniad2e4bf2016-04-26 14:35:54 -0700122 filter.addAction(Intent.ACTION_USER_STOPPED);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800123 filter.addAction(Intent.ACTION_USER_UNLOCKED);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700124 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Adrian Roose9c7d432014-07-17 18:27:38 +0200125 null /* permission */, null /* scheduler */);
Jason Monk2daf62c2014-07-31 14:35:06 -0400126
Winson3c2c34b2016-04-04 17:47:41 -0700127 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
128
Amith Yamasanid81f8272015-07-23 17:15:45 -0700129 filter = new IntentFilter();
130 filter.addAction(ACTION_REMOVE_GUEST);
Xiaohui Chen860397f2015-07-22 15:03:48 -0700131 filter.addAction(ACTION_LOGOUT_USER);
Amith Yamasani5891a342015-07-24 13:50:58 -0700132 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
Amith Yamasanid81f8272015-07-23 17:15:45 -0700133 PERMISSION_SELF, null /* scheduler */);
Adrian Roosccdff622014-08-06 00:07:18 +0200134
Jason Monk2daf62c2014-07-31 14:35:06 -0400135 mContext.getContentResolver().registerContentObserver(
136 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
Adrian Roosccdff622014-08-06 00:07:18 +0200137 mSettingsObserver);
138 mContext.getContentResolver().registerContentObserver(
139 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
140 mSettingsObserver);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800141 mContext.getContentResolver().registerContentObserver(
142 Settings.Global.getUriFor(
143 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED),
144 true, mSettingsObserver);
Adrian Roosccdff622014-08-06 00:07:18 +0200145 // Fetch initial values.
146 mSettingsObserver.onChange(false);
147
148 keyguardMonitor.addCallback(mCallback);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800149 listenForCallState();
Jason Monk2daf62c2014-07-31 14:35:06 -0400150
Adrian Roose9c7d432014-07-17 18:27:38 +0200151 refreshUsers(UserHandle.USER_NULL);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200152 }
153
Adrian Roose9c7d432014-07-17 18:27:38 +0200154 /**
155 * Refreshes users from UserManager.
156 *
157 * The pictures are only loaded if they have not been loaded yet.
158 *
159 * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
160 */
Amith Yamasani95ab7842014-08-11 17:09:26 -0700161 @SuppressWarnings("unchecked")
Adrian Roose9c7d432014-07-17 18:27:38 +0200162 private void refreshUsers(int forcePictureLoadForId) {
Adrian Roos88b11932015-07-22 14:59:48 -0700163 if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
164 if (forcePictureLoadForId != UserHandle.USER_NULL) {
165 mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
166 }
167
168 if (mPauseRefreshUsers) {
169 return;
170 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000171
Adrian Roosd390b892016-05-05 10:50:49 -0400172 boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
Adrian Roosc5db3902014-11-21 13:54:04 +0000173 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
174 final int N = mUsers.size();
175 for (int i = 0; i < N; i++) {
176 UserRecord r = mUsers.get(i);
Adrian Roosd390b892016-05-05 10:50:49 -0400177 if (r == null || r.picture == null || r.info == null || forceAllUsers
178 || mForcePictureLoadForUserId.get(r.info.id)) {
Adrian Roosc5db3902014-11-21 13:54:04 +0000179 continue;
Adrian Roose9c7d432014-07-17 18:27:38 +0200180 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000181 bitmaps.put(r.info.id, r.picture);
Adrian Roose9c7d432014-07-17 18:27:38 +0200182 }
Adrian Roos88b11932015-07-22 14:59:48 -0700183 mForcePictureLoadForUserId.clear();
Adrian Roose9c7d432014-07-17 18:27:38 +0200184
Adrian Roosccdff622014-08-06 00:07:18 +0200185 final boolean addUsersWhenLocked = mAddUsersWhenLocked;
Adrian Roose9c7d432014-07-17 18:27:38 +0200186 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
187 @SuppressWarnings("unchecked")
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200188 @Override
Adrian Roose9c7d432014-07-17 18:27:38 +0200189 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
190 final SparseArray<Bitmap> bitmaps = params[0];
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200191 List<UserInfo> infos = mUserManager.getUsers(true);
192 if (infos == null) {
193 return null;
194 }
195 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
196 int currentId = ActivityManager.getCurrentUser();
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800197 boolean canSwitchUsers = mUserManager.canSwitchUsers();
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700198 UserInfo currentUserInfo = null;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200199 UserRecord guestRecord = null;
200
201 for (UserInfo info : infos) {
202 boolean isCurrent = currentId == info.id;
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700203 if (isCurrent) {
204 currentUserInfo = info;
205 }
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800206 boolean switchToEnabled = canSwitchUsers || isCurrent;
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100207 if (info.isEnabled()) {
208 if (info.isGuest()) {
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800209 // Tapping guest icon triggers remove and a user switch therefore
210 // the icon shouldn't be enabled even if the user is current
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100211 guestRecord = new UserRecord(info, null /* picture */,
212 true /* isGuest */, isCurrent, false /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800213 false /* isRestricted */, canSwitchUsers);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100214 } else if (info.supportsSwitchToByUser()) {
215 Bitmap picture = bitmaps.get(info.id);
216 if (picture == null) {
217 picture = mUserManager.getUserIcon(info.id);
Adrian Rooscba0faa2014-11-17 17:41:28 +0100218
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100219 if (picture != null) {
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700220 int avatarSize = mContext.getResources()
221 .getDimensionPixelSize(R.dimen.max_avatar_size);
222 picture = Bitmap.createScaledBitmap(
223 picture, avatarSize, avatarSize, true);
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100224 }
Adrian Rooscba0faa2014-11-17 17:41:28 +0100225 }
Lenka Trochtova1ddda472016-02-12 10:42:12 +0100226 int index = isCurrent ? 0 : records.size();
227 records.add(index, new UserRecord(info, picture, false /* isGuest */,
228 isCurrent, false /* isAddUser */, false /* isRestricted */,
229 switchToEnabled));
Dan Sandler4d75c072014-07-17 16:01:28 -0400230 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200231 }
232 }
233
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000234 boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
235 UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700236 boolean currentUserCanCreateUsers = currentUserInfo != null
237 && (currentUserInfo.isAdmin()
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000238 || currentUserInfo.id == UserHandle.USER_SYSTEM)
239 && systemCanCreateUsers;
240 boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
241 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Adrian Roosccdff622014-08-06 00:07:18 +0200242 && guestRecord == null;
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000243 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Amith Yamasani95ab7842014-08-11 17:09:26 -0700244 && mUserManager.canAddMoreUsers();
Adrian Roosccdff622014-08-06 00:07:18 +0200245 boolean createIsRestricted = !addUsersWhenLocked;
246
Jason Monk2daf62c2014-07-31 14:35:06 -0400247 if (!mSimpleUserSwitcher) {
248 if (guestRecord == null) {
Adrian Roosccdff622014-08-06 00:07:18 +0200249 if (canCreateGuest) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000250 guestRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200251 true /* isGuest */, false /* isCurrent */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800252 false /* isAddUser */, createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000253 checkIfAddUserDisallowedByAdminOnly(guestRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000254 records.add(guestRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200255 }
Jason Monk2daf62c2014-07-31 14:35:06 -0400256 } else {
Adrian Roosbed6e3b2014-08-21 20:31:41 +0200257 int index = guestRecord.isCurrent ? 0 : records.size();
258 records.add(index, guestRecord);
Jason Monk2daf62c2014-07-31 14:35:06 -0400259 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200260 }
261
Jason Monk092be7d2014-09-02 15:26:13 -0400262 if (!mSimpleUserSwitcher && canCreateUser) {
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000263 UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200264 false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800265 createIsRestricted, canSwitchUsers);
Sudheer Shankaa8fbbb32016-02-11 17:17:57 +0000266 checkIfAddUserDisallowedByAdminOnly(addUserRecord);
Sudheer Shanka1c7cda82015-12-31 14:46:02 +0000267 records.add(addUserRecord);
Adrian Roosccdff622014-08-06 00:07:18 +0200268 }
269
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200270 return records;
271 }
272
273 @Override
274 protected void onPostExecute(ArrayList<UserRecord> userRecords) {
275 if (userRecords != null) {
276 mUsers = userRecords;
277 notifyAdapters();
278 }
279 }
Adrian Roosccdff622014-08-06 00:07:18 +0200280 }.execute((SparseArray) bitmaps);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200281 }
282
Adrian Roos88b11932015-07-22 14:59:48 -0700283 private void pauseRefreshUsers() {
284 if (!mPauseRefreshUsers) {
285 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
286 mPauseRefreshUsers = true;
287 }
288 }
289
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200290 private void notifyAdapters() {
291 for (int i = mAdapters.size() - 1; i >= 0; i--) {
292 BaseUserAdapter adapter = mAdapters.get(i).get();
293 if (adapter != null) {
294 adapter.notifyDataSetChanged();
295 } else {
296 mAdapters.remove(i);
297 }
298 }
299 }
300
Jason Monk2daf62c2014-07-31 14:35:06 -0400301 public boolean isSimpleUserSwitcher() {
302 return mSimpleUserSwitcher;
303 }
304
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700305 public boolean useFullscreenUserSwitcher() {
306 // Use adb to override:
307 // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
308 // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
309 // Restart SystemUI or adb reboot.
310 final int DEFAULT = -1;
311 final int overrideUseFullscreenUserSwitcher =
312 Settings.System.getInt(mContext.getContentResolver(),
313 "enable_fullscreen_user_switcher", DEFAULT);
314 if (overrideUseFullscreenUserSwitcher != DEFAULT) {
315 return overrideUseFullscreenUserSwitcher != 0;
316 }
317 // Otherwise default to the build setting.
Xiyuan Xia40f9dab2015-08-17 13:19:30 -0700318 return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700319 }
320
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800321 public void logoutCurrentUser() {
322 int currentUser = ActivityManager.getCurrentUser();
323 if (currentUser != UserHandle.USER_SYSTEM) {
Fyodor Kupolovb5690bc32016-03-02 17:50:58 -0800324 pauseRefreshUsers();
325 ActivityManager.logoutCurrentUser();
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800326 }
327 }
328
Xiyuan Xiacc3a74f62015-07-22 14:16:34 -0700329 public void removeUserId(int userId) {
330 if (userId == UserHandle.USER_SYSTEM) {
331 Log.w(TAG, "User " + userId + " could not removed.");
332 return;
333 }
334 if (ActivityManager.getCurrentUser() == userId) {
335 switchToUserId(UserHandle.USER_SYSTEM);
336 }
337 if (mUserManager.removeUser(userId)) {
338 refreshUsers(UserHandle.USER_NULL);
339 }
340 }
341
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200342 public void switchTo(UserRecord record) {
343 int id;
344 if (record.isGuest && record.info == null) {
345 // No guest user. Create one.
Adrian Roosf99727c2014-09-17 16:15:07 +0200346 UserInfo guest = mUserManager.createGuest(
347 mContext, mContext.getString(R.string.guest_nickname));
348 if (guest == null) {
349 // Couldn't create guest, most likely because there already exists one, we just
350 // haven't reloaded the user list yet.
351 return;
352 }
353 id = guest.id;
Adrian Roosccdff622014-08-06 00:07:18 +0200354 } else if (record.isAddUser) {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200355 showAddUserDialog();
356 return;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200357 } else {
358 id = record.info.id;
359 }
360
361 if (ActivityManager.getCurrentUser() == id) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200362 if (record.isGuest) {
Adrian Roos50052442014-07-17 23:35:18 +0200363 showExitGuestDialog(id);
Adrian Roose9c7d432014-07-17 18:27:38 +0200364 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200365 return;
366 }
367
Adrian Roose9c7d432014-07-17 18:27:38 +0200368 switchToUserId(id);
369 }
370
Fyodor Kupolovcaab2662016-03-01 18:39:27 -0800371 public void switchTo(int userId) {
372 final int count = mUsers.size();
373 for (int i = 0; i < count; ++i) {
374 UserRecord record = mUsers.get(i);
375 if (record.info != null && record.info.id == userId) {
376 switchTo(record);
377 return;
378 }
379 }
380
381 Log.e(TAG, "Couldn't switch to user, id=" + userId);
382 }
383
Daniel Nishi8ba446c2016-06-20 18:56:35 -0700384 public int getSwitchableUserCount() {
385 int count = 0;
386 final int N = mUsers.size();
387 for (int i = 0; i < N; ++i) {
388 UserRecord record = mUsers.get(i);
389 if (record.info != null && record.info.supportsSwitchTo()) {
390 count++;
391 }
392 }
393 return count;
394 }
395
Adrian Roose9c7d432014-07-17 18:27:38 +0200396 private void switchToUserId(int id) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200397 try {
Adrian Roos88b11932015-07-22 14:59:48 -0700398 pauseRefreshUsers();
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200399 ActivityManagerNative.getDefault().switchUser(id);
400 } catch (RemoteException e) {
401 Log.e(TAG, "Couldn't switch user.", e);
402 }
403 }
404
Adrian Roos50052442014-07-17 23:35:18 +0200405 private void showExitGuestDialog(int id) {
406 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
407 mExitGuestDialog.cancel();
408 }
409 mExitGuestDialog = new ExitGuestDialog(mContext, id);
410 mExitGuestDialog.show();
411 }
412
Adrian Roos0c6763a2014-09-08 19:11:00 +0200413 private void showAddUserDialog() {
414 if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
415 mAddUserDialog.cancel();
416 }
417 mAddUserDialog = new AddUserDialog(mContext);
418 mAddUserDialog.show();
419 }
420
Adrian Roose9c7d432014-07-17 18:27:38 +0200421 private void exitGuest(int id) {
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700422 int newId = UserHandle.USER_SYSTEM;
423 if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
Adrian Roos70441462014-07-22 16:03:12 +0200424 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700425 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
Adrian Roos70441462014-07-22 16:03:12 +0200426 newId = info.id;
427 }
428 }
429 switchToUserId(newId);
Adrian Roose9c7d432014-07-17 18:27:38 +0200430 mUserManager.removeUser(id);
431 }
432
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800433 private void listenForCallState() {
Jason Monk705a4bc2016-06-30 15:04:11 -0400434 TelephonyManager.from(mContext).listen(mPhoneStateListener,
435 PhoneStateListener.LISTEN_CALL_STATE);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800436 }
437
Jason Monk705a4bc2016-06-30 15:04:11 -0400438 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
439 private int mCallState;
440
441 @Override
442 public void onCallStateChanged(int state, String incomingNumber) {
443 if (mCallState == state) return;
444 if (DEBUG) Log.v(TAG, "Call state changed: " + state);
445 mCallState = state;
446 int currentUserId = ActivityManager.getCurrentUser();
447 UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
448 if (userInfo != null && userInfo.isGuest()) {
449 showGuestNotification(currentUserId);
450 }
451 refreshUsers(UserHandle.USER_NULL);
452 }
453 };
454
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200455 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
456 @Override
457 public void onReceive(Context context, Intent intent) {
Adrian Roos50052442014-07-17 23:35:18 +0200458 if (DEBUG) {
459 Log.v(TAG, "Broadcast: a=" + intent.getAction()
460 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
461 }
Adrian Roos88b11932015-07-22 14:59:48 -0700462
463 boolean unpauseRefreshUsers = false;
464 int forcePictureLoadForId = UserHandle.USER_NULL;
465
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700466 if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
467 int currentUser = ActivityManager.getCurrentUser();
468 UserInfo userInfo = mUserManager.getUserInfo(currentUser);
469 if (userInfo != null && userInfo.isGuest()) {
470 showExitGuestDialog(currentUser);
471 }
472 return;
Adrian Roos80996bb2015-07-24 16:38:58 -0700473 } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
Xiyuan Xia1b30f792016-01-06 08:50:30 -0800474 logoutCurrentUser();
Adrian Roos88b11932015-07-22 14:59:48 -0700475 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
Adrian Roos50052442014-07-17 23:35:18 +0200476 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
477 mExitGuestDialog.cancel();
478 mExitGuestDialog = null;
479 }
480
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200481 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700482 final UserInfo userInfo = mUserManager.getUserInfo(currentId);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200483 final int N = mUsers.size();
484 for (int i = 0; i < N; i++) {
485 UserRecord record = mUsers.get(i);
Adrian Roose9c7d432014-07-17 18:27:38 +0200486 if (record.info == null) continue;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200487 boolean shouldBeCurrent = record.info.id == currentId;
488 if (record.isCurrent != shouldBeCurrent) {
489 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
490 }
Adrian Roos70441462014-07-22 16:03:12 +0200491 if (shouldBeCurrent && !record.isGuest) {
492 mLastNonGuestUser = record.info.id;
493 }
Xiaohui Chen7cb69df2015-07-13 16:01:01 -0700494 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {
Adrian Roosccdff622014-08-06 00:07:18 +0200495 // Immediately remove restricted records in case the AsyncTask is too slow.
496 mUsers.remove(i);
497 i--;
498 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200499 }
500 notifyAdapters();
Xiaohui Chen860397f2015-07-22 15:03:48 -0700501
Winson3c2c34b2016-04-04 17:47:41 -0700502 // Disconnect from the old secondary user's service
503 if (mSecondaryUser != UserHandle.USER_NULL) {
504 context.stopServiceAsUser(mSecondaryUserServiceIntent,
505 UserHandle.of(mSecondaryUser));
506 mSecondaryUser = UserHandle.USER_NULL;
507 }
508 // Connect to the new secondary user's service (purely to ensure that a persistent
509 // SystemUI application is created for that user)
510 if (userInfo != null && !userInfo.isPrimary()) {
511 context.startServiceAsUser(mSecondaryUserServiceIntent,
512 UserHandle.of(userInfo.id));
513 mSecondaryUser = userInfo.id;
514 }
515
Xiaohui Chen860397f2015-07-22 15:03:48 -0700516 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
517 && userInfo.id != UserHandle.USER_SYSTEM) {
518 showLogoutNotification(currentId);
519 }
Fyodor Kupolovce161862015-08-18 12:41:52 -0700520 if (userInfo != null && userInfo.isGuest()) {
521 showGuestNotification(currentId);
522 }
Adrian Roos88b11932015-07-22 14:59:48 -0700523 unpauseRefreshUsers = true;
524 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200525 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
526 UserHandle.USER_NULL);
Fyodor Kupolov07140f72016-02-17 10:46:11 -0800527 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
528 // Unlocking the system user may require a refresh
529 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
530 if (userId != UserHandle.USER_SYSTEM) {
531 return;
532 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200533 }
534 refreshUsers(forcePictureLoadForId);
Adrian Roos88b11932015-07-22 14:59:48 -0700535 if (unpauseRefreshUsers) {
536 mUnpauseRefreshUsers.run();
537 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200538 }
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700539
Xiaohui Chen860397f2015-07-22 15:03:48 -0700540 private void showLogoutNotification(int userId) {
541 PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext,
542 0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
Adrian Roose25c18d2016-06-17 15:59:49 -0700543 Notification.Builder builder = new Notification.Builder(mContext)
Xiaohui Chen860397f2015-07-22 15:03:48 -0700544 .setVisibility(Notification.VISIBILITY_SECRET)
545 .setPriority(Notification.PRIORITY_MIN)
546 .setSmallIcon(R.drawable.ic_person)
547 .setContentTitle(mContext.getString(R.string.user_logout_notification_title))
548 .setContentText(mContext.getString(R.string.user_logout_notification_text))
Fyodor Kupolov52408f42015-08-17 16:34:07 -0700549 .setContentIntent(logoutPI)
Fyodor Kupolovce161862015-08-18 12:41:52 -0700550 .setOngoing(true)
Xiaohui Chen860397f2015-07-22 15:03:48 -0700551 .setShowWhen(false)
552 .addAction(R.drawable.ic_delete,
553 mContext.getString(R.string.user_logout_notification_action),
Adrian Roose25c18d2016-06-17 15:59:49 -0700554 logoutPI);
555 SystemUI.overrideNotificationAppName(mContext, builder);
Xiaohui Chen860397f2015-07-22 15:03:48 -0700556 NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, ID_LOGOUT_USER,
Adrian Roose25c18d2016-06-17 15:59:49 -0700557 builder.build(), new UserHandle(userId));
Xiaohui Chen860397f2015-07-22 15:03:48 -0700558 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200559 };
560
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800561 private void showGuestNotification(int guestUserId) {
562 boolean canSwitchUsers = mUserManager.canSwitchUsers();
563 // Disable 'Remove guest' action if cannot switch users right now
564 PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext,
565 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null;
566
Adrian Roose25c18d2016-06-17 15:59:49 -0700567 Notification.Builder builder = new Notification.Builder(mContext)
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800568 .setVisibility(Notification.VISIBILITY_SECRET)
569 .setPriority(Notification.PRIORITY_MIN)
570 .setSmallIcon(R.drawable.ic_person)
571 .setContentTitle(mContext.getString(R.string.guest_notification_title))
572 .setContentText(mContext.getString(R.string.guest_notification_text))
573 .setContentIntent(removeGuestPI)
574 .setShowWhen(false)
575 .addAction(R.drawable.ic_delete,
576 mContext.getString(R.string.guest_notification_remove_action),
Adrian Roose25c18d2016-06-17 15:59:49 -0700577 removeGuestPI);
578 SystemUI.overrideNotificationAppName(mContext, builder);
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800579 NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
Adrian Roose25c18d2016-06-17 15:59:49 -0700580 builder.build(), new UserHandle(guestUserId));
Fyodor Kupolov523c4042016-02-24 15:03:13 -0800581 }
582
Adrian Roos88b11932015-07-22 14:59:48 -0700583 private final Runnable mUnpauseRefreshUsers = new Runnable() {
584 @Override
585 public void run() {
586 mHandler.removeCallbacks(this);
587 mPauseRefreshUsers = false;
588 refreshUsers(UserHandle.USER_NULL);
589 }
590 };
591
Adrian Roosccdff622014-08-06 00:07:18 +0200592 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
Jason Monk2daf62c2014-07-31 14:35:06 -0400593 public void onChange(boolean selfChange) {
594 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
595 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
Adrian Roosccdff622014-08-06 00:07:18 +0200596 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
597 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
Jason Monk2daf62c2014-07-31 14:35:06 -0400598 refreshUsers(UserHandle.USER_NULL);
599 };
600 };
601
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200602 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
603 pw.println("UserSwitcherController state:");
Adrian Roos70441462014-07-22 16:03:12 +0200604 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200605 pw.print(" mUsers.size="); pw.println(mUsers.size());
606 for (int i = 0; i < mUsers.size(); i++) {
607 final UserRecord u = mUsers.get(i);
608 pw.print(" "); pw.println(u.toString());
609 }
610 }
611
Adrian Roos57cf5702014-09-03 15:56:30 +0200612 public String getCurrentUserName(Context context) {
613 if (mUsers.isEmpty()) return null;
614 UserRecord item = mUsers.get(0);
615 if (item == null || item.info == null) return null;
616 if (item.isGuest) return context.getString(R.string.guest_nickname);
617 return item.info.name;
618 }
619
Adrian Roosd390b892016-05-05 10:50:49 -0400620 public void onDensityOrFontScaleChanged() {
621 refreshUsers(UserHandle.USER_ALL);
622 }
623
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200624 public static abstract class BaseUserAdapter extends BaseAdapter {
625
626 final UserSwitcherController mController;
627
628 protected BaseUserAdapter(UserSwitcherController controller) {
629 mController = controller;
630 controller.mAdapters.add(new WeakReference<>(this));
631 }
632
633 @Override
634 public int getCount() {
Adrian Roosccdff622014-08-06 00:07:18 +0200635 boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing()
Jason Monk8a3a9642015-06-05 11:01:30 -0400636 && mController.mKeyguardMonitor.isSecure()
Selim Cineke8bae622015-07-15 13:24:06 -0700637 && !mController.mKeyguardMonitor.canSkipBouncer();
Adrian Roosccdff622014-08-06 00:07:18 +0200638 if (!secureKeyguardShowing) {
639 return mController.mUsers.size();
640 }
641 // The lock screen is secure and showing. Filter out restricted records.
642 final int N = mController.mUsers.size();
643 int count = 0;
644 for (int i = 0; i < N; i++) {
645 if (mController.mUsers.get(i).isRestricted) {
646 break;
647 } else {
648 count++;
649 }
650 }
651 return count;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200652 }
653
654 @Override
655 public UserRecord getItem(int position) {
656 return mController.mUsers.get(position);
657 }
658
659 @Override
660 public long getItemId(int position) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200661 return position;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200662 }
663
664 public void switchTo(UserRecord record) {
665 mController.switchTo(record);
666 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200667
668 public String getName(Context context, UserRecord item) {
669 if (item.isGuest) {
670 if (item.isCurrent) {
671 return context.getString(R.string.guest_exit_guest);
672 } else {
673 return context.getString(
674 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
675 }
Adrian Roosccdff622014-08-06 00:07:18 +0200676 } else if (item.isAddUser) {
677 return context.getString(R.string.user_add_user);
Adrian Roose9c7d432014-07-17 18:27:38 +0200678 } else {
679 return item.info.name;
680 }
681 }
Adrian Roos723632e2014-07-23 21:13:21 +0200682
Adrian Roosccdff622014-08-06 00:07:18 +0200683 public Drawable getDrawable(Context context, UserRecord item) {
684 if (item.isAddUser) {
685 return context.getDrawable(R.drawable.ic_add_circle_qs);
686 }
Evan Roskyaa7f51f2016-03-16 13:15:53 -0700687 return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
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) {
Sudheer Shankaad143c02016-03-31 00:24:05 +0000696 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
697 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
698 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
699 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
776 public final QSTile.DetailAdapter userDetailAdapter = new QSTile.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
Adrian Roosccdff622014-08-06 00:07:18 +0200817 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
818 @Override
819 public void onKeyguardChanged() {
820 notifyAdapters();
821 }
822 };
823
Adrian Roos50052442014-07-17 23:35:18 +0200824 private final class ExitGuestDialog extends SystemUIDialog implements
825 DialogInterface.OnClickListener {
826
827 private final int mGuestId;
828
829 public ExitGuestDialog(Context context, int guestId) {
830 super(context);
831 setTitle(R.string.guest_exit_guest_dialog_title);
832 setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
833 setButton(DialogInterface.BUTTON_NEGATIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700834 context.getString(android.R.string.cancel), this);
Adrian Roos50052442014-07-17 23:35:18 +0200835 setButton(DialogInterface.BUTTON_POSITIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700836 context.getString(R.string.guest_exit_guest_dialog_remove), this);
Adrian Roos50052442014-07-17 23:35:18 +0200837 setCanceledOnTouchOutside(false);
838 mGuestId = guestId;
839 }
840
841 @Override
842 public void onClick(DialogInterface dialog, int which) {
843 if (which == BUTTON_NEGATIVE) {
844 cancel();
845 } else {
846 dismiss();
847 exitGuest(mGuestId);
848 }
849 }
850 }
Adrian Roos0c6763a2014-09-08 19:11:00 +0200851
852 private final class AddUserDialog extends SystemUIDialog implements
853 DialogInterface.OnClickListener {
854
855 public AddUserDialog(Context context) {
856 super(context);
857 setTitle(R.string.user_add_user_title);
858 setMessage(context.getString(R.string.user_add_user_message_short));
859 setButton(DialogInterface.BUTTON_NEGATIVE,
860 context.getString(android.R.string.cancel), this);
861 setButton(DialogInterface.BUTTON_POSITIVE,
862 context.getString(android.R.string.ok), this);
863 }
864
865 @Override
866 public void onClick(DialogInterface dialog, int which) {
867 if (which == BUTTON_NEGATIVE) {
868 cancel();
869 } else {
870 dismiss();
Guang Zhuccbeb612014-10-21 14:33:50 -0700871 if (ActivityManager.isUserAMonkey()) {
872 return;
873 }
Xiaohui Chencfe64c82015-07-16 14:30:50 -0700874 UserInfo user = mUserManager.createUser(
Adrian Roosf99727c2014-09-17 16:15:07 +0200875 mContext.getString(R.string.user_new_user_name), 0 /* flags */);
876 if (user == null) {
877 // Couldn't create user, most likely because there are too many, but we haven't
878 // been able to reload the list yet.
879 return;
880 }
881 int id = user.id;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +0100882 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
883 id, /* light= */ false));
884 mUserManager.setUserIcon(id, icon);
Adrian Roos0c6763a2014-09-08 19:11:00 +0200885 switchToUserId(id);
886 }
887 }
888 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200889}