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.app.ActivityManagerNative; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.DialogInterface; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.SharedPreferences; 28 import android.content.pm.UserInfo; 29 import android.graphics.Bitmap; 30 import android.graphics.BitmapFactory; 31 import android.graphics.drawable.BitmapDrawable; 32 import android.graphics.drawable.Drawable; 33 import android.os.AsyncTask; 34 import android.os.Bundle; 35 import android.os.Handler; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.os.UserHandle; 39 import android.os.UserManager; 40 import android.preference.Preference; 41 import android.preference.PreferenceScreen; 42 import android.preference.Preference.OnPreferenceClickListener; 43 import android.preference.PreferenceGroup; 44 import android.provider.ContactsContract; 45 import android.provider.ContactsContract.Contacts; 46 import android.text.InputType; 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.view.View; 53 import android.view.View.OnClickListener; 54 55 import com.android.settings.R; 56 import com.android.settings.SelectableEditTextPreference; 57 import com.android.settings.SettingsPreferenceFragment; 58 import com.android.settings.Utils; 59 import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; 60 61 import java.util.ArrayList; 62 import java.util.List; 63 64 public class UserSettings extends SettingsPreferenceFragment 65 implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener, 66 Preference.OnPreferenceChangeListener { 67 68 private static final String TAG = "UserSettings"; 69 70 /** UserId of the user being removed */ 71 private static final String SAVE_REMOVING_USER = "removing_user"; 72 /** UserId of the user that was just added */ 73 private static final String SAVE_ADDING_USER = "adding_user"; 74 75 private static final String KEY_USER_NICKNAME = "user_nickname"; 76 private static final String KEY_USER_LIST = "user_list"; 77 private static final String KEY_USER_ME = "user_me"; 78 79 private static final int MENU_ADD_USER = Menu.FIRST; 80 private static final int MENU_REMOVE_USER = Menu.FIRST+1; 81 82 private static final int DIALOG_CONFIRM_REMOVE = 1; 83 private static final int DIALOG_ADD_USER = 2; 84 private static final int DIALOG_SETUP_USER = 3; 85 private static final int DIALOG_USER_CANNOT_MANAGE = 4; 86 87 private static final int MESSAGE_UPDATE_LIST = 1; 88 private static final int MESSAGE_SETUP_USER = 2; 89 90 private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED = 91 "key_add_user_long_message_displayed"; 92 93 private static final int[] USER_DRAWABLES = { 94 R.drawable.avatar_default_1, 95 R.drawable.avatar_default_2, 96 R.drawable.avatar_default_3, 97 R.drawable.avatar_default_4, 98 R.drawable.avatar_default_5, 99 R.drawable.avatar_default_6, 100 R.drawable.avatar_default_7, 101 R.drawable.avatar_default_8 102 }; 103 104 private PreferenceGroup mUserListCategory; 105 private Preference mMePreference; 106 private SelectableEditTextPreference mNicknamePreference; 107 private int mRemovingUserId = -1; 108 private int mAddedUserId = 0; 109 private boolean mAddingUser; 110 private boolean mProfileExists; 111 112 private final Object mUserLock = new Object(); 113 private UserManager mUserManager; 114 private SparseArray<Drawable> mUserIcons = new SparseArray<Drawable>(); 115 private boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER; 116 117 118 private Handler mHandler = new Handler() { 119 @Override 120 public void handleMessage(Message msg) { 121 switch (msg.what) { 122 case MESSAGE_UPDATE_LIST: 123 updateUserList(); 124 break; 125 case MESSAGE_SETUP_USER: 126 onUserCreated(msg.arg1); 127 break; 128 } 129 } 130 }; 131 132 private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { 133 @Override 134 public void onReceive(Context context, Intent intent) { 135 if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) { 136 mRemovingUserId = -1; 137 } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) { 138 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 139 if (userHandle != -1) { 140 mUserIcons.remove(userHandle); 141 } 142 } 143 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 144 } 145 }; 146 147 @Override 148 public void onCreate(Bundle icicle) { 149 super.onCreate(icicle); 150 151 if (icicle != null) { 152 if (icicle.containsKey(SAVE_ADDING_USER)) { 153 mAddedUserId = icicle.getInt(SAVE_ADDING_USER); 154 } 155 if (icicle.containsKey(SAVE_REMOVING_USER)) { 156 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER); 157 } 158 } 159 160 mUserManager = (UserManager) getActivity().getSystemService(Context.USER_SERVICE); 161 addPreferencesFromResource(R.xml.user_settings); 162 mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST); 163 mMePreference = (Preference) findPreference(KEY_USER_ME); 164 mMePreference.setOnPreferenceClickListener(this); 165 if (!mIsOwner) { 166 mMePreference.setSummary(null); 167 } 168 Preference ownerInfo = findPreference("user_owner_info"); 169 if (ownerInfo != null && !mIsOwner) { 170 ownerInfo.setTitle(R.string.user_info_settings_title); 171 } 172 mNicknamePreference = (SelectableEditTextPreference) findPreference(KEY_USER_NICKNAME); 173 mNicknamePreference.setOnPreferenceChangeListener(this); 174 mNicknamePreference.getEditText().setInputType( 175 InputType.TYPE_TEXT_VARIATION_NORMAL | InputType.TYPE_TEXT_FLAG_CAP_WORDS); 176 mNicknamePreference.setInitialSelectionMode( 177 SelectableEditTextPreference.SELECTION_SELECT_ALL); 178 loadProfile(); 179 setHasOptionsMenu(true); 180 IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED); 181 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 182 getActivity().registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, 183 mHandler); 184 } 185 186 @Override 187 public void onResume() { 188 super.onResume(); 189 loadProfile(); 190 updateUserList(); 191 } 192 193 @Override 194 public void onDestroy() { 195 super.onDestroy(); 196 getActivity().unregisterReceiver(mUserChangeReceiver); 197 } 198 199 @Override 200 public void onSaveInstanceState(Bundle outState) { 201 super.onSaveInstanceState(outState); 202 203 outState.putInt(SAVE_ADDING_USER, mAddedUserId); 204 outState.putInt(SAVE_REMOVING_USER, mRemovingUserId); 205 } 206 207 @Override 208 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 209 if (mIsOwner) { 210 if (UserManager.getMaxSupportedUsers() > mUserManager.getUsers(false).size()) { 211 MenuItem addUserItem = menu.add(0, MENU_ADD_USER, 0, R.string.user_add_user_menu); 212 addUserItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM 213 | MenuItem.SHOW_AS_ACTION_WITH_TEXT); 214 } 215 } else { 216 String nickname = mUserManager.getUserName(); 217 MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, 0, 218 getResources().getString(R.string.user_remove_user_menu, nickname)); 219 removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 220 } 221 super.onCreateOptionsMenu(menu, inflater); 222 } 223 224 @Override 225 public boolean onOptionsItemSelected(MenuItem item) { 226 final int itemId = item.getItemId(); 227 if (itemId == MENU_ADD_USER) { 228 onAddUserClicked(); 229 return true; 230 } else if (itemId == MENU_REMOVE_USER) { 231 onRemoveUserClicked(UserHandle.myUserId()); 232 return true; 233 } else { 234 return super.onOptionsItemSelected(item); 235 } 236 } 237 238 private void loadProfile() { 239 mProfileExists = false; 240 new AsyncTask<Void, Void, String>() { 241 @Override 242 protected void onPostExecute(String result) { 243 finishLoadProfile(result); 244 } 245 246 @Override 247 protected String doInBackground(Void... values) { 248 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId()); 249 if (user.iconPath == null || user.iconPath.equals("")) { 250 assignProfilePhoto(user); 251 } 252 String profileName = getProfileName(); 253 return profileName; 254 } 255 }.execute(); 256 } 257 258 private void finishLoadProfile(String profileName) { 259 mMePreference.setTitle(profileName); 260 int myUserId = UserHandle.myUserId(); 261 Bitmap b = mUserManager.getUserIcon(myUserId); 262 if (b != null) { 263 Drawable d = new BitmapDrawable(b); 264 mMePreference.setIcon(d); 265 mUserIcons.put(myUserId, d); 266 } 267 } 268 269 private void onAddUserClicked() { 270 synchronized (mUserLock) { 271 if (mRemovingUserId == -1 && !mAddingUser) { 272 showDialog(DIALOG_ADD_USER); 273 } 274 } 275 } 276 277 private void onRemoveUserClicked(int userId) { 278 synchronized (mUserLock) { 279 if (mRemovingUserId == -1 && !mAddingUser) { 280 mRemovingUserId = userId; 281 showDialog(DIALOG_CONFIRM_REMOVE); 282 } 283 } 284 } 285 286 private void onUserCreated(int userId) { 287 mAddedUserId = userId; 288 showDialog(DIALOG_SETUP_USER); 289 } 290 291 @Override 292 public void onDialogShowing() { 293 super.onDialogShowing(); 294 295 setOnDismissListener(this); 296 } 297 298 @Override 299 public Dialog onCreateDialog(int dialogId) { 300 switch (dialogId) { 301 case DIALOG_CONFIRM_REMOVE: { 302 Dialog dlg = new AlertDialog.Builder(getActivity()) 303 .setTitle(UserHandle.myUserId() == mRemovingUserId 304 ? R.string.user_confirm_remove_self_title 305 : R.string.user_confirm_remove_title) 306 .setMessage(UserHandle.myUserId() == mRemovingUserId 307 ? R.string.user_confirm_remove_self_message 308 : R.string.user_confirm_remove_message) 309 .setPositiveButton(R.string.user_delete_button, 310 new DialogInterface.OnClickListener() { 311 public void onClick(DialogInterface dialog, int which) { 312 removeUserNow(); 313 } 314 }) 315 .setNegativeButton(android.R.string.cancel, null) 316 .create(); 317 return dlg; 318 } 319 case DIALOG_USER_CANNOT_MANAGE: 320 return new AlertDialog.Builder(getActivity()) 321 .setMessage(R.string.user_cannot_manage_message) 322 .setPositiveButton(android.R.string.ok, null) 323 .create(); 324 case DIALOG_ADD_USER: { 325 final SharedPreferences preferences = getActivity().getPreferences( 326 Context.MODE_PRIVATE); 327 final boolean longMessageDisplayed = preferences.getBoolean( 328 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false); 329 final int messageResId = longMessageDisplayed 330 ? R.string.user_add_user_message_short 331 : R.string.user_add_user_message_long; 332 Dialog dlg = new AlertDialog.Builder(getActivity()) 333 .setTitle(R.string.user_add_user_title) 334 .setMessage(messageResId) 335 .setPositiveButton(android.R.string.ok, 336 new DialogInterface.OnClickListener() { 337 public void onClick(DialogInterface dialog, int which) { 338 addUserNow(); 339 if (!longMessageDisplayed) { 340 preferences.edit().putBoolean(KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, 341 true).commit(); 342 } 343 } 344 }) 345 .setNegativeButton(android.R.string.cancel, null) 346 .create(); 347 return dlg; 348 } 349 case DIALOG_SETUP_USER: { 350 Dialog dlg = new AlertDialog.Builder(getActivity()) 351 .setTitle(R.string.user_setup_dialog_title) 352 .setMessage(R.string.user_setup_dialog_message) 353 .setPositiveButton(R.string.user_setup_button_setup_now, 354 new DialogInterface.OnClickListener() { 355 public void onClick(DialogInterface dialog, int which) { 356 switchUserNow(mAddedUserId); 357 } 358 }) 359 .setNegativeButton(R.string.user_setup_button_setup_later, null) 360 .create(); 361 return dlg; 362 } 363 default: 364 return null; 365 } 366 } 367 368 private void removeUserNow() { 369 if (mRemovingUserId == UserHandle.myUserId()) { 370 removeThisUser(); 371 } else { 372 new Thread() { 373 public void run() { 374 synchronized (mUserLock) { 375 mUserManager.removeUser(mRemovingUserId); 376 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 377 } 378 } 379 }.start(); 380 } 381 } 382 383 private void removeThisUser() { 384 try { 385 ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER); 386 ((UserManager) getActivity().getSystemService(Context.USER_SERVICE)) 387 .removeUser(UserHandle.myUserId()); 388 } catch (RemoteException re) { 389 Log.e(TAG, "Unable to remove self user"); 390 } 391 } 392 393 private void addUserNow() { 394 synchronized (mUserLock) { 395 mAddingUser = true; 396 updateUserList(); 397 new Thread() { 398 public void run() { 399 // Could take a few seconds 400 UserInfo user = mUserManager.createUser( 401 getActivity().getResources().getString(R.string.user_new_user_name), 0); 402 if (user != null) { 403 assignDefaultPhoto(user); 404 } 405 synchronized (mUserLock) { 406 mAddingUser = false; 407 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 408 mHandler.sendMessage(mHandler.obtainMessage( 409 MESSAGE_SETUP_USER, user.id, user.serialNumber)); 410 } 411 } 412 }.start(); 413 } 414 } 415 416 private void switchUserNow(int userId) { 417 try { 418 ActivityManagerNative.getDefault().switchUser(userId); 419 } catch (RemoteException re) { 420 // Nothing to do 421 } 422 } 423 424 private void updateUserList() { 425 if (getActivity() == null) return; 426 List<UserInfo> users = mUserManager.getUsers(true); 427 428 mUserListCategory.removeAll(); 429 mUserListCategory.setOrderingAsAdded(false); 430 431 final ArrayList<Integer> missingIcons = new ArrayList<Integer>(); 432 for (UserInfo user : users) { 433 Preference pref; 434 if (user.id == UserHandle.myUserId()) { 435 pref = mMePreference; 436 mNicknamePreference.setText(user.name); 437 mNicknamePreference.setSummary(user.name); 438 } else { 439 pref = new UserPreference(getActivity(), null, user.id, 440 UserHandle.myUserId() == UserHandle.USER_OWNER, this); 441 pref.setOnPreferenceClickListener(this); 442 pref.setKey("id=" + user.id); 443 mUserListCategory.addPreference(pref); 444 if (user.id == UserHandle.USER_OWNER) { 445 pref.setSummary(R.string.user_owner); 446 } 447 pref.setTitle(user.name); 448 if (!isInitialized(user)) { 449 pref.setSummary(R.string.user_summary_not_set_up); 450 } 451 } 452 if (user.iconPath != null) { 453 if (mUserIcons.get(user.id) == null) { 454 missingIcons.add(user.id); 455 pref.setIcon(R.drawable.avatar_default_1); 456 } else { 457 setPhotoId(pref, user); 458 } 459 } 460 } 461 // Add a temporary entry for the user being created 462 if (mAddingUser) { 463 Preference pref = new UserPreference(getActivity(), null, UserPreference.USERID_UNKNOWN, 464 false, null); 465 pref.setEnabled(false); 466 pref.setTitle(R.string.user_new_user_name); 467 pref.setSummary(R.string.user_adding_new_user); 468 pref.setIcon(R.drawable.avatar_default_1); 469 mUserListCategory.addPreference(pref); 470 } 471 getActivity().invalidateOptionsMenu(); 472 473 // Load the icons 474 if (missingIcons.size() > 0) { 475 loadIconsAsync(missingIcons); 476 } 477 } 478 479 private void loadIconsAsync(List<Integer> missingIcons) { 480 new AsyncTask<List<Integer>, Void, Void>() { 481 @Override 482 protected void onPostExecute(Void result) { 483 updateUserList(); 484 } 485 486 @Override 487 protected Void doInBackground(List<Integer>... values) { 488 if (getActivity() == null) return null; 489 for (int userId : values[0]) { 490 Bitmap bitmap = mUserManager.getUserIcon(userId); 491 Drawable d = new BitmapDrawable(getResources(), bitmap); 492 mUserIcons.append(userId, d); 493 } 494 return null; 495 } 496 }.execute(missingIcons); 497 498 } 499 private void assignProfilePhoto(final UserInfo user) { 500 if (!Utils.copyMeProfilePhoto(getActivity(), user)) { 501 assignDefaultPhoto(user); 502 } 503 } 504 505 private String getProfileName() { 506 String name = Utils.getMeProfileName(getActivity(), true); 507 if (name != null) { 508 mProfileExists = true; 509 } 510 return name; 511 } 512 513 private void assignDefaultPhoto(UserInfo user) { 514 Bitmap bitmap = BitmapFactory.decodeResource(getResources(), 515 USER_DRAWABLES[user.id % USER_DRAWABLES.length]); 516 mUserManager.setUserIcon(user.id, bitmap); 517 } 518 519 private void setPhotoId(Preference pref, UserInfo user) { 520 Drawable d = mUserIcons.get(user.id); // UserUtils.getUserIcon(mUserManager, user); 521 if (d != null) { 522 pref.setIcon(d); 523 } 524 } 525 526 private void setUserName(String name) { 527 mUserManager.setUserName(UserHandle.myUserId(), name); 528 mNicknamePreference.setSummary(name); 529 getActivity().invalidateOptionsMenu(); 530 } 531 532 @Override 533 public boolean onPreferenceClick(Preference pref) { 534 if (pref == mMePreference) { 535 Intent editProfile; 536 if (!mProfileExists) { 537 editProfile = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI); 538 // TODO: Make this a proper API 539 editProfile.putExtra("newLocalProfile", true); 540 } else { 541 editProfile = new Intent(Intent.ACTION_EDIT, ContactsContract.Profile.CONTENT_URI); 542 } 543 // To make sure that it returns back here when done 544 // TODO: Make this a proper API 545 editProfile.putExtra("finishActivityOnSaveCompleted", true); 546 startActivity(editProfile); 547 } else if (pref instanceof UserPreference) { 548 int userId = ((UserPreference) pref).getUserId(); 549 // Get the latest status of the user 550 UserInfo user = mUserManager.getUserInfo(userId); 551 if (UserHandle.myUserId() != UserHandle.USER_OWNER) { 552 showDialog(DIALOG_USER_CANNOT_MANAGE); 553 } else { 554 if (!isInitialized(user)) { 555 mHandler.sendMessage(mHandler.obtainMessage( 556 MESSAGE_SETUP_USER, user.id, user.serialNumber)); 557 } 558 } 559 } 560 return false; 561 } 562 563 private boolean isInitialized(UserInfo user) { 564 return (user.flags & UserInfo.FLAG_INITIALIZED) != 0; 565 } 566 567 @Override 568 public void onClick(View v) { 569 if (v.getTag() instanceof UserPreference) { 570 int userId = ((UserPreference) v.getTag()).getUserId(); 571 onRemoveUserClicked(userId); 572 } 573 } 574 575 @Override 576 public void onDismiss(DialogInterface dialog) { 577 synchronized (mUserLock) { 578 mAddingUser = false; 579 mRemovingUserId = -1; 580 updateUserList(); 581 } 582 } 583 584 @Override 585 public boolean onPreferenceChange(Preference preference, Object newValue) { 586 if (preference == mNicknamePreference) { 587 String value = (String) newValue; 588 if (preference == mNicknamePreference && value != null 589 && value.length() > 0) { 590 setUserName(value); 591 } 592 return true; 593 } 594 return false; 595 } 596 597 @Override 598 public int getHelpResource() { 599 return R.string.help_url_users; 600 } 601 } 602