Home | History | Annotate | Download | only in users
      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