1 /* 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 17 package com.android.systemui.statusbar.policy; 18 19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 20 21 import android.R.attr; 22 import android.app.ActivityManager; 23 import android.app.Dialog; 24 import android.app.Notification; 25 import android.app.NotificationManager; 26 import android.app.PendingIntent; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.UserInfo; 33 import android.database.ContentObserver; 34 import android.graphics.Bitmap; 35 import android.graphics.PorterDuff.Mode; 36 import android.graphics.drawable.Drawable; 37 import android.os.AsyncTask; 38 import android.os.Handler; 39 import android.os.RemoteException; 40 import android.os.UserHandle; 41 import android.os.UserManager; 42 import android.provider.Settings; 43 import android.telephony.PhoneStateListener; 44 import android.telephony.TelephonyManager; 45 import android.util.Log; 46 import android.util.SparseArray; 47 import android.util.SparseBooleanArray; 48 import android.view.View; 49 import android.view.ViewGroup; 50 import android.widget.BaseAdapter; 51 52 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 53 import com.android.internal.annotations.VisibleForTesting; 54 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 55 import com.android.internal.util.UserIcons; 56 import com.android.settingslib.RestrictedLockUtils; 57 import com.android.settingslib.Utils; 58 import com.android.systemui.Dependency; 59 import com.android.systemui.GuestResumeSessionReceiver; 60 import com.android.systemui.Prefs; 61 import com.android.systemui.Prefs.Key; 62 import com.android.systemui.R; 63 import com.android.systemui.SystemUI; 64 import com.android.systemui.SystemUISecondaryUserService; 65 import com.android.systemui.plugins.qs.DetailAdapter; 66 import com.android.systemui.qs.tiles.UserDetailView; 67 import com.android.systemui.plugins.ActivityStarter; 68 import com.android.systemui.statusbar.phone.SystemUIDialog; 69 import com.android.systemui.util.NotificationChannels; 70 71 import java.io.FileDescriptor; 72 import java.io.PrintWriter; 73 import java.lang.ref.WeakReference; 74 import java.util.ArrayList; 75 import java.util.List; 76 77 /** 78 * Keeps a list of all users on the device for user switching. 79 */ 80 public class UserSwitcherController { 81 82 private static final String TAG = "UserSwitcherController"; 83 private static final boolean DEBUG = false; 84 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING = 85 "lockscreenSimpleUserSwitcher"; 86 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000; 87 88 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 89 90 protected final Context mContext; 91 protected final UserManager mUserManager; 92 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); 93 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver 94 = new GuestResumeSessionReceiver(); 95 private final KeyguardMonitor mKeyguardMonitor; 96 protected final Handler mHandler; 97 private final ActivityStarter mActivityStarter; 98 99 private ArrayList<UserRecord> mUsers = new ArrayList<>(); 100 private Dialog mExitGuestDialog; 101 private Dialog mAddUserDialog; 102 private int mLastNonGuestUser = UserHandle.USER_SYSTEM; 103 private boolean mResumeUserOnGuestLogout = true; 104 private boolean mSimpleUserSwitcher; 105 private boolean mAddUsersWhenLocked; 106 private boolean mPauseRefreshUsers; 107 private int mSecondaryUser = UserHandle.USER_NULL; 108 private Intent mSecondaryUserServiceIntent; 109 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); 110 UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, Handler handler, ActivityStarter activityStarter)111 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, 112 Handler handler, ActivityStarter activityStarter) { 113 mContext = context; 114 mGuestResumeSessionReceiver.register(context); 115 mKeyguardMonitor = keyguardMonitor; 116 mHandler = handler; 117 mActivityStarter = activityStarter; 118 mUserManager = UserManager.get(context); 119 IntentFilter filter = new IntentFilter(); 120 filter.addAction(Intent.ACTION_USER_ADDED); 121 filter.addAction(Intent.ACTION_USER_REMOVED); 122 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 123 filter.addAction(Intent.ACTION_USER_SWITCHED); 124 filter.addAction(Intent.ACTION_USER_STOPPED); 125 filter.addAction(Intent.ACTION_USER_UNLOCKED); 126 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, 127 null /* permission */, null /* scheduler */); 128 129 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class); 130 131 filter = new IntentFilter(); 132 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, 133 PERMISSION_SELF, null /* scheduler */); 134 135 mContext.getContentResolver().registerContentObserver( 136 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true, 137 mSettingsObserver); 138 mContext.getContentResolver().registerContentObserver( 139 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true, 140 mSettingsObserver); 141 mContext.getContentResolver().registerContentObserver( 142 Settings.Global.getUriFor( 143 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED), 144 true, mSettingsObserver); 145 // Fetch initial values. 146 mSettingsObserver.onChange(false); 147 148 keyguardMonitor.addCallback(mCallback); 149 listenForCallState(); 150 151 refreshUsers(UserHandle.USER_NULL); 152 } 153 154 /** 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 */ 161 @SuppressWarnings("unchecked") refreshUsers(int forcePictureLoadForId)162 private void refreshUsers(int forcePictureLoadForId) { 163 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 } 171 172 boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL); 173 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); 177 if (r == null || r.picture == null || r.info == null || forceAllUsers 178 || mForcePictureLoadForUserId.get(r.info.id)) { 179 continue; 180 } 181 bitmaps.put(r.info.id, r.picture); 182 } 183 mForcePictureLoadForUserId.clear(); 184 185 final boolean addUsersWhenLocked = mAddUsersWhenLocked; 186 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() { 187 @SuppressWarnings("unchecked") 188 @Override 189 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) { 190 final SparseArray<Bitmap> bitmaps = params[0]; 191 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(); 197 boolean canSwitchUsers = mUserManager.canSwitchUsers(); 198 UserInfo currentUserInfo = null; 199 UserRecord guestRecord = null; 200 201 for (UserInfo info : infos) { 202 boolean isCurrent = currentId == info.id; 203 if (isCurrent) { 204 currentUserInfo = info; 205 } 206 boolean switchToEnabled = canSwitchUsers || isCurrent; 207 if (info.isEnabled()) { 208 if (info.isGuest()) { 209 // Tapping guest icon triggers remove and a user switch therefore 210 // the icon shouldn't be enabled even if the user is current 211 guestRecord = new UserRecord(info, null /* picture */, 212 true /* isGuest */, isCurrent, false /* isAddUser */, 213 false /* isRestricted */, canSwitchUsers); 214 } else if (info.supportsSwitchToByUser()) { 215 Bitmap picture = bitmaps.get(info.id); 216 if (picture == null) { 217 picture = mUserManager.getUserIcon(info.id); 218 219 if (picture != null) { 220 int avatarSize = mContext.getResources() 221 .getDimensionPixelSize(R.dimen.max_avatar_size); 222 picture = Bitmap.createScaledBitmap( 223 picture, avatarSize, avatarSize, true); 224 } 225 } 226 int index = isCurrent ? 0 : records.size(); 227 records.add(index, new UserRecord(info, picture, false /* isGuest */, 228 isCurrent, false /* isAddUser */, false /* isRestricted */, 229 switchToEnabled)); 230 } 231 } 232 } 233 if (records.size() > 1 || guestRecord != null) { 234 Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true); 235 } 236 237 boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction( 238 UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); 239 boolean currentUserCanCreateUsers = currentUserInfo != null 240 && (currentUserInfo.isAdmin() 241 || currentUserInfo.id == UserHandle.USER_SYSTEM) 242 && systemCanCreateUsers; 243 boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked; 244 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers) 245 && guestRecord == null; 246 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers) 247 && mUserManager.canAddMoreUsers(); 248 boolean createIsRestricted = !addUsersWhenLocked; 249 250 if (!mSimpleUserSwitcher) { 251 if (guestRecord == null) { 252 if (canCreateGuest) { 253 guestRecord = new UserRecord(null /* info */, null /* picture */, 254 true /* isGuest */, false /* isCurrent */, 255 false /* isAddUser */, createIsRestricted, canSwitchUsers); 256 checkIfAddUserDisallowedByAdminOnly(guestRecord); 257 records.add(guestRecord); 258 } 259 } else { 260 int index = guestRecord.isCurrent ? 0 : records.size(); 261 records.add(index, guestRecord); 262 } 263 } 264 265 if (!mSimpleUserSwitcher && canCreateUser) { 266 UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, 267 false /* isGuest */, false /* isCurrent */, true /* isAddUser */, 268 createIsRestricted, canSwitchUsers); 269 checkIfAddUserDisallowedByAdminOnly(addUserRecord); 270 records.add(addUserRecord); 271 } 272 273 return records; 274 } 275 276 @Override 277 protected void onPostExecute(ArrayList<UserRecord> userRecords) { 278 if (userRecords != null) { 279 mUsers = userRecords; 280 notifyAdapters(); 281 } 282 } 283 }.execute((SparseArray) bitmaps); 284 } 285 pauseRefreshUsers()286 private void pauseRefreshUsers() { 287 if (!mPauseRefreshUsers) { 288 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); 289 mPauseRefreshUsers = true; 290 } 291 } 292 notifyAdapters()293 private void notifyAdapters() { 294 for (int i = mAdapters.size() - 1; i >= 0; i--) { 295 BaseUserAdapter adapter = mAdapters.get(i).get(); 296 if (adapter != null) { 297 adapter.notifyDataSetChanged(); 298 } else { 299 mAdapters.remove(i); 300 } 301 } 302 } 303 isSimpleUserSwitcher()304 public boolean isSimpleUserSwitcher() { 305 return mSimpleUserSwitcher; 306 } 307 useFullscreenUserSwitcher()308 public boolean useFullscreenUserSwitcher() { 309 // Use adb to override: 310 // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off. 311 // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on. 312 // Restart SystemUI or adb reboot. 313 final int DEFAULT = -1; 314 final int overrideUseFullscreenUserSwitcher = 315 Settings.System.getInt(mContext.getContentResolver(), 316 "enable_fullscreen_user_switcher", DEFAULT); 317 if (overrideUseFullscreenUserSwitcher != DEFAULT) { 318 return overrideUseFullscreenUserSwitcher != 0; 319 } 320 // Otherwise default to the build setting. 321 return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher); 322 } 323 setResumeUserOnGuestLogout(boolean resume)324 public void setResumeUserOnGuestLogout(boolean resume) { 325 mResumeUserOnGuestLogout = resume; 326 } 327 logoutCurrentUser()328 public void logoutCurrentUser() { 329 int currentUser = ActivityManager.getCurrentUser(); 330 if (currentUser != UserHandle.USER_SYSTEM) { 331 pauseRefreshUsers(); 332 ActivityManager.logoutCurrentUser(); 333 } 334 } 335 removeUserId(int userId)336 public void removeUserId(int userId) { 337 if (userId == UserHandle.USER_SYSTEM) { 338 Log.w(TAG, "User " + userId + " could not removed."); 339 return; 340 } 341 if (ActivityManager.getCurrentUser() == userId) { 342 switchToUserId(UserHandle.USER_SYSTEM); 343 } 344 if (mUserManager.removeUser(userId)) { 345 refreshUsers(UserHandle.USER_NULL); 346 } 347 } 348 switchTo(UserRecord record)349 public void switchTo(UserRecord record) { 350 int id; 351 if (record.isGuest && record.info == null) { 352 // No guest user. Create one. 353 UserInfo guest = mUserManager.createGuest( 354 mContext, mContext.getString(R.string.guest_nickname)); 355 if (guest == null) { 356 // Couldn't create guest, most likely because there already exists one, we just 357 // haven't reloaded the user list yet. 358 return; 359 } 360 id = guest.id; 361 } else if (record.isAddUser) { 362 showAddUserDialog(); 363 return; 364 } else { 365 id = record.info.id; 366 } 367 368 int currUserId = ActivityManager.getCurrentUser(); 369 if (currUserId == id) { 370 if (record.isGuest) { 371 showExitGuestDialog(id); 372 } 373 return; 374 } 375 376 if (UserManager.isGuestUserEphemeral()) { 377 // If switching from guest, we want to bring up the guest exit dialog instead of switching 378 UserInfo currUserInfo = mUserManager.getUserInfo(currUserId); 379 if (currUserInfo != null && currUserInfo.isGuest()) { 380 showExitGuestDialog(currUserId, record.resolveId()); 381 return; 382 } 383 } 384 385 switchToUserId(id); 386 } 387 switchTo(int userId)388 public void switchTo(int userId) { 389 final int count = mUsers.size(); 390 for (int i = 0; i < count; ++i) { 391 UserRecord record = mUsers.get(i); 392 if (record.info != null && record.info.id == userId) { 393 switchTo(record); 394 return; 395 } 396 } 397 398 Log.e(TAG, "Couldn't switch to user, id=" + userId); 399 } 400 getSwitchableUserCount()401 public int getSwitchableUserCount() { 402 int count = 0; 403 final int N = mUsers.size(); 404 for (int i = 0; i < N; ++i) { 405 UserRecord record = mUsers.get(i); 406 if (record.info != null && record.info.supportsSwitchTo()) { 407 count++; 408 } 409 } 410 return count; 411 } 412 switchToUserId(int id)413 protected void switchToUserId(int id) { 414 try { 415 pauseRefreshUsers(); 416 ActivityManager.getService().switchUser(id); 417 } catch (RemoteException e) { 418 Log.e(TAG, "Couldn't switch user.", e); 419 } 420 } 421 showExitGuestDialog(int id)422 private void showExitGuestDialog(int id) { 423 int newId = UserHandle.USER_SYSTEM; 424 if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) { 425 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); 426 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { 427 newId = info.id; 428 } 429 } 430 showExitGuestDialog(id, newId); 431 } 432 showExitGuestDialog(int id, int targetId)433 protected void showExitGuestDialog(int id, int targetId) { 434 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 435 mExitGuestDialog.cancel(); 436 } 437 mExitGuestDialog = new ExitGuestDialog(mContext, id, targetId); 438 mExitGuestDialog.show(); 439 } 440 showAddUserDialog()441 public void showAddUserDialog() { 442 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 443 mAddUserDialog.cancel(); 444 } 445 mAddUserDialog = new AddUserDialog(mContext); 446 mAddUserDialog.show(); 447 } 448 exitGuest(int id, int targetId)449 protected void exitGuest(int id, int targetId) { 450 switchToUserId(targetId); 451 mUserManager.removeUser(id); 452 } 453 listenForCallState()454 private void listenForCallState() { 455 TelephonyManager.from(mContext).listen(mPhoneStateListener, 456 PhoneStateListener.LISTEN_CALL_STATE); 457 } 458 459 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 460 private int mCallState; 461 462 @Override 463 public void onCallStateChanged(int state, String incomingNumber) { 464 if (mCallState == state) return; 465 if (DEBUG) Log.v(TAG, "Call state changed: " + state); 466 mCallState = state; 467 refreshUsers(UserHandle.USER_NULL); 468 } 469 }; 470 471 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 472 @Override 473 public void onReceive(Context context, Intent intent) { 474 if (DEBUG) { 475 Log.v(TAG, "Broadcast: a=" + intent.getAction() 476 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); 477 } 478 479 boolean unpauseRefreshUsers = false; 480 int forcePictureLoadForId = UserHandle.USER_NULL; 481 482 if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 483 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 484 mExitGuestDialog.cancel(); 485 mExitGuestDialog = null; 486 } 487 488 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 489 final UserInfo userInfo = mUserManager.getUserInfo(currentId); 490 final int N = mUsers.size(); 491 for (int i = 0; i < N; i++) { 492 UserRecord record = mUsers.get(i); 493 if (record.info == null) continue; 494 boolean shouldBeCurrent = record.info.id == currentId; 495 if (record.isCurrent != shouldBeCurrent) { 496 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); 497 } 498 if (shouldBeCurrent && !record.isGuest) { 499 mLastNonGuestUser = record.info.id; 500 } 501 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) { 502 // Immediately remove restricted records in case the AsyncTask is too slow. 503 mUsers.remove(i); 504 i--; 505 } 506 } 507 notifyAdapters(); 508 509 // Disconnect from the old secondary user's service 510 if (mSecondaryUser != UserHandle.USER_NULL) { 511 context.stopServiceAsUser(mSecondaryUserServiceIntent, 512 UserHandle.of(mSecondaryUser)); 513 mSecondaryUser = UserHandle.USER_NULL; 514 } 515 // Connect to the new secondary user's service (purely to ensure that a persistent 516 // SystemUI application is created for that user) 517 if (userInfo != null && userInfo.id != UserHandle.USER_SYSTEM) { 518 context.startServiceAsUser(mSecondaryUserServiceIntent, 519 UserHandle.of(userInfo.id)); 520 mSecondaryUser = userInfo.id; 521 } 522 unpauseRefreshUsers = true; 523 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { 524 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 525 UserHandle.USER_NULL); 526 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 527 // Unlocking the system user may require a refresh 528 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 529 if (userId != UserHandle.USER_SYSTEM) { 530 return; 531 } 532 } 533 refreshUsers(forcePictureLoadForId); 534 if (unpauseRefreshUsers) { 535 mUnpauseRefreshUsers.run(); 536 } 537 } 538 }; 539 540 private final Runnable mUnpauseRefreshUsers = new Runnable() { 541 @Override 542 public void run() { 543 mHandler.removeCallbacks(this); 544 mPauseRefreshUsers = false; 545 refreshUsers(UserHandle.USER_NULL); 546 } 547 }; 548 549 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 550 public void onChange(boolean selfChange) { 551 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), 552 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; 553 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), 554 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; 555 refreshUsers(UserHandle.USER_NULL); 556 }; 557 }; 558 dump(FileDescriptor fd, PrintWriter pw, String[] args)559 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 560 pw.println("UserSwitcherController state:"); 561 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser); 562 pw.print(" mUsers.size="); pw.println(mUsers.size()); 563 for (int i = 0; i < mUsers.size(); i++) { 564 final UserRecord u = mUsers.get(i); 565 pw.print(" "); pw.println(u.toString()); 566 } 567 } 568 getCurrentUserName(Context context)569 public String getCurrentUserName(Context context) { 570 if (mUsers.isEmpty()) return null; 571 UserRecord item = mUsers.get(0); 572 if (item == null || item.info == null) return null; 573 if (item.isGuest) return context.getString(R.string.guest_nickname); 574 return item.info.name; 575 } 576 onDensityOrFontScaleChanged()577 public void onDensityOrFontScaleChanged() { 578 refreshUsers(UserHandle.USER_ALL); 579 } 580 581 @VisibleForTesting addAdapter(WeakReference<BaseUserAdapter> adapter)582 public void addAdapter(WeakReference<BaseUserAdapter> adapter) { 583 mAdapters.add(adapter); 584 } 585 586 @VisibleForTesting getUsers()587 public ArrayList<UserRecord> getUsers() { 588 return mUsers; 589 } 590 591 public static abstract class BaseUserAdapter extends BaseAdapter { 592 593 final UserSwitcherController mController; 594 private final KeyguardMonitor mKeyguardMonitor; 595 BaseUserAdapter(UserSwitcherController controller)596 protected BaseUserAdapter(UserSwitcherController controller) { 597 mController = controller; 598 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); 599 controller.addAdapter(new WeakReference<>(this)); 600 } 601 getUserCount()602 public int getUserCount() { 603 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() 604 && mKeyguardMonitor.isSecure() 605 && !mKeyguardMonitor.canSkipBouncer(); 606 if (!secureKeyguardShowing) { 607 return mController.getUsers().size(); 608 } 609 // The lock screen is secure and showing. Filter out restricted records. 610 final int N = mController.getUsers().size(); 611 int count = 0; 612 for (int i = 0; i < N; i++) { 613 if (mController.getUsers().get(i).isGuest) continue; 614 if (mController.getUsers().get(i).isRestricted) { 615 break; 616 } else { 617 count++; 618 } 619 } 620 return count; 621 } 622 623 @Override getCount()624 public int getCount() { 625 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() 626 && mKeyguardMonitor.isSecure() 627 && !mKeyguardMonitor.canSkipBouncer(); 628 if (!secureKeyguardShowing) { 629 return mController.getUsers().size(); 630 } 631 // The lock screen is secure and showing. Filter out restricted records. 632 final int N = mController.getUsers().size(); 633 int count = 0; 634 for (int i = 0; i < N; i++) { 635 if (mController.getUsers().get(i).isRestricted) { 636 break; 637 } else { 638 count++; 639 } 640 } 641 return count; 642 } 643 644 @Override getItem(int position)645 public UserRecord getItem(int position) { 646 return mController.getUsers().get(position); 647 } 648 649 @Override getItemId(int position)650 public long getItemId(int position) { 651 return position; 652 } 653 switchTo(UserRecord record)654 public void switchTo(UserRecord record) { 655 mController.switchTo(record); 656 } 657 getName(Context context, UserRecord item)658 public String getName(Context context, UserRecord item) { 659 if (item.isGuest) { 660 if (item.isCurrent) { 661 return context.getString(R.string.guest_exit_guest); 662 } else { 663 return context.getString( 664 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); 665 } 666 } else if (item.isAddUser) { 667 return context.getString(R.string.user_add_user); 668 } else { 669 return item.info.name; 670 } 671 } 672 getDrawable(Context context, UserRecord item)673 public Drawable getDrawable(Context context, UserRecord item) { 674 if (item.isAddUser) { 675 return context.getDrawable(R.drawable.ic_add_circle_qs); 676 } 677 Drawable icon = UserIcons.getDefaultUserIcon( 678 context.getResources(), item.resolveId(), /* light= */ false); 679 if (item.isGuest) { 680 icon.setColorFilter(Utils.getColorAttr(context, android.R.attr.colorForeground), 681 Mode.SRC_IN); 682 } 683 return icon; 684 } 685 refresh()686 public void refresh() { 687 mController.refreshUsers(UserHandle.USER_NULL); 688 } 689 } 690 checkIfAddUserDisallowedByAdminOnly(UserRecord record)691 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { 692 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, 693 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser()); 694 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext, 695 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) { 696 record.isDisabledByAdmin = true; 697 record.enforcedAdmin = admin; 698 } else { 699 record.isDisabledByAdmin = false; 700 record.enforcedAdmin = null; 701 } 702 } 703 startActivity(Intent intent)704 public void startActivity(Intent intent) { 705 mActivityStarter.startActivity(intent, true); 706 } 707 708 public static final class UserRecord { 709 public final UserInfo info; 710 public final Bitmap picture; 711 public final boolean isGuest; 712 public final boolean isCurrent; 713 public final boolean isAddUser; 714 /** If true, the record is only visible to the owner and only when unlocked. */ 715 public final boolean isRestricted; 716 public boolean isDisabledByAdmin; 717 public EnforcedAdmin enforcedAdmin; 718 public boolean isSwitchToEnabled; 719 UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled)720 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, 721 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) { 722 this.info = info; 723 this.picture = picture; 724 this.isGuest = isGuest; 725 this.isCurrent = isCurrent; 726 this.isAddUser = isAddUser; 727 this.isRestricted = isRestricted; 728 this.isSwitchToEnabled = isSwitchToEnabled; 729 } 730 copyWithIsCurrent(boolean _isCurrent)731 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 732 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 733 isSwitchToEnabled); 734 } 735 resolveId()736 public int resolveId() { 737 if (isGuest || info == null) { 738 return UserHandle.USER_NULL; 739 } 740 return info.id; 741 } 742 toString()743 public String toString() { 744 StringBuilder sb = new StringBuilder(); 745 sb.append("UserRecord("); 746 if (info != null) { 747 sb.append("name=\"").append(info.name).append("\" id=").append(info.id); 748 } else { 749 if (isGuest) { 750 sb.append("<add guest placeholder>"); 751 } else if (isAddUser) { 752 sb.append("<add user placeholder>"); 753 } 754 } 755 if (isGuest) sb.append(" <isGuest>"); 756 if (isAddUser) sb.append(" <isAddUser>"); 757 if (isCurrent) sb.append(" <isCurrent>"); 758 if (picture != null) sb.append(" <hasPicture>"); 759 if (isRestricted) sb.append(" <isRestricted>"); 760 if (isDisabledByAdmin) { 761 sb.append(" <isDisabledByAdmin>"); 762 sb.append(" enforcedAdmin=").append(enforcedAdmin); 763 } 764 if (isSwitchToEnabled) { 765 sb.append(" <isSwitchToEnabled>"); 766 } 767 sb.append(')'); 768 return sb.toString(); 769 } 770 } 771 772 public final DetailAdapter userDetailAdapter = new DetailAdapter() { 773 private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS); 774 775 @Override 776 public CharSequence getTitle() { 777 return mContext.getString(R.string.quick_settings_user_title); 778 } 779 780 @Override 781 public View createDetailView(Context context, View convertView, ViewGroup parent) { 782 UserDetailView v; 783 if (!(convertView instanceof UserDetailView)) { 784 v = UserDetailView.inflate(context, parent, false); 785 v.createAndSetAdapter(UserSwitcherController.this); 786 } else { 787 v = (UserDetailView) convertView; 788 } 789 v.refreshAdapter(); 790 return v; 791 } 792 793 @Override 794 public Intent getSettingsIntent() { 795 return USER_SETTINGS_INTENT; 796 } 797 798 @Override 799 public Boolean getToggleState() { 800 return null; 801 } 802 803 @Override 804 public void setToggleState(boolean state) { 805 } 806 807 @Override 808 public int getMetricsCategory() { 809 return MetricsEvent.QS_USERDETAIL; 810 } 811 }; 812 813 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() { 814 @Override 815 public void onKeyguardShowingChanged() { 816 817 // When Keyguard is going away, we don't need to update our items immediately which 818 // helps making the transition faster. 819 if (!mKeyguardMonitor.isShowing()) { 820 mHandler.post(UserSwitcherController.this::notifyAdapters); 821 } else { 822 notifyAdapters(); 823 } 824 } 825 }; 826 827 private final class ExitGuestDialog extends SystemUIDialog implements 828 DialogInterface.OnClickListener { 829 830 private final int mGuestId; 831 private final int mTargetId; 832 ExitGuestDialog(Context context, int guestId, int targetId)833 public ExitGuestDialog(Context context, int guestId, int targetId) { 834 super(context); 835 setTitle(R.string.guest_exit_guest_dialog_title); 836 setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); 837 setButton(DialogInterface.BUTTON_NEGATIVE, 838 context.getString(android.R.string.cancel), this); 839 setButton(DialogInterface.BUTTON_POSITIVE, 840 context.getString(R.string.guest_exit_guest_dialog_remove), this); 841 SystemUIDialog.setWindowOnTop(this); 842 setCanceledOnTouchOutside(false); 843 mGuestId = guestId; 844 mTargetId = targetId; 845 } 846 847 @Override onClick(DialogInterface dialog, int which)848 public void onClick(DialogInterface dialog, int which) { 849 if (which == BUTTON_NEGATIVE) { 850 cancel(); 851 } else { 852 dismiss(); 853 exitGuest(mGuestId, mTargetId); 854 } 855 } 856 } 857 858 private final class AddUserDialog extends SystemUIDialog implements 859 DialogInterface.OnClickListener { 860 AddUserDialog(Context context)861 public AddUserDialog(Context context) { 862 super(context); 863 setTitle(R.string.user_add_user_title); 864 setMessage(context.getString(R.string.user_add_user_message_short)); 865 setButton(DialogInterface.BUTTON_NEGATIVE, 866 context.getString(android.R.string.cancel), this); 867 setButton(DialogInterface.BUTTON_POSITIVE, 868 context.getString(android.R.string.ok), this); 869 SystemUIDialog.setWindowOnTop(this); 870 } 871 872 @Override onClick(DialogInterface dialog, int which)873 public void onClick(DialogInterface dialog, int which) { 874 if (which == BUTTON_NEGATIVE) { 875 cancel(); 876 } else { 877 dismiss(); 878 if (ActivityManager.isUserAMonkey()) { 879 return; 880 } 881 UserInfo user = mUserManager.createUser( 882 mContext.getString(R.string.user_new_user_name), 0 /* flags */); 883 if (user == null) { 884 // Couldn't create user, most likely because there are too many, but we haven't 885 // been able to reload the list yet. 886 return; 887 } 888 int id = user.id; 889 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon( 890 mContext.getResources(), id, /* light= */ false)); 891 mUserManager.setUserIcon(id, icon); 892 switchToUserId(id); 893 } 894 } 895 } 896 } 897