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.accounts.Account;
20import android.accounts.AccountManager;
21import android.app.Activity;
22import android.app.ActivityManagerNative;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.app.Fragment;
26import android.app.admin.DevicePolicyManager;
27import android.content.BroadcastReceiver;
28import android.content.Context;
29import android.content.DialogInterface;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.SharedPreferences;
33import android.content.pm.UserInfo;
34import android.content.res.Resources;
35import android.graphics.Bitmap;
36import android.graphics.drawable.Drawable;
37import android.os.AsyncTask;
38import android.os.Bundle;
39import android.os.Handler;
40import android.os.Message;
41import android.os.RemoteException;
42import android.os.UserHandle;
43import android.os.UserManager;
44import android.preference.Preference;
45import android.preference.Preference.OnPreferenceClickListener;
46import android.preference.PreferenceGroup;
47import android.provider.Settings;
48import android.provider.Settings.Secure;
49import android.util.Log;
50import android.util.SparseArray;
51import android.view.Menu;
52import android.view.MenuInflater;
53import android.view.MenuItem;
54import android.view.View;
55import android.view.View.OnClickListener;
56import android.widget.SimpleAdapter;
57
58import com.android.internal.util.UserIcons;
59import com.android.internal.widget.LockPatternUtils;
60import com.android.settings.ChooseLockGeneric;
61import com.android.settings.OwnerInfoSettings;
62import com.android.settings.R;
63import com.android.settings.SelectableEditTextPreference;
64import com.android.settings.SettingsActivity;
65import com.android.settings.SettingsPreferenceFragment;
66import com.android.settings.Utils;
67import com.android.settings.drawable.CircleFramedDrawable;
68
69import java.util.ArrayList;
70import java.util.HashMap;
71import java.util.List;
72
73/**
74 * Screen that manages the list of users on the device.
75 * Guest user is an always visible entry, even if the guest is not currently
76 * active/created. It is meant for controlling properties of a guest user.
77 *
78 * The first one is always the current user.
79 * Owner is the primary user.
80 */
81public class UserSettings extends SettingsPreferenceFragment
82        implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener,
83        Preference.OnPreferenceChangeListener,
84        EditUserInfoController.OnContentChangedCallback {
85
86    private static final String TAG = "UserSettings";
87
88    /** UserId of the user being removed */
89    private static final String SAVE_REMOVING_USER = "removing_user";
90    /** UserId of the user that was just added */
91    private static final String SAVE_ADDING_USER = "adding_user";
92
93    private static final String KEY_USER_LIST = "user_list";
94    private static final String KEY_USER_ME = "user_me";
95    private static final String KEY_ADD_USER = "user_add";
96
97    private static final int MENU_REMOVE_USER = Menu.FIRST;
98    private static final int MENU_ADD_ON_LOCKSCREEN = Menu.FIRST + 1;
99
100    private static final int DIALOG_CONFIRM_REMOVE = 1;
101    private static final int DIALOG_ADD_USER = 2;
102    private static final int DIALOG_SETUP_USER = 3;
103    private static final int DIALOG_SETUP_PROFILE = 4;
104    private static final int DIALOG_USER_CANNOT_MANAGE = 5;
105    private static final int DIALOG_CHOOSE_USER_TYPE = 6;
106    private static final int DIALOG_NEED_LOCKSCREEN = 7;
107    private static final int DIALOG_CONFIRM_EXIT_GUEST = 8;
108    private static final int DIALOG_USER_PROFILE_EDITOR = 9;
109
110    private static final int MESSAGE_UPDATE_LIST = 1;
111    private static final int MESSAGE_SETUP_USER = 2;
112    private static final int MESSAGE_CONFIG_USER = 3;
113
114    private static final int USER_TYPE_USER = 1;
115    private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
116
117    private static final int REQUEST_CHOOSE_LOCK = 10;
118
119    private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
120            "key_add_user_long_message_displayed";
121
122    private static final String KEY_TITLE = "title";
123    private static final String KEY_SUMMARY = "summary";
124
125    private PreferenceGroup mUserListCategory;
126    private Preference mMePreference;
127    private SelectableEditTextPreference mNicknamePreference;
128    private Preference mAddUser;
129    private int mRemovingUserId = -1;
130    private int mAddedUserId = 0;
131    private boolean mAddingUser;
132    private boolean mEnabled = true;
133    private boolean mCanAddRestrictedProfile = true;
134
135    private final Object mUserLock = new Object();
136    private UserManager mUserManager;
137    private SparseArray<Bitmap> mUserIcons = new SparseArray<Bitmap>();
138    private boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER;
139    private boolean mIsGuest;
140
141    private EditUserInfoController mEditUserInfoController =
142            new EditUserInfoController();
143
144    // A place to cache the generated default avatar
145    private Drawable mDefaultIconDrawable;
146
147    private Handler mHandler = new Handler() {
148        @Override
149        public void handleMessage(Message msg) {
150            switch (msg.what) {
151            case MESSAGE_UPDATE_LIST:
152                updateUserList();
153                break;
154            case MESSAGE_SETUP_USER:
155                onUserCreated(msg.arg1);
156                break;
157            case MESSAGE_CONFIG_USER:
158                onManageUserClicked(msg.arg1, true);
159                break;
160            }
161        }
162    };
163
164    private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
165        @Override
166        public void onReceive(Context context, Intent intent) {
167            if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
168                mRemovingUserId = -1;
169            } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) {
170                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
171                if (userHandle != -1) {
172                    mUserIcons.remove(userHandle);
173                }
174            }
175            mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
176        }
177    };
178
179    @Override
180    public void onCreate(Bundle icicle) {
181        super.onCreate(icicle);
182
183        if (icicle != null) {
184            if (icicle.containsKey(SAVE_ADDING_USER)) {
185                mAddedUserId = icicle.getInt(SAVE_ADDING_USER);
186            }
187            if (icicle.containsKey(SAVE_REMOVING_USER)) {
188                mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER);
189            }
190            mEditUserInfoController.onRestoreInstanceState(icicle);
191        }
192        final Context context = getActivity();
193        mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
194        boolean hasMultipleUsers = mUserManager.getUserCount() > 1;
195        if ((!UserManager.supportsMultipleUsers() && !hasMultipleUsers)
196                || Utils.isMonkeyRunning()) {
197            mEnabled = false;
198            return;
199        }
200
201        final int myUserId = UserHandle.myUserId();
202        mIsGuest = mUserManager.getUserInfo(myUserId).isGuest();
203
204        addPreferencesFromResource(R.xml.user_settings);
205        mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
206        mMePreference = new UserPreference(context, null /* attrs */, myUserId,
207                null /* settings icon handler */,
208                null /* delete icon handler */);
209        mMePreference.setKey(KEY_USER_ME);
210        mMePreference.setOnPreferenceClickListener(this);
211        if (mIsOwner) {
212            mMePreference.setSummary(R.string.user_owner);
213        }
214        mAddUser = findPreference(KEY_ADD_USER);
215        if (!mIsOwner || UserManager.getMaxSupportedUsers() < 2
216                || !UserManager.supportsMultipleUsers()
217                || mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
218            removePreference(KEY_ADD_USER);
219        } else {
220            mAddUser.setOnPreferenceClickListener(this);
221            DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
222                    Context.DEVICE_POLICY_SERVICE);
223            // No restricted profiles for tablets with a device owner, or phones.
224            if (dpm.getDeviceOwner() != null || Utils.isVoiceCapable(context)) {
225                mCanAddRestrictedProfile = false;
226                mAddUser.setTitle(R.string.user_add_user_menu);
227            }
228        }
229        loadProfile();
230        setHasOptionsMenu(true);
231        IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
232        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
233        context.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null,
234                mHandler);
235    }
236
237    @Override
238    public void onResume() {
239        super.onResume();
240
241        if (!mEnabled) return;
242
243        loadProfile();
244        updateUserList();
245    }
246
247    @Override
248    public void onDestroy() {
249        super.onDestroy();
250
251        if (!mEnabled) return;
252
253        getActivity().unregisterReceiver(mUserChangeReceiver);
254    }
255
256    @Override
257    public void onSaveInstanceState(Bundle outState) {
258        super.onSaveInstanceState(outState);
259        mEditUserInfoController.onSaveInstanceState(outState);
260        outState.putInt(SAVE_ADDING_USER, mAddedUserId);
261        outState.putInt(SAVE_REMOVING_USER, mRemovingUserId);
262    }
263
264    @Override
265    public void startActivityForResult(Intent intent, int requestCode) {
266        mEditUserInfoController.startingActivityForResult();
267        super.startActivityForResult(intent, requestCode);
268    }
269
270    @Override
271    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
272        int pos = 0;
273        UserManager um = (UserManager) getActivity().getSystemService(Context.USER_SERVICE);
274        if (!mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER)) {
275            String nickname = mUserManager.getUserName();
276            MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
277                    getResources().getString(R.string.user_remove_user_menu, nickname));
278            removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
279        }
280        if (mIsOwner && !um.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
281            MenuItem allowAddOnLockscreen = menu.add(0, MENU_ADD_ON_LOCKSCREEN, pos++,
282                    R.string.user_add_on_lockscreen_menu);
283            allowAddOnLockscreen.setCheckable(true);
284            allowAddOnLockscreen.setChecked(Settings.Global.getInt(getContentResolver(),
285                    Settings.Global.ADD_USERS_WHEN_LOCKED, 0) == 1);
286        }
287        super.onCreateOptionsMenu(menu, inflater);
288    }
289
290    @Override
291    public boolean onOptionsItemSelected(MenuItem item) {
292        final int itemId = item.getItemId();
293        if (itemId == MENU_REMOVE_USER) {
294            onRemoveUserClicked(UserHandle.myUserId());
295            return true;
296        } else if (itemId == MENU_ADD_ON_LOCKSCREEN) {
297            final boolean isChecked = item.isChecked();
298            Settings.Global.putInt(getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED,
299                    isChecked ? 0 : 1);
300            item.setChecked(!isChecked);
301            return true;
302        } else {
303            return super.onOptionsItemSelected(item);
304        }
305    }
306
307    /**
308     * Loads profile information for the current user.
309     */
310    private void loadProfile() {
311        if (mIsGuest) {
312            // No need to load profile information
313            mMePreference.setIcon(getEncircledDefaultIcon());
314            mMePreference.setTitle(R.string.user_exit_guest_title);
315            return;
316        }
317
318        new AsyncTask<Void, Void, String>() {
319            @Override
320            protected void onPostExecute(String result) {
321                finishLoadProfile(result);
322            }
323
324            @Override
325            protected String doInBackground(Void... values) {
326                UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
327                if (user.iconPath == null || user.iconPath.equals("")) {
328                    assignProfilePhoto(user);
329                }
330                return user.name;
331            }
332        }.execute();
333    }
334
335    private void finishLoadProfile(String profileName) {
336        if (getActivity() == null) return;
337        mMePreference.setTitle(getString(R.string.user_you, profileName));
338        int myUserId = UserHandle.myUserId();
339        Bitmap b = mUserManager.getUserIcon(myUserId);
340        if (b != null) {
341            mMePreference.setIcon(encircle(b));
342            mUserIcons.put(myUserId, b);
343        }
344    }
345
346    private boolean hasLockscreenSecurity() {
347        LockPatternUtils lpu = new LockPatternUtils(getActivity());
348        return lpu.isLockPasswordEnabled() || lpu.isLockPatternEnabled();
349    }
350
351    private void launchChooseLockscreen() {
352        Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
353        chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
354                DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
355        startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK);
356    }
357
358    @Override
359    public void onActivityResult(int requestCode, int resultCode, Intent data) {
360        super.onActivityResult(requestCode, resultCode, data);
361
362        if (requestCode == REQUEST_CHOOSE_LOCK) {
363            if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) {
364                addUserNow(USER_TYPE_RESTRICTED_PROFILE);
365            }
366        } else {
367            mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
368        }
369    }
370
371    private void onAddUserClicked(int userType) {
372        synchronized (mUserLock) {
373            if (mRemovingUserId == -1 && !mAddingUser) {
374                switch (userType) {
375                case USER_TYPE_USER:
376                    showDialog(DIALOG_ADD_USER);
377                    break;
378                case USER_TYPE_RESTRICTED_PROFILE:
379                    if (hasLockscreenSecurity()) {
380                        addUserNow(USER_TYPE_RESTRICTED_PROFILE);
381                    } else {
382                        showDialog(DIALOG_NEED_LOCKSCREEN);
383                    }
384                    break;
385                }
386            }
387        }
388    }
389
390    private void onRemoveUserClicked(int userId) {
391        synchronized (mUserLock) {
392            if (mRemovingUserId == -1 && !mAddingUser) {
393                mRemovingUserId = userId;
394                showDialog(DIALOG_CONFIRM_REMOVE);
395            }
396        }
397    }
398
399    private UserInfo createLimitedUser() {
400        UserInfo newUserInfo = mUserManager.createSecondaryUser(
401                getResources().getString(R.string.user_new_profile_name),
402                UserInfo.FLAG_RESTRICTED);
403        int userId = newUserInfo.id;
404        UserHandle user = new UserHandle(userId);
405        mUserManager.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user);
406        // Change the setting before applying the DISALLOW_SHARE_LOCATION restriction, otherwise
407        // the putIntForUser() will fail.
408        Secure.putIntForUser(getContentResolver(),
409                Secure.LOCATION_MODE, Secure.LOCATION_MODE_OFF, userId);
410        mUserManager.setUserRestriction(UserManager.DISALLOW_SHARE_LOCATION, true, user);
411        assignDefaultPhoto(newUserInfo);
412        // Add shared accounts
413        AccountManager am = AccountManager.get(getActivity());
414        Account [] accounts = am.getAccounts();
415        if (accounts != null) {
416            for (Account account : accounts) {
417                am.addSharedAccount(account, user);
418            }
419        }
420        return newUserInfo;
421    }
422
423    private UserInfo createTrustedUser() {
424        UserInfo newUserInfo = mUserManager.createSecondaryUser(
425                getResources().getString(R.string.user_new_user_name), 0);
426        if (newUserInfo != null) {
427            assignDefaultPhoto(newUserInfo);
428        }
429        return newUserInfo;
430    }
431
432    private void onManageUserClicked(int userId, boolean newUser) {
433        if (userId == UserPreference.USERID_GUEST_DEFAULTS) {
434            Bundle extras = new Bundle();
435            extras.putBoolean(UserDetailsSettings.EXTRA_USER_GUEST, true);
436            ((SettingsActivity) getActivity()).startPreferencePanel(
437                    UserDetailsSettings.class.getName(),
438                    extras, R.string.user_guest, null, null, 0);
439            return;
440        }
441        UserInfo info = mUserManager.getUserInfo(userId);
442        if (info.isRestricted() && mIsOwner) {
443            Bundle extras = new Bundle();
444            extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId);
445            extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser);
446            ((SettingsActivity) getActivity()).startPreferencePanel(
447                    RestrictedProfileSettings.class.getName(),
448                    extras, R.string.user_restrictions_title, null,
449                    null, 0);
450        } else if (info.id == UserHandle.myUserId()) {
451            // Jump to owner info panel
452            Bundle extras = new Bundle();
453            if (!info.isRestricted()) {
454                extras.putBoolean(OwnerInfoSettings.EXTRA_SHOW_NICKNAME, true);
455            }
456            int titleResId = info.id == UserHandle.USER_OWNER ? R.string.owner_info_settings_title
457                    : (info.isRestricted() ? R.string.profile_info_settings_title
458                            : R.string.user_info_settings_title);
459            ((SettingsActivity) getActivity()).startPreferencePanel(
460                    OwnerInfoSettings.class.getName(),
461                    extras, titleResId, null, null, 0);
462        } else if (mIsOwner) {
463            Bundle extras = new Bundle();
464            extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId);
465            ((SettingsActivity) getActivity()).startPreferencePanel(
466                    UserDetailsSettings.class.getName(),
467                    extras,
468                    -1, /* No title res id */
469                    info.name, /* title */
470                    null, /* resultTo */
471                    0 /* resultRequestCode */);
472        }
473    }
474
475    private void onUserCreated(int userId) {
476        mAddedUserId = userId;
477        if (mUserManager.getUserInfo(userId).isRestricted()) {
478            showDialog(DIALOG_SETUP_PROFILE);
479        } else {
480            showDialog(DIALOG_SETUP_USER);
481        }
482    }
483
484    @Override
485    public void onDialogShowing() {
486        super.onDialogShowing();
487
488        setOnDismissListener(this);
489    }
490
491    @Override
492    public Dialog onCreateDialog(int dialogId) {
493        Context context = getActivity();
494        if (context == null) return null;
495        switch (dialogId) {
496            case DIALOG_CONFIRM_REMOVE: {
497                Dialog dlg =
498                        Utils.createRemoveConfirmationDialog(getActivity(), mRemovingUserId,
499                                new DialogInterface.OnClickListener() {
500                                    public void onClick(DialogInterface dialog, int which) {
501                                        removeUserNow();
502                                    }
503                                }
504                        );
505                return dlg;
506            }
507            case DIALOG_USER_CANNOT_MANAGE:
508                return new AlertDialog.Builder(context)
509                    .setMessage(R.string.user_cannot_manage_message)
510                    .setPositiveButton(android.R.string.ok, null)
511                    .create();
512            case DIALOG_ADD_USER: {
513                final SharedPreferences preferences = getActivity().getPreferences(
514                        Context.MODE_PRIVATE);
515                final boolean longMessageDisplayed = preferences.getBoolean(
516                        KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false);
517                final int messageResId = longMessageDisplayed
518                        ? R.string.user_add_user_message_short
519                        : R.string.user_add_user_message_long;
520                final int userType = dialogId == DIALOG_ADD_USER
521                        ? USER_TYPE_USER : USER_TYPE_RESTRICTED_PROFILE;
522                Dialog dlg = new AlertDialog.Builder(context)
523                    .setTitle(R.string.user_add_user_title)
524                    .setMessage(messageResId)
525                    .setPositiveButton(android.R.string.ok,
526                        new DialogInterface.OnClickListener() {
527                            public void onClick(DialogInterface dialog, int which) {
528                                addUserNow(userType);
529                                if (!longMessageDisplayed) {
530                                    preferences.edit().putBoolean(
531                                            KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, true).apply();
532                                }
533                            }
534                    })
535                    .setNegativeButton(android.R.string.cancel, null)
536                    .create();
537                return dlg;
538            }
539            case DIALOG_SETUP_USER: {
540                Dialog dlg = new AlertDialog.Builder(context)
541                    .setTitle(R.string.user_setup_dialog_title)
542                    .setMessage(R.string.user_setup_dialog_message)
543                    .setPositiveButton(R.string.user_setup_button_setup_now,
544                        new DialogInterface.OnClickListener() {
545                            public void onClick(DialogInterface dialog, int which) {
546                                switchUserNow(mAddedUserId);
547                            }
548                    })
549                    .setNegativeButton(R.string.user_setup_button_setup_later, null)
550                    .create();
551                return dlg;
552            }
553            case DIALOG_SETUP_PROFILE: {
554                Dialog dlg = new AlertDialog.Builder(context)
555                    .setMessage(R.string.user_setup_profile_dialog_message)
556                    .setPositiveButton(android.R.string.ok,
557                        new DialogInterface.OnClickListener() {
558                            public void onClick(DialogInterface dialog, int which) {
559                                switchUserNow(mAddedUserId);
560                            }
561                    })
562                    .setNegativeButton(android.R.string.cancel, null)
563                    .create();
564                return dlg;
565            }
566            case DIALOG_CHOOSE_USER_TYPE: {
567                List<HashMap<String, String>> data = new ArrayList<HashMap<String,String>>();
568                HashMap<String,String> addUserItem = new HashMap<String,String>();
569                addUserItem.put(KEY_TITLE, getString(R.string.user_add_user_item_title));
570                addUserItem.put(KEY_SUMMARY, getString(R.string.user_add_user_item_summary));
571                HashMap<String,String> addProfileItem = new HashMap<String,String>();
572                addProfileItem.put(KEY_TITLE, getString(R.string.user_add_profile_item_title));
573                addProfileItem.put(KEY_SUMMARY, getString(R.string.user_add_profile_item_summary));
574                data.add(addUserItem);
575                data.add(addProfileItem);
576                Dialog dlg = new AlertDialog.Builder(context)
577                        .setTitle(R.string.user_add_user_type_title)
578                        .setAdapter(new SimpleAdapter(context, data, R.layout.two_line_list_item,
579                                new String[] {KEY_TITLE, KEY_SUMMARY},
580                                new int[] {R.id.title, R.id.summary}),
581                                new DialogInterface.OnClickListener() {
582                                    public void onClick(DialogInterface dialog, int which) {
583                                        onAddUserClicked(which == 0
584                                                ? USER_TYPE_USER
585                                                : USER_TYPE_RESTRICTED_PROFILE);
586                                    }
587                                })
588                        .create();
589                return dlg;
590            }
591            case DIALOG_NEED_LOCKSCREEN: {
592                Dialog dlg = new AlertDialog.Builder(context)
593                        .setMessage(R.string.user_need_lock_message)
594                        .setPositiveButton(R.string.user_set_lock_button,
595                                new DialogInterface.OnClickListener() {
596                                    @Override
597                                    public void onClick(DialogInterface dialog, int which) {
598                                        launchChooseLockscreen();
599                                    }
600                                })
601                        .setNegativeButton(android.R.string.cancel, null)
602                        .create();
603                return dlg;
604            }
605            case DIALOG_CONFIRM_EXIT_GUEST: {
606                Dialog dlg = new AlertDialog.Builder(context)
607                        .setTitle(R.string.user_exit_guest_confirm_title)
608                        .setMessage(R.string.user_exit_guest_confirm_message)
609                        .setPositiveButton(R.string.user_exit_guest_dialog_remove,
610                                new DialogInterface.OnClickListener() {
611                                    @Override
612                                    public void onClick(DialogInterface dialog, int which) {
613                                        exitGuest();
614                                    }
615                                })
616                        .setNegativeButton(android.R.string.cancel, null)
617                        .create();
618                return dlg;
619            }
620            case DIALOG_USER_PROFILE_EDITOR: {
621                Dialog dlg = mEditUserInfoController.createDialog(
622                        (Fragment) this,
623                        mMePreference.getIcon(),
624                        mMePreference.getTitle(),
625                        R.string.profile_info_settings_title,
626                        this /* callback */,
627                        android.os.Process.myUserHandle());
628                return dlg;
629            }
630            default:
631                return null;
632        }
633    }
634
635    private void removeUserNow() {
636        if (mRemovingUserId == UserHandle.myUserId()) {
637            removeThisUser();
638        } else {
639            new Thread() {
640                public void run() {
641                    synchronized (mUserLock) {
642                        mUserManager.removeUser(mRemovingUserId);
643                        mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
644                    }
645                }
646            }.start();
647        }
648    }
649
650    private void removeThisUser() {
651        try {
652            ActivityManagerNative.getDefault().switchUser(UserHandle.USER_OWNER);
653            ((UserManager) getActivity().getSystemService(Context.USER_SERVICE))
654                    .removeUser(UserHandle.myUserId());
655        } catch (RemoteException re) {
656            Log.e(TAG, "Unable to remove self user");
657        }
658    }
659
660    private void addUserNow(final int userType) {
661        synchronized (mUserLock) {
662            mAddingUser = true;
663            //updateUserList();
664            new Thread() {
665                public void run() {
666                    UserInfo user = null;
667                    // Could take a few seconds
668                    if (userType == USER_TYPE_USER) {
669                        user = createTrustedUser();
670                    } else {
671                        user = createLimitedUser();
672                    }
673                    synchronized (mUserLock) {
674                        mAddingUser = false;
675                        if (userType == USER_TYPE_USER) {
676                            mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
677                            mHandler.sendMessage(mHandler.obtainMessage(
678                                    MESSAGE_SETUP_USER, user.id, user.serialNumber));
679                        } else {
680                            mHandler.sendMessage(mHandler.obtainMessage(
681                                    MESSAGE_CONFIG_USER, user.id, user.serialNumber));
682                        }
683                    }
684                }
685            }.start();
686        }
687    }
688
689    private void switchUserNow(int userId) {
690        try {
691            ActivityManagerNative.getDefault().switchUser(userId);
692        } catch (RemoteException re) {
693            // Nothing to do
694        }
695    }
696
697    /**
698     * Erase the current user (guest) and switch to another user.
699     */
700    private void exitGuest() {
701        // Just to be safe
702        if (!mIsGuest) {
703            return;
704        }
705        removeThisUser();
706    }
707
708    private void updateUserList() {
709        if (getActivity() == null) return;
710        List<UserInfo> users = mUserManager.getUsers(true);
711        final Context context = getActivity();
712
713        mUserListCategory.removeAll();
714        mUserListCategory.setOrderingAsAdded(false);
715        mUserListCategory.addPreference(mMePreference);
716
717        final boolean voiceCapable = Utils.isVoiceCapable(context);
718        final ArrayList<Integer> missingIcons = new ArrayList<Integer>();
719        for (UserInfo user : users) {
720            if (user.isManagedProfile()) {
721                // Managed profiles appear under Accounts Settings instead
722                continue;
723            }
724            Preference pref;
725            if (user.id == UserHandle.myUserId()) {
726                pref = mMePreference;
727            } else if (user.isGuest()) {
728                // Skip over Guest. We add generic Guest settings after this loop
729                continue;
730            } else {
731                // With Telephony:
732                //   Secondary user: Settings
733                //   Guest: Settings
734                //   Restricted Profile: There is no Restricted Profile
735                // Without Telephony:
736                //   Secondary user: Delete
737                //   Guest: Nothing
738                //   Restricted Profile: Settings
739                final boolean showSettings = mIsOwner && (voiceCapable || user.isRestricted());
740                final boolean showDelete = mIsOwner
741                        && (!voiceCapable && !user.isRestricted() && !user.isGuest());
742                pref = new UserPreference(context, null, user.id,
743                        showSettings ? this : null,
744                        showDelete ? this : null);
745                pref.setOnPreferenceClickListener(this);
746                pref.setKey("id=" + user.id);
747                mUserListCategory.addPreference(pref);
748                if (user.id == UserHandle.USER_OWNER) {
749                    pref.setSummary(R.string.user_owner);
750                }
751                pref.setTitle(user.name);
752            }
753            if (!isInitialized(user)) {
754                if (user.isRestricted()) {
755                    pref.setSummary(R.string.user_summary_restricted_not_set_up);
756                } else {
757                    pref.setSummary(R.string.user_summary_not_set_up);
758                }
759            } else if (user.isRestricted()) {
760                pref.setSummary(R.string.user_summary_restricted_profile);
761            }
762            if (user.iconPath != null) {
763                if (mUserIcons.get(user.id) == null) {
764                    // Icon not loaded yet, print a placeholder
765                    missingIcons.add(user.id);
766                    pref.setIcon(getEncircledDefaultIcon());
767                } else {
768                    setPhotoId(pref, user);
769                }
770            } else {
771                // Icon not available yet, print a placeholder
772                pref.setIcon(getEncircledDefaultIcon());
773            }
774        }
775
776        // Add a temporary entry for the user being created
777        if (mAddingUser) {
778            Preference pref = new UserPreference(getActivity(), null, UserPreference.USERID_UNKNOWN,
779                    null, null);
780            pref.setEnabled(false);
781            pref.setTitle(R.string.user_new_user_name);
782            pref.setIcon(getEncircledDefaultIcon());
783            mUserListCategory.addPreference(pref);
784        }
785
786        boolean showGuestPreference = !mIsGuest;
787        // If user has DISALLOW_ADD_USER don't allow creating a guest either.
788        if (showGuestPreference && mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)) {
789            showGuestPreference = false;
790            // If guest already exists, no user creation needed.
791            for (UserInfo user : users) {
792                if (user.isGuest()) {
793                    showGuestPreference = true;
794                    break;
795                }
796            }
797        }
798        if (showGuestPreference) {
799            // Add a virtual Guest user for guest defaults
800            Preference pref = new UserPreference(getActivity(), null,
801                    UserPreference.USERID_GUEST_DEFAULTS,
802                    mIsOwner && voiceCapable? this : null /* settings icon handler */,
803                    null /* delete icon handler */);
804            pref.setTitle(R.string.user_guest);
805            pref.setIcon(getEncircledDefaultIcon());
806            pref.setOnPreferenceClickListener(this);
807            mUserListCategory.addPreference(pref);
808        }
809
810        getActivity().invalidateOptionsMenu();
811
812        // Load the icons
813        if (missingIcons.size() > 0) {
814            loadIconsAsync(missingIcons);
815        }
816        boolean moreUsers = mUserManager.canAddMoreUsers();
817        mAddUser.setEnabled(moreUsers);
818    }
819
820    private void loadIconsAsync(List<Integer> missingIcons) {
821        final Resources resources = getResources();
822        new AsyncTask<List<Integer>, Void, Void>() {
823            @Override
824            protected void onPostExecute(Void result) {
825                updateUserList();
826            }
827
828            @Override
829            protected Void doInBackground(List<Integer>... values) {
830                for (int userId : values[0]) {
831                    Bitmap bitmap = mUserManager.getUserIcon(userId);
832                    if (bitmap == null) {
833                        bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(userId,
834                                /* light= */ false));
835                    }
836                    mUserIcons.append(userId, bitmap);
837                }
838                return null;
839            }
840        }.execute(missingIcons);
841    }
842
843    private void assignProfilePhoto(final UserInfo user) {
844        if (!Utils.copyMeProfilePhoto(getActivity(), user)) {
845            assignDefaultPhoto(user);
846        }
847    }
848
849    private void assignDefaultPhoto(UserInfo user) {
850        Bitmap bitmap = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(user.id,
851                /* light= */ false));
852        mUserManager.setUserIcon(user.id, bitmap);
853    }
854
855    private Drawable getEncircledDefaultIcon() {
856        if (mDefaultIconDrawable == null) {
857            mDefaultIconDrawable = encircle(UserIcons.convertToBitmap(
858                    UserIcons.getDefaultUserIcon(UserHandle.USER_NULL, /* light= */ false)));
859        }
860        return mDefaultIconDrawable;
861    }
862
863    private void setPhotoId(Preference pref, UserInfo user) {
864        Bitmap bitmap = mUserIcons.get(user.id);
865        if (bitmap != null) {
866            pref.setIcon(encircle(bitmap));
867        }
868    }
869
870    private void setUserName(String name) {
871        mUserManager.setUserName(UserHandle.myUserId(), name);
872        mNicknamePreference.setSummary(name);
873        getActivity().invalidateOptionsMenu();
874    }
875
876    @Override
877    public boolean onPreferenceClick(Preference pref) {
878        if (pref == mMePreference) {
879            if (mIsGuest) {
880                showDialog(DIALOG_CONFIRM_EXIT_GUEST);
881                return true;
882            }
883            // If this is a limited user, launch the user info settings instead of profile editor
884            if (mUserManager.isLinkedUser()) {
885                onManageUserClicked(UserHandle.myUserId(), false);
886            } else {
887                showDialog(DIALOG_USER_PROFILE_EDITOR);
888            }
889        } else if (pref instanceof UserPreference) {
890            int userId = ((UserPreference) pref).getUserId();
891            if (userId == UserPreference.USERID_GUEST_DEFAULTS) {
892                createAndSwitchToGuestUser();
893            } else {
894                // Get the latest status of the user
895                UserInfo user = mUserManager.getUserInfo(userId);
896                if (!isInitialized(user)) {
897                    mHandler.sendMessage(mHandler.obtainMessage(
898                            MESSAGE_SETUP_USER, user.id, user.serialNumber));
899                } else {
900                    switchUserNow(userId);
901                }
902            }
903        } else if (pref == mAddUser) {
904            // If we allow both types, show a picker, otherwise directly go to
905            // flow for full user.
906            if (mCanAddRestrictedProfile) {
907                showDialog(DIALOG_CHOOSE_USER_TYPE);
908            } else {
909                onAddUserClicked(USER_TYPE_USER);
910            }
911        }
912        return false;
913    }
914
915    private void createAndSwitchToGuestUser() {
916        List<UserInfo> users = mUserManager.getUsers();
917        for (UserInfo user : users) {
918            if (user.isGuest()) {
919                switchUserNow(user.id);
920                return;
921            }
922        }
923        // No guest user. Create one, if there's no restriction.
924        // If it is not the primary user, then adding users from lockscreen must be enabled
925        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_ADD_USER)
926                || (!mIsOwner && Settings.Global.getInt(getContentResolver(),
927                        Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 1)) {
928            Log.i(TAG, "Blocking guest creation because it is restricted");
929            return;
930        }
931        UserInfo guestUser = mUserManager.createGuest(getActivity(),
932                    getResources().getString(R.string.user_guest));
933        if (guestUser != null) {
934            switchUserNow(guestUser.id);
935        }
936    }
937
938    private boolean isInitialized(UserInfo user) {
939        return (user.flags & UserInfo.FLAG_INITIALIZED) != 0;
940    }
941
942    private Drawable encircle(Bitmap icon) {
943        Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon);
944        return circled;
945    }
946
947    @Override
948    public void onClick(View v) {
949        if (v.getTag() instanceof UserPreference) {
950            int userId = ((UserPreference) v.getTag()).getUserId();
951            switch (v.getId()) {
952            case UserPreference.DELETE_ID:
953                onRemoveUserClicked(userId);
954                break;
955            case UserPreference.SETTINGS_ID:
956                onManageUserClicked(userId, false);
957                break;
958            }
959        }
960    }
961
962    @Override
963    public void onDismiss(DialogInterface dialog) {
964        synchronized (mUserLock) {
965            mAddingUser = false;
966            mRemovingUserId = -1;
967            updateUserList();
968        }
969    }
970
971    @Override
972    public boolean onPreferenceChange(Preference preference, Object newValue) {
973        if (preference == mNicknamePreference) {
974            String value = (String) newValue;
975            if (preference == mNicknamePreference && value != null
976                    && value.length() > 0) {
977                setUserName(value);
978            }
979            return true;
980        }
981        return false;
982    }
983
984    @Override
985    public int getHelpResource() {
986        return R.string.help_url_users;
987    }
988
989    @Override
990    public void onPhotoChanged(Drawable photo) {
991        mMePreference.setIcon(photo);
992    }
993
994    @Override
995    public void onLabelChanged(CharSequence label) {
996        mMePreference.setTitle(label);
997    }
998}
999