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