1 /* 2 * Copyright (C) 2012 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.settings.users; 18 19 import android.annotation.NonNull; 20 import android.app.Activity; 21 import android.app.ActivityManager; 22 import android.app.Dialog; 23 import android.app.admin.DevicePolicyManager; 24 import android.app.settings.SettingsEnums; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.SharedPreferences; 31 import android.content.pm.UserInfo; 32 import android.content.res.Resources; 33 import android.graphics.Bitmap; 34 import android.graphics.BitmapFactory; 35 import android.graphics.drawable.Drawable; 36 import android.net.Uri; 37 import android.os.AsyncTask; 38 import android.os.Bundle; 39 import android.os.Handler; 40 import android.os.Message; 41 import android.os.Process; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.provider.ContactsContract; 46 import android.text.TextUtils; 47 import android.util.Log; 48 import android.util.SparseArray; 49 import android.view.Menu; 50 import android.view.MenuInflater; 51 import android.view.MenuItem; 52 import android.widget.SimpleAdapter; 53 import android.widget.Toast; 54 55 import androidx.annotation.VisibleForTesting; 56 import androidx.annotation.WorkerThread; 57 import androidx.appcompat.app.AlertDialog; 58 import androidx.preference.Preference; 59 import androidx.preference.PreferenceGroup; 60 import androidx.preference.PreferenceScreen; 61 62 import com.android.internal.util.UserIcons; 63 import com.android.internal.widget.LockPatternUtils; 64 import com.android.settings.R; 65 import com.android.settings.SettingsActivity; 66 import com.android.settings.SettingsPreferenceFragment; 67 import com.android.settings.Utils; 68 import com.android.settings.core.SubSettingLauncher; 69 import com.android.settings.password.ChooseLockGeneric; 70 import com.android.settings.search.BaseSearchIndexProvider; 71 import com.android.settings.widget.MainSwitchBarController; 72 import com.android.settings.widget.SettingsMainSwitchBar; 73 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 74 import com.android.settingslib.RestrictedLockUtilsInternal; 75 import com.android.settingslib.RestrictedPreference; 76 import com.android.settingslib.drawable.CircleFramedDrawable; 77 import com.android.settingslib.search.SearchIndexable; 78 import com.android.settingslib.users.EditUserInfoController; 79 import com.android.settingslib.users.UserCreatingDialog; 80 import com.android.settingslib.utils.ThreadUtils; 81 82 import com.google.android.setupcompat.util.WizardManagerHelper; 83 84 import java.io.IOException; 85 import java.io.InputStream; 86 import java.util.ArrayList; 87 import java.util.Collections; 88 import java.util.HashMap; 89 import java.util.List; 90 import java.util.concurrent.ExecutorService; 91 import java.util.concurrent.Executors; 92 import java.util.concurrent.atomic.AtomicBoolean; 93 94 /** 95 * Screen that manages the list of users on the device. 96 * Secondary users and a guest user can be created if there is no restriction. 97 * 98 * The first user in the list is always the current user. 99 * Owner is the primary user. 100 */ 101 @SearchIndexable 102 public class UserSettings extends SettingsPreferenceFragment 103 implements Preference.OnPreferenceClickListener, 104 MultiUserSwitchBarController.OnMultiUserSwitchChangedListener, 105 DialogInterface.OnDismissListener { 106 107 private static final String TAG = "UserSettings"; 108 109 /** UserId of the user being removed */ 110 private static final String SAVE_REMOVING_USER = "removing_user"; 111 112 private static final String KEY_USER_LIST = "user_list"; 113 private static final String KEY_USER_ME = "user_me"; 114 private static final String KEY_USER_GUEST = "user_guest"; 115 private static final String KEY_ADD_GUEST = "guest_add"; 116 private static final String KEY_ADD_USER = "user_add"; 117 private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked"; 118 private static final String KEY_MULTIUSER_TOP_INTRO = "multiuser_top_intro"; 119 120 private static final int MENU_REMOVE_USER = Menu.FIRST; 121 122 private static final IntentFilter USER_REMOVED_INTENT_FILTER; 123 124 private static final int DIALOG_CONFIRM_REMOVE = 1; 125 private static final int DIALOG_ADD_USER = 2; 126 // Dialogs with id 3 and 4 got removed 127 private static final int DIALOG_USER_CANNOT_MANAGE = 5; 128 private static final int DIALOG_CHOOSE_USER_TYPE = 6; 129 private static final int DIALOG_NEED_LOCKSCREEN = 7; 130 private static final int DIALOG_CONFIRM_EXIT_GUEST = 8; 131 private static final int DIALOG_USER_PROFILE_EDITOR = 9; 132 private static final int DIALOG_USER_PROFILE_EDITOR_ADD_USER = 10; 133 private static final int DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE = 11; 134 private static final int DIALOG_CONFIRM_RESET_GUEST = 12; 135 136 private static final int MESSAGE_UPDATE_LIST = 1; 137 private static final int MESSAGE_USER_CREATED = 2; 138 139 private static final int USER_TYPE_USER = 1; 140 private static final int USER_TYPE_RESTRICTED_PROFILE = 2; 141 142 private static final int REQUEST_CHOOSE_LOCK = 10; 143 private static final int REQUEST_EDIT_GUEST = 11; 144 145 static final int RESULT_GUEST_REMOVED = 100; 146 147 private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED = 148 "key_add_user_long_message_displayed"; 149 150 private static final String KEY_TITLE = "title"; 151 private static final String KEY_SUMMARY = "summary"; 152 153 static { 154 USER_REMOVED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_REMOVED); 155 USER_REMOVED_INTENT_FILTER.addAction(Intent.ACTION_USER_INFO_CHANGED); 156 } 157 158 @VisibleForTesting 159 PreferenceGroup mUserListCategory; 160 @VisibleForTesting 161 UserPreference mMePreference; 162 @VisibleForTesting 163 RestrictedPreference mAddGuest; 164 @VisibleForTesting 165 RestrictedPreference mAddUser; 166 @VisibleForTesting 167 SparseArray<Bitmap> mUserIcons = new SparseArray<>(); 168 private int mRemovingUserId = -1; 169 private boolean mAddingUser; 170 private boolean mGuestUserAutoCreated; 171 private String mAddingUserName; 172 private UserCapabilities mUserCaps; 173 private boolean mShouldUpdateUserList = true; 174 private final Object mUserLock = new Object(); 175 private UserManager mUserManager; 176 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>(); 177 178 private MultiUserSwitchBarController mSwitchBarController; 179 private EditUserInfoController mEditUserInfoController = 180 new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY); 181 private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; 182 private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController; 183 private UserCreatingDialog mUserCreatingDialog; 184 private final AtomicBoolean mGuestCreationScheduled = new AtomicBoolean(); 185 private final ExecutorService mExecutor = Executors.newSingleThreadExecutor(); 186 187 private CharSequence mPendingUserName; 188 private Drawable mPendingUserIcon; 189 190 // A place to cache the generated default avatar 191 private Drawable mDefaultIconDrawable; 192 193 // TODO: Replace current Handler solution to something that doesn't leak memory and works 194 // TODO: during a configuration change 195 private Handler mHandler = new Handler() { 196 @Override 197 public void handleMessage(Message msg) { 198 switch (msg.what) { 199 case MESSAGE_UPDATE_LIST: 200 updateUserList(); 201 break; 202 case MESSAGE_USER_CREATED: 203 onUserCreated(msg.arg1); 204 break; 205 } 206 } 207 }; 208 209 private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { 210 @Override 211 public void onReceive(Context context, Intent intent) { 212 if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) { 213 mRemovingUserId = -1; 214 } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) { 215 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 216 if (userHandle != -1) { 217 mUserIcons.remove(userHandle); 218 } 219 } 220 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 221 } 222 }; 223 224 @Override getMetricsCategory()225 public int getMetricsCategory() { 226 return SettingsEnums.USER; 227 } 228 229 @Override onActivityCreated(Bundle savedInstanceState)230 public void onActivityCreated(Bundle savedInstanceState) { 231 super.onActivityCreated(savedInstanceState); 232 // Assume we are in a SettingsActivity. This is only safe because we currently use 233 // SettingsActivity as base for all preference fragments. 234 final SettingsActivity activity = (SettingsActivity) getActivity(); 235 final SettingsMainSwitchBar switchBar = activity.getSwitchBar(); 236 switchBar.setTitle(getContext().getString(R.string.multiple_users_main_switch_title)); 237 switchBar.show(); 238 mSwitchBarController = new MultiUserSwitchBarController(activity, 239 new MainSwitchBarController(switchBar), this /* listener */); 240 getSettingsLifecycle().addObserver(mSwitchBarController); 241 } 242 243 @Override onCreate(Bundle icicle)244 public void onCreate(Bundle icicle) { 245 super.onCreate(icicle); 246 addPreferencesFromResource(R.xml.user_settings); 247 final Activity activity = getActivity(); 248 if (!WizardManagerHelper.isDeviceProvisioned(activity)) { 249 activity.finish(); 250 return; 251 } 252 253 mGuestUserAutoCreated = getPrefContext().getResources().getBoolean( 254 com.android.internal.R.bool.config_guestUserAutoCreated); 255 256 mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController( 257 activity, KEY_ADD_USER_WHEN_LOCKED); 258 259 mMultiUserTopIntroPreferenceController = new MultiUserTopIntroPreferenceController(activity, 260 KEY_MULTIUSER_TOP_INTRO); 261 262 final PreferenceScreen screen = getPreferenceScreen(); 263 mAddUserWhenLockedPreferenceController.displayPreference(screen); 264 mMultiUserTopIntroPreferenceController.displayPreference(screen); 265 266 screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey()) 267 .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController); 268 269 if (icicle != null) { 270 if (icicle.containsKey(SAVE_REMOVING_USER)) { 271 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER); 272 } 273 mEditUserInfoController.onRestoreInstanceState(icicle); 274 } 275 276 mUserCaps = UserCapabilities.create(activity); 277 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 278 if (!mUserCaps.mEnabled) { 279 return; 280 } 281 282 final int myUserId = UserHandle.myUserId(); 283 284 mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST); 285 mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId); 286 mMePreference.setKey(KEY_USER_ME); 287 mMePreference.setOnPreferenceClickListener(this); 288 if (mUserCaps.mIsAdmin) { 289 mMePreference.setSummary(R.string.user_admin); 290 } 291 292 mAddGuest = findPreference(KEY_ADD_GUEST); 293 mAddGuest.setOnPreferenceClickListener(this); 294 295 mAddUser = findPreference(KEY_ADD_USER); 296 if (!mUserCaps.mCanAddRestrictedProfile) { 297 // Label should only mention adding a "user", not a "profile" 298 mAddUser.setTitle(R.string.user_add_user_menu); 299 } 300 mAddUser.setOnPreferenceClickListener(this); 301 302 activity.registerReceiverAsUser( 303 mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler); 304 305 updateUI(); 306 mShouldUpdateUserList = false; 307 } 308 309 @Override onResume()310 public void onResume() { 311 super.onResume(); 312 313 if (!mUserCaps.mEnabled) { 314 return; 315 } 316 final PreferenceScreen screen = getPreferenceScreen(); 317 318 mAddUserWhenLockedPreferenceController.updateState(screen.findPreference( 319 mAddUserWhenLockedPreferenceController.getPreferenceKey())); 320 321 if (mShouldUpdateUserList) { 322 updateUI(); 323 } 324 } 325 326 @Override onPause()327 public void onPause() { 328 mShouldUpdateUserList = true; 329 super.onPause(); 330 } 331 332 @Override onDestroy()333 public void onDestroy() { 334 super.onDestroy(); 335 336 if (mUserCaps == null || !mUserCaps.mEnabled) { 337 return; 338 } 339 340 getActivity().unregisterReceiver(mUserChangeReceiver); 341 } 342 343 @Override onSaveInstanceState(Bundle outState)344 public void onSaveInstanceState(Bundle outState) { 345 mEditUserInfoController.onSaveInstanceState(outState); 346 outState.putInt(SAVE_REMOVING_USER, mRemovingUserId); 347 super.onSaveInstanceState(outState); 348 } 349 350 @Override startActivityForResult(Intent intent, int requestCode)351 public void startActivityForResult(Intent intent, int requestCode) { 352 mEditUserInfoController.startingActivityForResult(); 353 super.startActivityForResult(intent, requestCode); 354 } 355 356 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)357 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 358 int pos = 0; 359 // TODO(b/191509236): The menu item does not need to be accessible for guest users, 360 // regardless of mGuestUserAutoCreated 361 if (!mUserCaps.mIsAdmin && canSwitchUserNow() && !(isCurrentUserGuest() 362 && mGuestUserAutoCreated)) { 363 String nickname = mUserManager.getUserName(); 364 MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++, 365 getResources().getString(R.string.user_remove_user_menu, nickname)); 366 removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 367 368 final EnforcedAdmin disallowRemoveUserAdmin = 369 RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getContext(), 370 UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); 371 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getContext(), removeThisUser, 372 disallowRemoveUserAdmin); 373 } 374 super.onCreateOptionsMenu(menu, inflater); 375 } 376 377 @Override onOptionsItemSelected(MenuItem item)378 public boolean onOptionsItemSelected(MenuItem item) { 379 final int itemId = item.getItemId(); 380 if (itemId == MENU_REMOVE_USER) { 381 onRemoveUserClicked(UserHandle.myUserId()); 382 return true; 383 } else { 384 return super.onOptionsItemSelected(item); 385 } 386 } 387 388 @Override onMultiUserSwitchChanged(boolean newState)389 public void onMultiUserSwitchChanged(boolean newState) { 390 updateUI(); 391 } 392 updateUI()393 private void updateUI() { 394 mUserCaps.updateAddUserCapabilities(getActivity()); 395 loadProfile(); 396 updateUserList(); 397 } 398 399 /** 400 * Loads profile information for the current user. 401 */ loadProfile()402 private void loadProfile() { 403 if (isCurrentUserGuest()) { 404 // No need to load profile information 405 mMePreference.setIcon(getEncircledDefaultIcon()); 406 mMePreference.setTitle( 407 mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest 408 : R.string.user_exit_guest_title); 409 mMePreference.setSelectable(true); 410 // removing a guest will result in switching back to the admin user 411 mMePreference.setEnabled(canSwitchUserNow()); 412 return; 413 } 414 415 new AsyncTask<Void, Void, String>() { 416 @Override 417 protected void onPostExecute(String result) { 418 finishLoadProfile(result); 419 } 420 421 @Override 422 protected String doInBackground(Void... values) { 423 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId()); 424 if (user.iconPath == null || user.iconPath.equals("")) { 425 // Assign profile photo. 426 copyMeProfilePhoto(getActivity(), user); 427 } 428 return user.name; 429 } 430 }.execute(); 431 } 432 finishLoadProfile(String profileName)433 private void finishLoadProfile(String profileName) { 434 if (getActivity() == null) { 435 return; 436 } 437 mMePreference.setTitle(getString(R.string.user_you, profileName)); 438 int myUserId = UserHandle.myUserId(); 439 Bitmap b = mUserManager.getUserIcon(myUserId); 440 if (b != null) { 441 mMePreference.setIcon(encircle(b)); 442 mUserIcons.put(myUserId, b); 443 } 444 } 445 hasLockscreenSecurity()446 private boolean hasLockscreenSecurity() { 447 LockPatternUtils lpu = new LockPatternUtils(getActivity()); 448 return lpu.isSecure(UserHandle.myUserId()); 449 } 450 launchChooseLockscreen()451 private void launchChooseLockscreen() { 452 Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); 453 chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS, 454 true); 455 startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK); 456 } 457 458 @Override onActivityResult(int requestCode, int resultCode, Intent data)459 public void onActivityResult(int requestCode, int resultCode, Intent data) { 460 super.onActivityResult(requestCode, resultCode, data); 461 462 if (requestCode == REQUEST_CHOOSE_LOCK) { 463 if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) { 464 addUserNow(USER_TYPE_RESTRICTED_PROFILE); 465 } 466 } else if (mGuestUserAutoCreated && requestCode == REQUEST_EDIT_GUEST 467 && resultCode == RESULT_GUEST_REMOVED) { 468 scheduleGuestCreation(); 469 } else { 470 mEditUserInfoController.onActivityResult(requestCode, resultCode, data); 471 } 472 } 473 onAddUserClicked(int userType)474 private void onAddUserClicked(int userType) { 475 synchronized (mUserLock) { 476 if (mRemovingUserId == -1 && !mAddingUser) { 477 switch (userType) { 478 case USER_TYPE_USER: 479 showDialog(DIALOG_ADD_USER); 480 break; 481 case USER_TYPE_RESTRICTED_PROFILE: 482 if (hasLockscreenSecurity()) { 483 showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE); 484 } else { 485 showDialog(DIALOG_NEED_LOCKSCREEN); 486 } 487 break; 488 } 489 } 490 } 491 } 492 onRemoveUserClicked(int userId)493 private void onRemoveUserClicked(int userId) { 494 synchronized (mUserLock) { 495 if (mRemovingUserId == -1 && !mAddingUser) { 496 mRemovingUserId = userId; 497 showDialog(DIALOG_CONFIRM_REMOVE); 498 } 499 } 500 } 501 onUserCreated(int userId)502 private void onUserCreated(int userId) { 503 hideUserCreatingDialog(); 504 // prevent crash when config changes during user creation 505 if (getContext() == null) { 506 return; 507 } 508 mAddingUser = false; 509 UserInfo userInfo = mUserManager.getUserInfo(userId); 510 openUserDetails(userInfo, true); 511 } 512 hideUserCreatingDialog()513 private void hideUserCreatingDialog() { 514 if (mUserCreatingDialog != null && mUserCreatingDialog.isShowing()) { 515 mUserCreatingDialog.dismiss(); 516 } 517 } 518 onUserCreationFailed()519 private void onUserCreationFailed() { 520 Toast.makeText(getContext(), 521 com.android.settingslib.R.string.add_user_failed, 522 Toast.LENGTH_SHORT).show(); 523 hideUserCreatingDialog(); 524 } 525 openUserDetails(UserInfo userInfo, boolean newUser)526 private void openUserDetails(UserInfo userInfo, boolean newUser) { 527 Bundle extras = new Bundle(); 528 extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userInfo.id); 529 extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, newUser); 530 531 final Context context = getContext(); 532 SubSettingLauncher launcher = new SubSettingLauncher(context) 533 .setDestination(UserDetailsSettings.class.getName()) 534 .setArguments(extras) 535 .setTitleText(getUserName(context, userInfo)) 536 .setSourceMetricsCategory(getMetricsCategory()); 537 if (mGuestUserAutoCreated && userInfo.isGuest()) { 538 launcher.setResultListener(this, REQUEST_EDIT_GUEST); 539 } 540 launcher.launch(); 541 } 542 543 @Override onDialogShowing()544 public void onDialogShowing() { 545 super.onDialogShowing(); 546 547 setOnDismissListener(this); 548 } 549 550 @Override onCreateDialog(int dialogId)551 public Dialog onCreateDialog(int dialogId) { 552 Context context = getActivity(); 553 if (context == null) { 554 return null; 555 } 556 switch (dialogId) { 557 case DIALOG_CONFIRM_REMOVE: { 558 Dialog dlg = 559 UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId, 560 new DialogInterface.OnClickListener() { 561 public void onClick(DialogInterface dialog, int which) { 562 removeUserNow(); 563 } 564 } 565 ); 566 return dlg; 567 } 568 case DIALOG_USER_CANNOT_MANAGE: 569 return new AlertDialog.Builder(context) 570 .setMessage(R.string.user_cannot_manage_message) 571 .setPositiveButton(android.R.string.ok, null) 572 .create(); 573 case DIALOG_ADD_USER: { 574 final SharedPreferences preferences = getActivity().getPreferences( 575 Context.MODE_PRIVATE); 576 final boolean longMessageDisplayed = preferences.getBoolean( 577 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false); 578 final int messageResId = longMessageDisplayed 579 ? com.android.settingslib.R.string.user_add_user_message_short 580 : com.android.settingslib.R.string.user_add_user_message_long; 581 Dialog dlg = new AlertDialog.Builder(context) 582 .setTitle(com.android.settingslib.R.string.user_add_user_title) 583 .setMessage(messageResId) 584 .setPositiveButton(android.R.string.ok, 585 new DialogInterface.OnClickListener() { 586 public void onClick(DialogInterface dialog, int which) { 587 showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER); 588 if (!longMessageDisplayed) { 589 preferences.edit().putBoolean( 590 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, 591 true).apply(); 592 } 593 } 594 }) 595 .setNegativeButton(android.R.string.cancel, null) 596 .create(); 597 return dlg; 598 } 599 case DIALOG_CHOOSE_USER_TYPE: { 600 List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>(); 601 HashMap<String, String> addUserItem = new HashMap<String, String>(); 602 addUserItem.put(KEY_TITLE, getString( 603 com.android.settingslib.R.string.user_add_user_item_title)); 604 addUserItem.put(KEY_SUMMARY, getString( 605 com.android.settingslib.R.string.user_add_user_item_summary)); 606 HashMap<String, String> addProfileItem = new HashMap<String, String>(); 607 addProfileItem.put(KEY_TITLE, getString( 608 com.android.settingslib.R.string.user_add_profile_item_title)); 609 addProfileItem.put(KEY_SUMMARY, getString( 610 com.android.settingslib.R.string.user_add_profile_item_summary)); 611 data.add(addUserItem); 612 data.add(addProfileItem); 613 AlertDialog.Builder builder = new AlertDialog.Builder(context); 614 SimpleAdapter adapter = new SimpleAdapter(builder.getContext(), 615 data, R.layout.two_line_list_item, 616 new String[]{KEY_TITLE, KEY_SUMMARY}, 617 new int[]{R.id.title, R.id.summary}); 618 builder.setTitle(com.android.settingslib.R.string.user_add_user_type_title); 619 builder.setAdapter(adapter, 620 new DialogInterface.OnClickListener() { 621 @Override 622 public void onClick(DialogInterface dialog, int which) { 623 onAddUserClicked(which == 0 624 ? USER_TYPE_USER 625 : USER_TYPE_RESTRICTED_PROFILE); 626 } 627 }); 628 return builder.create(); 629 } 630 case DIALOG_NEED_LOCKSCREEN: { 631 Dialog dlg = new AlertDialog.Builder(context) 632 .setMessage(com.android.settingslib.R.string.user_need_lock_message) 633 .setPositiveButton(com.android.settingslib.R.string.user_set_lock_button, 634 new DialogInterface.OnClickListener() { 635 @Override 636 public void onClick(DialogInterface dialog, int which) { 637 launchChooseLockscreen(); 638 } 639 }) 640 .setNegativeButton(android.R.string.cancel, null) 641 .create(); 642 return dlg; 643 } 644 case DIALOG_CONFIRM_EXIT_GUEST: { 645 Dialog dlg = new AlertDialog.Builder(context) 646 .setTitle(R.string.user_exit_guest_confirm_title) 647 .setMessage(R.string.user_exit_guest_confirm_message) 648 .setPositiveButton(R.string.user_exit_guest_dialog_remove, 649 new DialogInterface.OnClickListener() { 650 @Override 651 public void onClick(DialogInterface dialog, int which) { 652 exitGuest(); 653 } 654 }) 655 .setNegativeButton(android.R.string.cancel, null) 656 .create(); 657 return dlg; 658 } 659 case DIALOG_USER_PROFILE_EDITOR: { 660 return buildEditCurrentUserDialog(); 661 } 662 case DIALOG_USER_PROFILE_EDITOR_ADD_USER: { 663 synchronized (mUserLock) { 664 mPendingUserName = getString( 665 com.android.settingslib.R.string.user_new_user_name); 666 mPendingUserIcon = null; 667 } 668 return buildAddUserDialog(USER_TYPE_USER); 669 } 670 case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: { 671 synchronized (mUserLock) { 672 mPendingUserName = getString( 673 com.android.settingslib.R.string.user_new_profile_name); 674 mPendingUserIcon = null; 675 } 676 return buildAddUserDialog(USER_TYPE_RESTRICTED_PROFILE); 677 } 678 case DIALOG_CONFIRM_RESET_GUEST: { 679 return UserDialogs.createResetGuestDialog(getActivity(), 680 (dialog, which) -> resetGuest()); 681 } 682 default: 683 return null; 684 } 685 } 686 buildEditCurrentUserDialog()687 private Dialog buildEditCurrentUserDialog() { 688 final Activity activity = getActivity(); 689 if (activity == null) { 690 return null; 691 } 692 693 UserInfo user = mUserManager.getUserInfo(Process.myUserHandle().getIdentifier()); 694 Drawable userIcon = Utils.getUserIcon(activity, mUserManager, user); 695 696 return mEditUserInfoController.createDialog( 697 activity, 698 this::startActivityForResult, 699 userIcon, 700 user.name, 701 getString(com.android.settingslib.R.string.profile_info_settings_title), 702 (newUserName, newUserIcon) -> { 703 if (newUserIcon != userIcon) { 704 ThreadUtils.postOnBackgroundThread(() -> 705 mUserManager.setUserIcon(user.id, 706 UserIcons.convertToBitmap(newUserIcon))); 707 mMePreference.setIcon(newUserIcon); 708 } 709 710 if (!TextUtils.isEmpty(newUserName) && !newUserName.equals(user.name)) { 711 mMePreference.setTitle(newUserName); 712 mUserManager.setUserName(user.id, newUserName); 713 } 714 }, null); 715 } 716 717 private Dialog buildAddUserDialog(int userType) { 718 Dialog d; 719 synchronized (mUserLock) { 720 d = mEditUserInfoController.createDialog( 721 getActivity(), 722 this::startActivityForResult, 723 null, 724 mPendingUserName.toString(), 725 getString(userType == USER_TYPE_USER 726 ? com.android.settingslib.R.string.user_info_settings_title 727 : com.android.settingslib.R.string.profile_info_settings_title), 728 (userName, userIcon) -> { 729 mPendingUserIcon = userIcon; 730 mPendingUserName = userName; 731 addUserNow(userType); 732 }, 733 () -> { 734 synchronized (mUserLock) { 735 mPendingUserIcon = null; 736 mPendingUserName = null; 737 } 738 } 739 ); 740 } 741 return d; 742 } 743 744 @Override 745 public int getDialogMetricsCategory(int dialogId) { 746 switch (dialogId) { 747 case DIALOG_CONFIRM_REMOVE: 748 return SettingsEnums.DIALOG_USER_REMOVE; 749 case DIALOG_USER_CANNOT_MANAGE: 750 return SettingsEnums.DIALOG_USER_CANNOT_MANAGE; 751 case DIALOG_ADD_USER: 752 return SettingsEnums.DIALOG_USER_ADD; 753 case DIALOG_CHOOSE_USER_TYPE: 754 return SettingsEnums.DIALOG_USER_CHOOSE_TYPE; 755 case DIALOG_NEED_LOCKSCREEN: 756 return SettingsEnums.DIALOG_USER_NEED_LOCKSCREEN; 757 case DIALOG_CONFIRM_EXIT_GUEST: 758 case DIALOG_CONFIRM_RESET_GUEST: 759 return SettingsEnums.DIALOG_USER_CONFIRM_EXIT_GUEST; 760 case DIALOG_USER_PROFILE_EDITOR: 761 case DIALOG_USER_PROFILE_EDITOR_ADD_USER: 762 case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: 763 return SettingsEnums.DIALOG_USER_EDIT_PROFILE; 764 default: 765 return 0; 766 } 767 } 768 769 private void removeUserNow() { 770 if (mRemovingUserId == UserHandle.myUserId()) { 771 removeThisUser(); 772 } else { 773 ThreadUtils.postOnBackgroundThread(new Runnable() { 774 @Override 775 public void run() { 776 synchronized (mUserLock) { 777 mUserManager.removeUser(mRemovingUserId); 778 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 779 } 780 } 781 }); 782 } 783 } 784 785 private void removeThisUser() { 786 if (!canSwitchUserNow()) { 787 Log.w(TAG, "Cannot remove current user when switching is disabled"); 788 return; 789 } 790 try { 791 getContext().getSystemService(UserManager.class) 792 .removeUserOrSetEphemeral(UserHandle.myUserId(), 793 /* evenWhenDisallowed= */ false); 794 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); 795 } catch (RemoteException re) { 796 Log.e(TAG, "Unable to remove self user"); 797 } 798 } 799 800 private void addUserNow(final int userType) { 801 synchronized (mUserLock) { 802 mAddingUser = true; 803 mAddingUserName = userType == USER_TYPE_USER 804 ? (mPendingUserName != null ? mPendingUserName.toString() 805 : getString(R.string.user_new_user_name)) 806 : (mPendingUserName != null ? mPendingUserName.toString() 807 : getString(R.string.user_new_profile_name)); 808 } 809 810 mUserCreatingDialog = new UserCreatingDialog(getActivity()); 811 mUserCreatingDialog.show(); 812 ThreadUtils.postOnBackgroundThread(new Runnable() { 813 @Override 814 public void run() { 815 UserInfo user; 816 String username; 817 818 synchronized (mUserLock) { 819 username = mAddingUserName; 820 } 821 822 // Could take a few seconds 823 if (userType == USER_TYPE_USER) { 824 user = mUserManager.createUser(username, 0); 825 } else { 826 user = mUserManager.createRestrictedProfile(username); 827 } 828 829 synchronized (mUserLock) { 830 if (user == null) { 831 mAddingUser = false; 832 mPendingUserIcon = null; 833 mPendingUserName = null; 834 ThreadUtils.postOnMainThread(() -> onUserCreationFailed()); 835 return; 836 } 837 838 Drawable newUserIcon = mPendingUserIcon; 839 if (newUserIcon == null) { 840 newUserIcon = UserIcons.getDefaultUserIcon(getResources(), user.id, false); 841 } 842 mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon)); 843 844 if (userType == USER_TYPE_USER) { 845 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 846 } 847 848 mHandler.sendMessage(mHandler.obtainMessage( 849 MESSAGE_USER_CREATED, user.id, user.serialNumber)); 850 851 mPendingUserIcon = null; 852 mPendingUserName = null; 853 } 854 } 855 }); 856 } 857 858 /** 859 * Erase the current user (guest) and switch to another user. 860 */ 861 @VisibleForTesting 862 void exitGuest() { 863 // Just to be safe 864 if (!isCurrentUserGuest()) { 865 return; 866 } 867 mMetricsFeatureProvider.action(getActivity(), 868 SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED); 869 removeThisUser(); 870 } 871 872 /** 873 * Erase the current user (assuming it is a guest user), and create a new one in the background 874 */ 875 @VisibleForTesting 876 void resetGuest() { 877 // Just to be safe 878 if (!isCurrentUserGuest()) { 879 return; 880 } 881 int guestUserId = UserHandle.myUserId(); 882 // Using markGuestForDeletion allows us to create a new guest before this one is 883 // fully removed. This could happen if someone calls scheduleGuestCreation() 884 // immediately after calling this method. 885 boolean marked = mUserManager.markGuestForDeletion(guestUserId); 886 if (!marked) { 887 Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId); 888 return; 889 } 890 exitGuest(); 891 scheduleGuestCreation(); 892 } 893 894 /** 895 * Create a guest user in the background 896 */ 897 @VisibleForTesting 898 void scheduleGuestCreation() { 899 // TODO(b/191067027): Move guest recreation to system_server 900 if (mGuestCreationScheduled.compareAndSet(/* expect= */ false, /* update= */ true)) { 901 // Once mGuestCreationScheduled=true, mAddGuest needs to be updated so that it shows 902 // "Resetting guest..." 903 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 904 mExecutor.execute(() -> { 905 UserInfo guest = mUserManager.createGuest( 906 getContext(), getString(com.android.settingslib.R.string.user_guest)); 907 mGuestCreationScheduled.set(false); 908 if (guest == null) { 909 Log.e(TAG, "Unable to automatically recreate guest user"); 910 } 911 // The list needs to be updated whether or not guest creation worked. If guest 912 // creation failed, the list needs to update so that "Add guest" is displayed. 913 // Otherwise, the UX could be stuck in a state where there is no way to switch to 914 // the guest user (e.g. Guest would not be selectable, and it would be stuck 915 // saying "Resetting guest...") 916 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 917 }); 918 } 919 } 920 921 @VisibleForTesting 922 void updateUserList() { 923 final Context context = getActivity(); 924 if (context == null) { 925 return; 926 } 927 final List<UserInfo> users = mUserManager.getAliveUsers(); 928 929 final ArrayList<Integer> missingIcons = new ArrayList<>(); 930 final ArrayList<UserPreference> userPreferences = new ArrayList<>(); 931 userPreferences.add(mMePreference); 932 933 boolean canOpenUserDetails = 934 mUserCaps.mIsAdmin || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser); 935 for (UserInfo user : users) { 936 if (!user.supportsSwitchToByUser()) { 937 // Only users that can be switched to should show up here. 938 // e.g. Managed profiles appear under Accounts Settings instead 939 continue; 940 } 941 UserPreference pref; 942 if (user.id == UserHandle.myUserId()) { 943 pref = mMePreference; 944 } else { 945 final Context prefContext = getPrefContext(); 946 pref = new UserPreference(prefContext, null, user.id); 947 pref.setTitle(getUserName(prefContext, user)); 948 userPreferences.add(pref); 949 pref.setOnPreferenceClickListener(this); 950 pref.setEnabled(canOpenUserDetails); 951 pref.setSelectable(true); 952 953 if (user.isGuest()) { 954 pref.setIcon(getEncircledDefaultIcon()); 955 pref.setKey(KEY_USER_GUEST); 956 if (mUserCaps.mDisallowSwitchUser) { 957 pref.setDisabledByAdmin( 958 RestrictedLockUtilsInternal.getDeviceOwner(context)); 959 } else { 960 pref.setDisabledByAdmin(null); 961 } 962 } else { 963 pref.setKey("id=" + user.id); 964 if (user.isAdmin()) { 965 pref.setSummary(R.string.user_admin); 966 } 967 } 968 } 969 if (pref == null) { 970 continue; 971 } 972 if (user.id != UserHandle.myUserId() && !user.isGuest() && !user.isInitialized()) { 973 // sometimes after creating a guest the initialized flag isn't immediately set 974 // and we don't want to show "Not set up" summary for them 975 if (user.isRestricted()) { 976 pref.setSummary(R.string.user_summary_restricted_not_set_up); 977 } else { 978 pref.setSummary(R.string.user_summary_not_set_up); 979 // Disallow setting up user which results in user switching when the 980 // restriction is set. 981 pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow()); 982 } 983 } else if (user.isRestricted()) { 984 pref.setSummary(R.string.user_summary_restricted_profile); 985 } 986 if (user.iconPath != null) { 987 if (mUserIcons.get(user.id) == null) { 988 // Icon not loaded yet, print a placeholder 989 missingIcons.add(user.id); 990 pref.setIcon(getEncircledDefaultIcon()); 991 } else { 992 setPhotoId(pref, user); 993 } 994 } else { 995 // Icon not available yet, print a placeholder 996 pref.setIcon(getEncircledDefaultIcon()); 997 } 998 } 999 1000 // Add a temporary entry for the user being created 1001 if (mAddingUser) { 1002 UserPreference pref = new UserPreference(getPrefContext(), null, 1003 UserPreference.USERID_UNKNOWN); 1004 pref.setEnabled(false); 1005 pref.setTitle(mAddingUserName); 1006 pref.setIcon(getEncircledDefaultIcon()); 1007 userPreferences.add(pref); 1008 } 1009 1010 1011 // Sort list of users by serialNum 1012 Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR); 1013 1014 getActivity().invalidateOptionsMenu(); 1015 1016 // Load the icons 1017 if (missingIcons.size() > 0) { 1018 loadIconsAsync(missingIcons); 1019 } 1020 1021 // If profiles are supported, mUserListCategory will have a special title 1022 if (mUserCaps.mCanAddRestrictedProfile) { 1023 mUserListCategory.setTitle(R.string.user_list_title); 1024 } else { 1025 mUserListCategory.setTitle(null); 1026 } 1027 1028 // Remove everything from mUserListCategory and add new users. 1029 mUserListCategory.removeAll(); 1030 1031 // If multi-user is disabled, just show top info and return. 1032 final Preference addUserOnLockScreen = getPreferenceScreen().findPreference( 1033 mAddUserWhenLockedPreferenceController.getPreferenceKey()); 1034 mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen); 1035 1036 final Preference multiUserTopIntroPrefence = getPreferenceScreen().findPreference( 1037 mMultiUserTopIntroPreferenceController.getPreferenceKey()); 1038 mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPrefence); 1039 mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled); 1040 1041 updateAddGuest(context, users.stream().anyMatch(UserInfo::isGuest)); 1042 updateAddUser(context); 1043 1044 if (!mUserCaps.mUserSwitcherEnabled) { 1045 return; 1046 } 1047 1048 for (UserPreference userPreference : userPreferences) { 1049 userPreference.setOrder(Preference.DEFAULT_ORDER); 1050 mUserListCategory.addPreference(userPreference); 1051 } 1052 1053 } 1054 1055 private boolean isCurrentUserGuest() { 1056 return mUserCaps.mIsGuest; 1057 } 1058 1059 private boolean canSwitchUserNow() { 1060 return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; 1061 } 1062 1063 private void updateAddGuest(Context context, boolean isGuestAlreadyCreated) { 1064 if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest 1065 && WizardManagerHelper.isDeviceProvisioned(context) 1066 && mUserCaps.mUserSwitcherEnabled) { 1067 mAddGuest.setVisible(true); 1068 mAddGuest.setIcon(getEncircledDefaultIcon()); 1069 mAddGuest.setSelectable(true); 1070 if (mGuestUserAutoCreated && mGuestCreationScheduled.get()) { 1071 mAddGuest.setTitle(com.android.settingslib.R.string.user_guest); 1072 mAddGuest.setSummary(R.string.guest_resetting); 1073 mAddGuest.setEnabled(false); 1074 } else { 1075 mAddGuest.setTitle(com.android.settingslib.R.string.guest_new_guest); 1076 mAddGuest.setEnabled(canSwitchUserNow()); 1077 } 1078 } else { 1079 mAddGuest.setVisible(false); 1080 } 1081 } 1082 1083 private void updateAddUser(Context context) { 1084 if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) 1085 && WizardManagerHelper.isDeviceProvisioned(context) 1086 && mUserCaps.mUserSwitcherEnabled) { 1087 mAddUser.setVisible(true); 1088 mAddUser.setSelectable(true); 1089 final boolean canAddMoreUsers = mUserManager.canAddMoreUsers(); 1090 mAddUser.setEnabled(canAddMoreUsers && !mAddingUser && canSwitchUserNow()); 1091 if (!canAddMoreUsers) { 1092 mAddUser.setSummary( 1093 getString(R.string.user_add_max_count, getRealUsersCount())); 1094 } else { 1095 mAddUser.setSummary(null); 1096 } 1097 if (mAddUser.isEnabled()) { 1098 mAddUser.setDisabledByAdmin( 1099 mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null); 1100 } 1101 } else { 1102 mAddUser.setVisible(false); 1103 } 1104 } 1105 1106 /** 1107 * @return number of non-guest non-managed users 1108 */ 1109 @VisibleForTesting 1110 int getRealUsersCount() { 1111 return (int) mUserManager.getUsers() 1112 .stream() 1113 .filter(user -> !user.isGuest() && !user.isProfile()) 1114 .count(); 1115 } 1116 1117 private void loadIconsAsync(List<Integer> missingIcons) { 1118 new AsyncTask<List<Integer>, Void, Void>() { 1119 @Override 1120 protected void onPostExecute(Void result) { 1121 updateUserList(); 1122 } 1123 1124 @Override 1125 protected Void doInBackground(List<Integer>... values) { 1126 for (int userId : values[0]) { 1127 Bitmap bitmap = mUserManager.getUserIcon(userId); 1128 if (bitmap == null) { 1129 bitmap = getDefaultUserIconAsBitmap(getContext().getResources(), userId); 1130 } 1131 mUserIcons.append(userId, bitmap); 1132 } 1133 return null; 1134 } 1135 }.execute(missingIcons); 1136 } 1137 1138 private Drawable getEncircledDefaultIcon() { 1139 if (mDefaultIconDrawable == null) { 1140 mDefaultIconDrawable = encircle( 1141 getDefaultUserIconAsBitmap(getContext().getResources(), UserHandle.USER_NULL)); 1142 } 1143 return mDefaultIconDrawable; 1144 } 1145 1146 private void setPhotoId(Preference pref, UserInfo user) { 1147 Bitmap bitmap = mUserIcons.get(user.id); 1148 if (bitmap != null) { 1149 pref.setIcon(encircle(bitmap)); 1150 } 1151 } 1152 1153 /** Returns the user's name, or the appropriate string in the case of a Guest. */ 1154 public static String getUserName(Context context, @NonNull UserInfo userInfo) { 1155 if (userInfo.isGuest()) { 1156 return context.getString(R.string.user_guest); 1157 } 1158 return userInfo.name; 1159 } 1160 1161 @Override 1162 public boolean onPreferenceClick(Preference pref) { 1163 if (pref == mMePreference) { 1164 if (isCurrentUserGuest()) { 1165 if (mGuestUserAutoCreated) { 1166 showDialog(DIALOG_CONFIRM_RESET_GUEST); 1167 } else { 1168 showDialog(DIALOG_CONFIRM_EXIT_GUEST); 1169 } 1170 } else { 1171 showDialog(DIALOG_USER_PROFILE_EDITOR); 1172 } 1173 return true; 1174 } else if (pref instanceof UserPreference) { 1175 UserInfo userInfo = mUserManager.getUserInfo(((UserPreference) pref).getUserId()); 1176 openUserDetails(userInfo, false); 1177 return true; 1178 } else if (pref == mAddUser) { 1179 // If we allow both types, show a picker, otherwise directly go to 1180 // flow for full user. 1181 if (mUserCaps.mCanAddRestrictedProfile) { 1182 showDialog(DIALOG_CHOOSE_USER_TYPE); 1183 } else { 1184 onAddUserClicked(USER_TYPE_USER); 1185 } 1186 return true; 1187 } else if (pref == mAddGuest) { 1188 mAddGuest.setEnabled(false); // prevent multiple tap issue 1189 mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_GUEST_ADD); 1190 UserInfo guest = mUserManager.createGuest( 1191 getContext(), getString(com.android.settingslib.R.string.user_guest)); 1192 if (guest == null) { 1193 Toast.makeText(getContext(), 1194 com.android.settingslib.R.string.add_user_failed, 1195 Toast.LENGTH_SHORT).show(); 1196 return true; 1197 } 1198 openUserDetails(guest, true); 1199 return true; 1200 } 1201 return false; 1202 } 1203 1204 private Drawable encircle(Bitmap icon) { 1205 Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon); 1206 return circled; 1207 } 1208 1209 @Override 1210 public void onDismiss(DialogInterface dialog) { 1211 synchronized (mUserLock) { 1212 mRemovingUserId = -1; 1213 updateUserList(); 1214 } 1215 } 1216 1217 @Override 1218 public int getHelpResource() { 1219 return R.string.help_url_users; 1220 } 1221 1222 /** 1223 * Returns a default user icon (as a {@link Bitmap}) for the given user. 1224 * 1225 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}. 1226 * 1227 * @param resources resources object to fetch the user icon. 1228 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon 1229 */ 1230 private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) { 1231 Bitmap bitmap = null; 1232 // Try finding the corresponding bitmap in the dark bitmap cache 1233 bitmap = sDarkDefaultUserBitmapCache.get(userId); 1234 if (bitmap == null) { 1235 bitmap = UserIcons.convertToBitmap( 1236 UserIcons.getDefaultUserIcon(resources, userId, false)); 1237 // Save it to cache 1238 sDarkDefaultUserBitmapCache.put(userId, bitmap); 1239 } 1240 return bitmap; 1241 } 1242 1243 /** 1244 * Assign the default photo to user with {@paramref userId} 1245 * 1246 * @param context used to get the {@link UserManager} 1247 * @param userId used to get the icon bitmap 1248 * @return true if assign photo successfully, false if failed 1249 */ 1250 @VisibleForTesting 1251 static boolean assignDefaultPhoto(Context context, int userId) { 1252 if (context == null) { 1253 return false; 1254 } 1255 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 1256 Bitmap bitmap = getDefaultUserIconAsBitmap(context.getResources(), userId); 1257 um.setUserIcon(userId, bitmap); 1258 1259 return true; 1260 } 1261 1262 @WorkerThread 1263 static void copyMeProfilePhoto(Context context, UserInfo user) { 1264 Uri contactUri = ContactsContract.Profile.CONTENT_URI; 1265 1266 int userId = user != null ? user.id : UserHandle.myUserId(); 1267 1268 InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream( 1269 context.getContentResolver(), 1270 contactUri, true); 1271 // If there's no profile photo, assign a default avatar 1272 if (avatarDataStream == null) { 1273 assignDefaultPhoto(context, userId); 1274 return; 1275 } 1276 1277 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 1278 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream); 1279 um.setUserIcon(userId, icon); 1280 try { 1281 avatarDataStream.close(); 1282 } catch (IOException ioe) { 1283 } 1284 } 1285 1286 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1287 new BaseSearchIndexProvider(R.xml.user_settings) { 1288 1289 @Override 1290 protected boolean isPageSearchEnabled(Context context) { 1291 final UserCapabilities userCaps = UserCapabilities.create(context); 1292 return userCaps.mEnabled; 1293 } 1294 1295 @Override 1296 public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId, 1297 boolean suppressAllPage) { 1298 final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId, 1299 suppressAllPage); 1300 AddUserWhenLockedPreferenceController controller = 1301 new AddUserWhenLockedPreferenceController( 1302 context, KEY_ADD_USER_WHEN_LOCKED); 1303 controller.updateNonIndexableKeys(niks); 1304 new AutoSyncDataPreferenceController(context, null /* parent */) 1305 .updateNonIndexableKeys(niks); 1306 new AutoSyncPersonalDataPreferenceController(context, null /* parent */) 1307 .updateNonIndexableKeys(niks); 1308 new AutoSyncWorkDataPreferenceController(context, null /* parent */) 1309 .updateNonIndexableKeys(niks); 1310 return niks; 1311 } 1312 }; 1313 } 1314