blob: 3e8d4e909d61706444776a08cfb17faf3ce08033 [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;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020040import android.util.Log;
Adrian Roose9c7d432014-07-17 18:27:38 +020041import android.util.SparseArray;
Adrian Roos88b11932015-07-22 14:59:48 -070042import android.util.SparseBooleanArray;
43import android.util.SparseIntArray;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020044import android.view.View;
45import android.view.ViewGroup;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020046import android.widget.BaseAdapter;
47
Chris Wren457a21c2015-05-06 17:50:34 -040048import com.android.internal.logging.MetricsLogger;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +010049import com.android.internal.util.UserIcons;
50import com.android.systemui.BitmapHelper;
51import com.android.systemui.GuestResumeSessionReceiver;
52import com.android.systemui.R;
53import com.android.systemui.qs.QSTile;
54import com.android.systemui.qs.tiles.UserDetailView;
55import com.android.systemui.statusbar.phone.SystemUIDialog;
56
Adrian Roos00a0b1f2014-07-16 16:44:49 +020057import java.io.FileDescriptor;
58import java.io.PrintWriter;
59import java.lang.ref.WeakReference;
60import java.util.ArrayList;
61import java.util.List;
62
63/**
64 * Keeps a list of all users on the device for user switching.
65 */
66public class UserSwitcherController {
67
68 private static final String TAG = "UserSwitcherController";
Adrian Roos50052442014-07-17 23:35:18 +020069 private static final boolean DEBUG = false;
Jason Monk2daf62c2014-07-31 14:35:06 -040070 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
71 "lockscreenSimpleUserSwitcher";
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -070072 private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
Adrian Roos88b11932015-07-22 14:59:48 -070073 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020074
Amith Yamasanid81f8272015-07-23 17:15:45 -070075 private static final int ID_REMOVE_GUEST = 1010;
76 private static final String TAG_REMOVE_GUEST = "remove_guest";
77 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
78
Adrian Roos00a0b1f2014-07-16 16:44:49 +020079 private final Context mContext;
80 private final UserManager mUserManager;
81 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020082 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
83 = new GuestResumeSessionReceiver();
Adrian Roosccdff622014-08-06 00:07:18 +020084 private final KeyguardMonitor mKeyguardMonitor;
Adrian Roos88b11932015-07-22 14:59:48 -070085 private final Handler mHandler;
Adrian Roos00a0b1f2014-07-16 16:44:49 +020086
87 private ArrayList<UserRecord> mUsers = new ArrayList<>();
Adrian Roos50052442014-07-17 23:35:18 +020088 private Dialog mExitGuestDialog;
Adrian Roos0c6763a2014-09-08 19:11:00 +020089 private Dialog mAddUserDialog;
Adrian Roos70441462014-07-22 16:03:12 +020090 private int mLastNonGuestUser = UserHandle.USER_OWNER;
Adrian Roosccdff622014-08-06 00:07:18 +020091 private boolean mSimpleUserSwitcher;
92 private boolean mAddUsersWhenLocked;
Adrian Roos88b11932015-07-22 14:59:48 -070093 private boolean mPauseRefreshUsers;
94 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
Adrian Roos00a0b1f2014-07-16 16:44:49 +020095
Adrian Roos88b11932015-07-22 14:59:48 -070096 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
97 Handler handler) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +020098 mContext = context;
Adrian Roos50052442014-07-17 23:35:18 +020099 mGuestResumeSessionReceiver.register(context);
Adrian Roosccdff622014-08-06 00:07:18 +0200100 mKeyguardMonitor = keyguardMonitor;
Adrian Roos88b11932015-07-22 14:59:48 -0700101 mHandler = handler;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200102 mUserManager = UserManager.get(context);
103 IntentFilter filter = new IntentFilter();
104 filter.addAction(Intent.ACTION_USER_ADDED);
105 filter.addAction(Intent.ACTION_USER_REMOVED);
106 filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
107 filter.addAction(Intent.ACTION_USER_SWITCHED);
Adrian Roose9c7d432014-07-17 18:27:38 +0200108 filter.addAction(Intent.ACTION_USER_STOPPING);
109 mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
110 null /* permission */, null /* scheduler */);
Jason Monk2daf62c2014-07-31 14:35:06 -0400111
Amith Yamasanid81f8272015-07-23 17:15:45 -0700112 filter = new IntentFilter();
113 filter.addAction(ACTION_REMOVE_GUEST);
114 mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
115 PERMISSION_SELF, null /* scheduler */);
Adrian Roosccdff622014-08-06 00:07:18 +0200116
Jason Monk2daf62c2014-07-31 14:35:06 -0400117 mContext.getContentResolver().registerContentObserver(
118 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
Adrian Roosccdff622014-08-06 00:07:18 +0200119 mSettingsObserver);
120 mContext.getContentResolver().registerContentObserver(
121 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
122 mSettingsObserver);
123 // Fetch initial values.
124 mSettingsObserver.onChange(false);
125
126 keyguardMonitor.addCallback(mCallback);
Jason Monk2daf62c2014-07-31 14:35:06 -0400127
Adrian Roose9c7d432014-07-17 18:27:38 +0200128 refreshUsers(UserHandle.USER_NULL);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200129 }
130
Adrian Roose9c7d432014-07-17 18:27:38 +0200131 /**
132 * Refreshes users from UserManager.
133 *
134 * The pictures are only loaded if they have not been loaded yet.
135 *
136 * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
137 */
Amith Yamasani95ab7842014-08-11 17:09:26 -0700138 @SuppressWarnings("unchecked")
Adrian Roose9c7d432014-07-17 18:27:38 +0200139 private void refreshUsers(int forcePictureLoadForId) {
Adrian Roos88b11932015-07-22 14:59:48 -0700140 if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
141 if (forcePictureLoadForId != UserHandle.USER_NULL) {
142 mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
143 }
144
145 if (mPauseRefreshUsers) {
146 return;
147 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000148
149 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
150 final int N = mUsers.size();
151 for (int i = 0; i < N; i++) {
152 UserRecord r = mUsers.get(i);
Adrian Roos88b11932015-07-22 14:59:48 -0700153 if (r == null || r.picture == null ||
154 r.info == null || mForcePictureLoadForUserId.get(r.info.id)) {
Adrian Roosc5db3902014-11-21 13:54:04 +0000155 continue;
Adrian Roose9c7d432014-07-17 18:27:38 +0200156 }
Adrian Roosc5db3902014-11-21 13:54:04 +0000157 bitmaps.put(r.info.id, r.picture);
Adrian Roose9c7d432014-07-17 18:27:38 +0200158 }
Adrian Roos88b11932015-07-22 14:59:48 -0700159 mForcePictureLoadForUserId.clear();
Adrian Roose9c7d432014-07-17 18:27:38 +0200160
Adrian Roosccdff622014-08-06 00:07:18 +0200161 final boolean addUsersWhenLocked = mAddUsersWhenLocked;
Adrian Roose9c7d432014-07-17 18:27:38 +0200162 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
163 @SuppressWarnings("unchecked")
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200164 @Override
Adrian Roose9c7d432014-07-17 18:27:38 +0200165 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
166 final SparseArray<Bitmap> bitmaps = params[0];
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200167 List<UserInfo> infos = mUserManager.getUsers(true);
168 if (infos == null) {
169 return null;
170 }
171 ArrayList<UserRecord> records = new ArrayList<>(infos.size());
172 int currentId = ActivityManager.getCurrentUser();
173 UserRecord guestRecord = null;
Dan Sandler4d75c072014-07-17 16:01:28 -0400174 int avatarSize = mContext.getResources()
175 .getDimensionPixelSize(R.dimen.max_avatar_size);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200176
177 for (UserInfo info : infos) {
178 boolean isCurrent = currentId == info.id;
179 if (info.isGuest()) {
180 guestRecord = new UserRecord(info, null /* picture */,
Adrian Roosccdff622014-08-06 00:07:18 +0200181 true /* isGuest */, isCurrent, false /* isAddUser */,
182 false /* isRestricted */);
Adrian Roos723632e2014-07-23 21:13:21 +0200183 } else if (info.supportsSwitchTo()) {
Adrian Roosc5db3902014-11-21 13:54:04 +0000184 Bitmap picture = bitmaps.get(info.id);
185 if (picture == null) {
186 picture = mUserManager.getUserIcon(info.id);
Adrian Rooscba0faa2014-11-17 17:41:28 +0100187
Adrian Roosc5db3902014-11-21 13:54:04 +0000188 if (picture != null) {
Adrian Rooscba0faa2014-11-17 17:41:28 +0100189 picture = BitmapHelper.createCircularClip(
Adrian Roosc5db3902014-11-21 13:54:04 +0000190 picture, avatarSize, avatarSize);
Adrian Rooscba0faa2014-11-17 17:41:28 +0100191 }
Dan Sandler4d75c072014-07-17 16:01:28 -0400192 }
Adrian Roosbed6e3b2014-08-21 20:31:41 +0200193 int index = isCurrent ? 0 : records.size();
194 records.add(index, new UserRecord(info, picture, false /* isGuest */,
195 isCurrent, false /* isAddUser */, false /* isRestricted */));
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200196 }
197 }
198
Adrian Roosccdff622014-08-06 00:07:18 +0200199 boolean ownerCanCreateUsers = !mUserManager.hasUserRestriction(
200 UserManager.DISALLOW_ADD_USER, UserHandle.OWNER);
201 boolean currentUserCanCreateUsers =
202 (currentId == UserHandle.USER_OWNER) && ownerCanCreateUsers;
203 boolean anyoneCanCreateUsers = ownerCanCreateUsers && addUsersWhenLocked;
204 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
205 && guestRecord == null;
206 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
Amith Yamasani95ab7842014-08-11 17:09:26 -0700207 && mUserManager.canAddMoreUsers();
Adrian Roosccdff622014-08-06 00:07:18 +0200208 boolean createIsRestricted = !addUsersWhenLocked;
209
Jason Monk2daf62c2014-07-31 14:35:06 -0400210 if (!mSimpleUserSwitcher) {
211 if (guestRecord == null) {
Adrian Roosccdff622014-08-06 00:07:18 +0200212 if (canCreateGuest) {
213 records.add(new UserRecord(null /* info */, null /* picture */,
214 true /* isGuest */, false /* isCurrent */,
215 false /* isAddUser */, createIsRestricted));
216 }
Jason Monk2daf62c2014-07-31 14:35:06 -0400217 } else {
Adrian Roosbed6e3b2014-08-21 20:31:41 +0200218 int index = guestRecord.isCurrent ? 0 : records.size();
219 records.add(index, guestRecord);
Jason Monk2daf62c2014-07-31 14:35:06 -0400220 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200221 }
222
Jason Monk092be7d2014-09-02 15:26:13 -0400223 if (!mSimpleUserSwitcher && canCreateUser) {
Adrian Roosccdff622014-08-06 00:07:18 +0200224 records.add(new UserRecord(null /* info */, null /* picture */,
225 false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
226 createIsRestricted));
227 }
228
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200229 return records;
230 }
231
232 @Override
233 protected void onPostExecute(ArrayList<UserRecord> userRecords) {
234 if (userRecords != null) {
235 mUsers = userRecords;
236 notifyAdapters();
237 }
238 }
Adrian Roosccdff622014-08-06 00:07:18 +0200239 }.execute((SparseArray) bitmaps);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200240 }
241
Adrian Roos88b11932015-07-22 14:59:48 -0700242 private void pauseRefreshUsers() {
243 if (!mPauseRefreshUsers) {
244 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
245 mPauseRefreshUsers = true;
246 }
247 }
248
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200249 private void notifyAdapters() {
250 for (int i = mAdapters.size() - 1; i >= 0; i--) {
251 BaseUserAdapter adapter = mAdapters.get(i).get();
252 if (adapter != null) {
253 adapter.notifyDataSetChanged();
254 } else {
255 mAdapters.remove(i);
256 }
257 }
258 }
259
Jason Monk2daf62c2014-07-31 14:35:06 -0400260 public boolean isSimpleUserSwitcher() {
261 return mSimpleUserSwitcher;
262 }
263
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200264 public void switchTo(UserRecord record) {
265 int id;
266 if (record.isGuest && record.info == null) {
267 // No guest user. Create one.
Adrian Roosf99727c2014-09-17 16:15:07 +0200268 UserInfo guest = mUserManager.createGuest(
269 mContext, mContext.getString(R.string.guest_nickname));
270 if (guest == null) {
271 // Couldn't create guest, most likely because there already exists one, we just
272 // haven't reloaded the user list yet.
273 return;
274 }
275 id = guest.id;
Adrian Roosccdff622014-08-06 00:07:18 +0200276 } else if (record.isAddUser) {
Adrian Roos0c6763a2014-09-08 19:11:00 +0200277 showAddUserDialog();
278 return;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200279 } else {
280 id = record.info.id;
281 }
282
283 if (ActivityManager.getCurrentUser() == id) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200284 if (record.isGuest) {
Adrian Roos50052442014-07-17 23:35:18 +0200285 showExitGuestDialog(id);
Adrian Roose9c7d432014-07-17 18:27:38 +0200286 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200287 return;
288 }
289
Adrian Roose9c7d432014-07-17 18:27:38 +0200290 switchToUserId(id);
291 }
292
293 private void switchToUserId(int id) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200294 try {
Adrian Roos88b11932015-07-22 14:59:48 -0700295 pauseRefreshUsers();
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200296 ActivityManagerNative.getDefault().switchUser(id);
297 } catch (RemoteException e) {
298 Log.e(TAG, "Couldn't switch user.", e);
299 }
300 }
301
Adrian Roos50052442014-07-17 23:35:18 +0200302 private void showExitGuestDialog(int id) {
303 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
304 mExitGuestDialog.cancel();
305 }
306 mExitGuestDialog = new ExitGuestDialog(mContext, id);
307 mExitGuestDialog.show();
308 }
309
Adrian Roos0c6763a2014-09-08 19:11:00 +0200310 private void showAddUserDialog() {
311 if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
312 mAddUserDialog.cancel();
313 }
314 mAddUserDialog = new AddUserDialog(mContext);
315 mAddUserDialog.show();
316 }
317
Adrian Roose9c7d432014-07-17 18:27:38 +0200318 private void exitGuest(int id) {
Adrian Roos70441462014-07-22 16:03:12 +0200319 int newId = UserHandle.USER_OWNER;
320 if (mLastNonGuestUser != UserHandle.USER_OWNER) {
321 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
322 if (info != null && info.isEnabled() && info.supportsSwitchTo()) {
323 newId = info.id;
324 }
325 }
326 switchToUserId(newId);
Adrian Roose9c7d432014-07-17 18:27:38 +0200327 mUserManager.removeUser(id);
328 }
329
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200330 private BroadcastReceiver mReceiver = new BroadcastReceiver() {
331 @Override
332 public void onReceive(Context context, Intent intent) {
Adrian Roos50052442014-07-17 23:35:18 +0200333 if (DEBUG) {
334 Log.v(TAG, "Broadcast: a=" + intent.getAction()
335 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
336 }
Adrian Roos88b11932015-07-22 14:59:48 -0700337
338 boolean unpauseRefreshUsers = false;
339 int forcePictureLoadForId = UserHandle.USER_NULL;
340
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700341 if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
342 int currentUser = ActivityManager.getCurrentUser();
343 UserInfo userInfo = mUserManager.getUserInfo(currentUser);
344 if (userInfo != null && userInfo.isGuest()) {
345 showExitGuestDialog(currentUser);
346 }
347 return;
Adrian Roos88b11932015-07-22 14:59:48 -0700348 } else if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700349 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
350 UserInfo userInfo = mUserManager.getUserInfo(currentId);
351 if (userInfo != null && userInfo.isGuest()) {
352 showGuestNotification(currentId);
353 }
Adrian Roos88b11932015-07-22 14:59:48 -0700354 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
Adrian Roos50052442014-07-17 23:35:18 +0200355 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
356 mExitGuestDialog.cancel();
357 mExitGuestDialog = null;
358 }
359
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200360 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
361 final int N = mUsers.size();
362 for (int i = 0; i < N; i++) {
363 UserRecord record = mUsers.get(i);
Adrian Roose9c7d432014-07-17 18:27:38 +0200364 if (record.info == null) continue;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200365 boolean shouldBeCurrent = record.info.id == currentId;
366 if (record.isCurrent != shouldBeCurrent) {
367 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
368 }
Adrian Roos70441462014-07-22 16:03:12 +0200369 if (shouldBeCurrent && !record.isGuest) {
370 mLastNonGuestUser = record.info.id;
371 }
Adrian Roosccdff622014-08-06 00:07:18 +0200372 if (currentId != UserHandle.USER_OWNER && record.isRestricted) {
373 // Immediately remove restricted records in case the AsyncTask is too slow.
374 mUsers.remove(i);
375 i--;
376 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200377 }
378 notifyAdapters();
Adrian Roos88b11932015-07-22 14:59:48 -0700379 unpauseRefreshUsers = true;
380 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200381 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
382 UserHandle.USER_NULL);
383 }
384 refreshUsers(forcePictureLoadForId);
Adrian Roos88b11932015-07-22 14:59:48 -0700385 if (unpauseRefreshUsers) {
386 mUnpauseRefreshUsers.run();
387 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200388 }
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700389
390 private void showGuestNotification(int guestUserId) {
391 PendingIntent removeGuestPI = PendingIntent.getBroadcastAsUser(mContext,
392 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.OWNER);
393 Notification notification = new Notification.Builder(mContext)
394 .setVisibility(Notification.VISIBILITY_SECRET)
395 .setPriority(Notification.PRIORITY_MIN)
396 .setSmallIcon(R.drawable.ic_person)
397 .setContentTitle(mContext.getString(R.string.guest_notification_title))
398 .setContentText(mContext.getString(R.string.guest_notification_text))
399 .setShowWhen(false)
400 .addAction(R.drawable.ic_delete,
401 mContext.getString(R.string.guest_notification_remove_action),
402 removeGuestPI)
403 .build();
Amith Yamasanid81f8272015-07-23 17:15:45 -0700404 NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
405 notification, new UserHandle(guestUserId));
Fyodor Kupolovf4d6ad22015-04-13 11:52:18 -0700406 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200407 };
408
Adrian Roos88b11932015-07-22 14:59:48 -0700409 private final Runnable mUnpauseRefreshUsers = new Runnable() {
410 @Override
411 public void run() {
412 mHandler.removeCallbacks(this);
413 mPauseRefreshUsers = false;
414 refreshUsers(UserHandle.USER_NULL);
415 }
416 };
417
Adrian Roosccdff622014-08-06 00:07:18 +0200418 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
Jason Monk2daf62c2014-07-31 14:35:06 -0400419 public void onChange(boolean selfChange) {
420 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
421 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
Adrian Roosccdff622014-08-06 00:07:18 +0200422 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
423 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
Jason Monk2daf62c2014-07-31 14:35:06 -0400424 refreshUsers(UserHandle.USER_NULL);
425 };
426 };
427
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200428 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
429 pw.println("UserSwitcherController state:");
Adrian Roos70441462014-07-22 16:03:12 +0200430 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200431 pw.print(" mUsers.size="); pw.println(mUsers.size());
432 for (int i = 0; i < mUsers.size(); i++) {
433 final UserRecord u = mUsers.get(i);
434 pw.print(" "); pw.println(u.toString());
435 }
436 }
437
Adrian Roos57cf5702014-09-03 15:56:30 +0200438 public String getCurrentUserName(Context context) {
439 if (mUsers.isEmpty()) return null;
440 UserRecord item = mUsers.get(0);
441 if (item == null || item.info == null) return null;
442 if (item.isGuest) return context.getString(R.string.guest_nickname);
443 return item.info.name;
444 }
445
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200446 public static abstract class BaseUserAdapter extends BaseAdapter {
447
448 final UserSwitcherController mController;
449
450 protected BaseUserAdapter(UserSwitcherController controller) {
451 mController = controller;
452 controller.mAdapters.add(new WeakReference<>(this));
453 }
454
455 @Override
456 public int getCount() {
Adrian Roosccdff622014-08-06 00:07:18 +0200457 boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing()
Jason Monk8a3a9642015-06-05 11:01:30 -0400458 && mController.mKeyguardMonitor.isSecure()
Selim Cineke8bae622015-07-15 13:24:06 -0700459 && !mController.mKeyguardMonitor.canSkipBouncer();
Adrian Roosccdff622014-08-06 00:07:18 +0200460 if (!secureKeyguardShowing) {
461 return mController.mUsers.size();
462 }
463 // The lock screen is secure and showing. Filter out restricted records.
464 final int N = mController.mUsers.size();
465 int count = 0;
466 for (int i = 0; i < N; i++) {
467 if (mController.mUsers.get(i).isRestricted) {
468 break;
469 } else {
470 count++;
471 }
472 }
473 return count;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200474 }
475
476 @Override
477 public UserRecord getItem(int position) {
478 return mController.mUsers.get(position);
479 }
480
481 @Override
482 public long getItemId(int position) {
Adrian Roose9c7d432014-07-17 18:27:38 +0200483 return position;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200484 }
485
486 public void switchTo(UserRecord record) {
487 mController.switchTo(record);
488 }
Adrian Roose9c7d432014-07-17 18:27:38 +0200489
490 public String getName(Context context, UserRecord item) {
491 if (item.isGuest) {
492 if (item.isCurrent) {
493 return context.getString(R.string.guest_exit_guest);
494 } else {
495 return context.getString(
496 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
497 }
Adrian Roosccdff622014-08-06 00:07:18 +0200498 } else if (item.isAddUser) {
499 return context.getString(R.string.user_add_user);
Adrian Roose9c7d432014-07-17 18:27:38 +0200500 } else {
501 return item.info.name;
502 }
503 }
Adrian Roos723632e2014-07-23 21:13:21 +0200504
Adrian Roosccdff622014-08-06 00:07:18 +0200505 public Drawable getDrawable(Context context, UserRecord item) {
506 if (item.isAddUser) {
507 return context.getDrawable(R.drawable.ic_add_circle_qs);
508 }
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +0100509 return UserIcons.getDefaultUserIcon(item.isGuest ? UserHandle.USER_NULL : item.info.id,
510 /* light= */ true);
Adrian Roosccdff622014-08-06 00:07:18 +0200511 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100512
513 public void refresh() {
514 mController.refreshUsers(UserHandle.USER_NULL);
515 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200516 }
517
518 public static final class UserRecord {
519 public final UserInfo info;
520 public final Bitmap picture;
521 public final boolean isGuest;
522 public final boolean isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200523 public final boolean isAddUser;
524 /** If true, the record is only visible to the owner and only when unlocked. */
525 public final boolean isRestricted;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200526
Adrian Roosccdff622014-08-06 00:07:18 +0200527 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
528 boolean isAddUser, boolean isRestricted) {
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200529 this.info = info;
530 this.picture = picture;
531 this.isGuest = isGuest;
532 this.isCurrent = isCurrent;
Adrian Roosccdff622014-08-06 00:07:18 +0200533 this.isAddUser = isAddUser;
534 this.isRestricted = isRestricted;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200535 }
536
537 public UserRecord copyWithIsCurrent(boolean _isCurrent) {
Adrian Roosccdff622014-08-06 00:07:18 +0200538 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200539 }
540
541 public String toString() {
542 StringBuilder sb = new StringBuilder();
543 sb.append("UserRecord(");
544 if (info != null) {
545 sb.append("name=\"" + info.name + "\" id=" + info.id);
546 } else {
Adrian Roosccdff622014-08-06 00:07:18 +0200547 if (isGuest) {
548 sb.append("<add guest placeholder>");
549 } else if (isAddUser) {
550 sb.append("<add user placeholder>");
551 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200552 }
Adrian Roosccdff622014-08-06 00:07:18 +0200553 if (isGuest) sb.append(" <isGuest>");
554 if (isAddUser) sb.append(" <isAddUser>");
555 if (isCurrent) sb.append(" <isCurrent>");
556 if (picture != null) sb.append(" <hasPicture>");
557 if (isRestricted) sb.append(" <isRestricted>");
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200558 sb.append(')');
559 return sb.toString();
560 }
561 }
562
563 public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() {
564 private final Intent USER_SETTINGS_INTENT = new Intent("android.settings.USER_SETTINGS");
565
566 @Override
567 public int getTitle() {
568 return R.string.quick_settings_user_title;
569 }
570
571 @Override
572 public View createDetailView(Context context, View convertView, ViewGroup parent) {
Adrian Roos19408922014-08-07 20:54:12 +0200573 UserDetailView v;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200574 if (!(convertView instanceof UserDetailView)) {
Adrian Roos19408922014-08-07 20:54:12 +0200575 v = UserDetailView.inflate(context, parent, false);
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200576 v.createAndSetAdapter(UserSwitcherController.this);
Adrian Roos19408922014-08-07 20:54:12 +0200577 } else {
578 v = (UserDetailView) convertView;
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200579 }
Adrian Roos844c92b2014-12-01 14:19:05 +0100580 v.refreshAdapter();
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200581 return v;
582 }
583
584 @Override
585 public Intent getSettingsIntent() {
586 return USER_SETTINGS_INTENT;
587 }
588
589 @Override
590 public Boolean getToggleState() {
591 return null;
592 }
593
594 @Override
595 public void setToggleState(boolean state) {
596 }
Chris Wren457a21c2015-05-06 17:50:34 -0400597
598 @Override
599 public int getMetricsCategory() {
600 return MetricsLogger.QS_USERDETAIL;
601 }
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200602 };
Adrian Roos50052442014-07-17 23:35:18 +0200603
Adrian Roosccdff622014-08-06 00:07:18 +0200604 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
605 @Override
606 public void onKeyguardChanged() {
607 notifyAdapters();
608 }
609 };
610
Adrian Roos50052442014-07-17 23:35:18 +0200611 private final class ExitGuestDialog extends SystemUIDialog implements
612 DialogInterface.OnClickListener {
613
614 private final int mGuestId;
615
616 public ExitGuestDialog(Context context, int guestId) {
617 super(context);
618 setTitle(R.string.guest_exit_guest_dialog_title);
619 setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
620 setButton(DialogInterface.BUTTON_NEGATIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700621 context.getString(android.R.string.cancel), this);
Adrian Roos50052442014-07-17 23:35:18 +0200622 setButton(DialogInterface.BUTTON_POSITIVE,
Amith Yamasanie5b274a2014-08-18 07:57:56 -0700623 context.getString(R.string.guest_exit_guest_dialog_remove), this);
Adrian Roos50052442014-07-17 23:35:18 +0200624 setCanceledOnTouchOutside(false);
625 mGuestId = guestId;
626 }
627
628 @Override
629 public void onClick(DialogInterface dialog, int which) {
630 if (which == BUTTON_NEGATIVE) {
631 cancel();
632 } else {
633 dismiss();
634 exitGuest(mGuestId);
635 }
636 }
637 }
Adrian Roos0c6763a2014-09-08 19:11:00 +0200638
639 private final class AddUserDialog extends SystemUIDialog implements
640 DialogInterface.OnClickListener {
641
642 public AddUserDialog(Context context) {
643 super(context);
644 setTitle(R.string.user_add_user_title);
645 setMessage(context.getString(R.string.user_add_user_message_short));
646 setButton(DialogInterface.BUTTON_NEGATIVE,
647 context.getString(android.R.string.cancel), this);
648 setButton(DialogInterface.BUTTON_POSITIVE,
649 context.getString(android.R.string.ok), this);
650 }
651
652 @Override
653 public void onClick(DialogInterface dialog, int which) {
654 if (which == BUTTON_NEGATIVE) {
655 cancel();
656 } else {
657 dismiss();
Guang Zhuccbeb612014-10-21 14:33:50 -0700658 if (ActivityManager.isUserAMonkey()) {
659 return;
660 }
Amith Yamasaniaa6634e2014-10-06 14:20:28 -0700661 UserInfo user = mUserManager.createSecondaryUser(
Adrian Roosf99727c2014-09-17 16:15:07 +0200662 mContext.getString(R.string.user_new_user_name), 0 /* flags */);
663 if (user == null) {
664 // Couldn't create user, most likely because there are too many, but we haven't
665 // been able to reload the list yet.
666 return;
667 }
668 int id = user.id;
Alexandra Gherghina64d4dca2014-08-28 18:26:56 +0100669 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
670 id, /* light= */ false));
671 mUserManager.setUserIcon(id, icon);
Adrian Roos0c6763a2014-09-08 19:11:00 +0200672 switchToUserId(id);
673 }
674 }
675 }
Adrian Roos2b154a92014-11-17 15:18:39 +0100676
677 public static boolean isUserSwitcherAvailable(UserManager um) {
678 return UserManager.supportsMultipleUsers() && um.isUserSwitcherEnabled();
679 }
680
Adrian Roos00a0b1f2014-07-16 16:44:49 +0200681}