diff --git a/res/drawable/ic_add_40dp.xml b/res/drawable/ic_add_40dp.xml deleted file mode 100644 index 7245823749b..00000000000 --- a/res/drawable/ic_add_40dp.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - diff --git a/res/drawable/ic_phone.xml b/res/drawable/ic_phone.xml deleted file mode 100644 index 28f47fd3486..00000000000 --- a/res/drawable/ic_phone.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/drawable/ic_swap.xml b/res/drawable/ic_swap.xml deleted file mode 100644 index 1c43d974428..00000000000 --- a/res/drawable/ic_swap.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - diff --git a/res/layout/restricted_preference_user_delete_widget.xml b/res/layout/restricted_preference_user_delete_widget.xml new file mode 100644 index 00000000000..71f1dd7c8bf --- /dev/null +++ b/res/layout/restricted_preference_user_delete_widget.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/user_info_header.xml b/res/layout/user_info_header.xml index bfdf3fc7324..5135e0e2d27 100644 --- a/res/layout/user_info_header.xml +++ b/res/layout/user_info_header.xml @@ -15,7 +15,6 @@ --> + android:layout_marginEnd="@*android:dimen/preference_item_padding_inner" + /> @@ -89,40 +88,6 @@ android:layout_gravity="center" android:background="?android:attr/selectableItemBackground" /> - - - - - - - - - diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml index d336395d02a..09154c483d7 100644 --- a/res/xml/user_details_settings.xml +++ b/res/xml/user_details_settings.xml @@ -17,17 +17,12 @@ - - diff --git a/res/xml/user_settings.xml b/res/xml/user_settings.xml index 7726a18ea94..eb8803b73ee 100644 --- a/res/xml/user_settings.xml +++ b/res/xml/user_settings.xml @@ -27,15 +27,10 @@ settings:searchable="false"> - - switchUser()); - // This is going to bind the preferences. super.onActivityCreated(savedInstanceState); } @@ -92,6 +80,7 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment @Override public void onResume() { super.onResume(); + // Check if user still exists UserInfo info = Utils.getExistingUser(mUserManager, mUser); if (info == null) { @@ -100,16 +89,6 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment ((TextView) mHeaderView.findViewById(android.R.id.title)).setText(info.name); ((ImageView) mHeaderView.findViewById(android.R.id.icon)).setImageDrawable( com.android.settingslib.Utils.getUserIcon(getActivity(), mUserManager, info)); - - boolean canSwitchUser = - mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; - if (mShowSwitchUser && canSwitchUser) { - mSwitchUserView.setVisibility(View.VISIBLE); - mSwitchTitle.setText(getString(com.android.settingslib.R.string.user_switch_to_user, - info.name)); - } else { - mSwitchUserView.setVisibility(View.GONE); - } } } @@ -179,16 +158,6 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment }); } - private void switchUser() { - try { - ActivityManager.getService().switchUser(mUser.getIdentifier()); - } catch (RemoteException re) { - Log.e(TAG, "Error while switching to other user."); - } finally { - finishFragment(); - } - } - @Override public void onPhotoChanged(UserHandle user, Drawable photo) { mUserIconView.setImageDrawable(photo); diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index 2696ddc1319..371c152e157 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -16,63 +16,55 @@ package com.android.settings.users; -import static android.os.UserHandle.USER_NULL; - -import android.app.ActivityManager; import android.app.Dialog; import android.app.settings.SettingsEnums; import android.content.Context; +import android.content.DialogInterface; import android.content.pm.UserInfo; import android.os.Bundle; -import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.util.Log; -import androidx.annotation.VisibleForTesting; import androidx.preference.Preference; import androidx.preference.SwitchPreference; import com.android.settings.R; import com.android.settings.SettingsPreferenceFragment; -import com.android.settings.Utils; -import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtilsInternal; import java.util.List; /** - * Settings screen for configuring, deleting or switching to a specific user. - * It is shown when you tap on a user in the user management (UserSettings) screen. + * Settings screen for configuring a specific user. It can contain user restrictions + * and deletion controls. It is shown when you tap on the settings icon in the + * user management (UserSettings) screen. * * Arguments to this fragment must include the userId of the user (in EXTRA_USER_ID) for whom - * to display controls. + * to display controls, or should contain the EXTRA_USER_GUEST = true. */ public class UserDetailsSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { private static final String TAG = UserDetailsSettings.class.getSimpleName(); - private static final String KEY_SWITCH_USER = "switch_user"; private static final String KEY_ENABLE_TELEPHONY = "enable_calling"; private static final String KEY_REMOVE_USER = "remove_user"; /** Integer extra containing the userId to manage */ static final String EXTRA_USER_ID = "user_id"; + /** Boolean extra to indicate guest preferences */ + static final String EXTRA_USER_GUEST = "guest_user"; private static final int DIALOG_CONFIRM_REMOVE = 1; private static final int DIALOG_CONFIRM_ENABLE_CALLING = 2; private static final int DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS = 3; private UserManager mUserManager; - @VisibleForTesting - Preference mSwitchUserPref; private SwitchPreference mPhonePref; - @VisibleForTesting - Preference mRemoveUserPref; + private Preference mRemoveUserPref; - @VisibleForTesting - UserInfo mUserInfo; + private UserInfo mUserInfo; + private boolean mGuestUser; private Bundle mDefaultGuestRestrictions; @Override @@ -86,28 +78,46 @@ public class UserDetailsSettings extends SettingsPreferenceFragment final Context context = getActivity(); mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE); + addPreferencesFromResource(R.xml.user_details_settings); + mPhonePref = (SwitchPreference) findPreference(KEY_ENABLE_TELEPHONY); + mRemoveUserPref = findPreference(KEY_REMOVE_USER); - initialize(context, getArguments()); - } + mGuestUser = getArguments().getBoolean(EXTRA_USER_GUEST, false); - @Override - public void onResume() { - super.onResume(); - mSwitchUserPref.setEnabled(canSwitchUserNow()); + if (!mGuestUser) { + // Regular user. Get the user id from the caller. + final int userId = getArguments().getInt(EXTRA_USER_ID, -1); + if (userId == -1) { + throw new RuntimeException("Arguments to this fragment must contain the user id"); + } + mUserInfo = mUserManager.getUserInfo(userId); + mPhonePref.setChecked(!mUserManager.hasUserRestriction( + UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); + mRemoveUserPref.setOnPreferenceClickListener(this); + } else { + // These are not for an existing user, just general Guest settings. + removePreference(KEY_REMOVE_USER); + // Default title is for calling and SMS. Change to calling-only here + mPhonePref.setTitle(R.string.user_enable_calling); + mDefaultGuestRestrictions = mUserManager.getDefaultGuestRestrictions(); + mPhonePref.setChecked( + !mDefaultGuestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); + } + if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, + UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) { + removePreference(KEY_REMOVE_USER); + } + mPhonePref.setOnPreferenceChangeListener(this); } @Override public boolean onPreferenceClick(Preference preference) { if (preference == mRemoveUserPref) { - if (canDeleteUser()) { - showDialog(DIALOG_CONFIRM_REMOVE); - } - return true; - } else if (preference == mSwitchUserPref) { - if (canSwitchUserNow()) { - switchUser(); + if (!mUserManager.isAdminUser()) { + throw new RuntimeException("Only admins can remove a user"); } + showDialog(DIALOG_CONFIRM_REMOVE); return true; } return false; @@ -116,7 +126,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment @Override public boolean onPreferenceChange(Preference preference, Object newValue) { if (Boolean.TRUE.equals(newValue)) { - showDialog(mUserInfo.isGuest() ? DIALOG_CONFIRM_ENABLE_CALLING + showDialog(mGuestUser ? DIALOG_CONFIRM_ENABLE_CALLING : DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS); return false; } @@ -124,135 +134,9 @@ public class UserDetailsSettings extends SettingsPreferenceFragment return true; } - @Override - public int getDialogMetricsCategory(int dialogId) { - switch (dialogId) { - case DIALOG_CONFIRM_REMOVE: - return SettingsEnums.DIALOG_USER_REMOVE; - case DIALOG_CONFIRM_ENABLE_CALLING: - return SettingsEnums.DIALOG_USER_ENABLE_CALLING; - case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: - return SettingsEnums.DIALOG_USER_ENABLE_CALLING_AND_SMS; - default: - return 0; - } - } - - @Override - public Dialog onCreateDialog(int dialogId) { - Context context = getActivity(); - if (context == null) { - return null; - } - switch (dialogId) { - case DIALOG_CONFIRM_REMOVE: - return UserDialogs.createRemoveDialog(getActivity(), mUserInfo.id, - (dialog, which) -> removeUser()); - case DIALOG_CONFIRM_ENABLE_CALLING: - return UserDialogs.createEnablePhoneCallsDialog(getActivity(), - (dialog, which) -> enableCallsAndSms(true)); - case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: - return UserDialogs.createEnablePhoneCallsAndSmsDialog(getActivity(), - (dialog, which) -> enableCallsAndSms(true)); - } - throw new IllegalArgumentException("Unsupported dialogId " + dialogId); - } - - @VisibleForTesting - @Override - protected void showDialog(int dialogId) { - super.showDialog(dialogId); - } - - @VisibleForTesting - void initialize(Context context, Bundle arguments) { - int userId = arguments != null ? arguments.getInt(EXTRA_USER_ID, USER_NULL) : USER_NULL; - if (userId == USER_NULL) { - throw new IllegalStateException("Arguments to this fragment must contain the user id"); - } - mUserInfo = mUserManager.getUserInfo(userId); - - mSwitchUserPref = findPreference(KEY_SWITCH_USER); - mPhonePref = findPreference(KEY_ENABLE_TELEPHONY); - mRemoveUserPref = findPreference(KEY_REMOVE_USER); - - mSwitchUserPref.setTitle( - context.getString(com.android.settingslib.R.string.user_switch_to_user, - mUserInfo.name)); - mSwitchUserPref.setOnPreferenceClickListener(this); - - if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls - removePreference(KEY_ENABLE_TELEPHONY); - removePreference(KEY_REMOVE_USER); - } else { - if (!Utils.isVoiceCapable(context)) { // no telephony - removePreference(KEY_ENABLE_TELEPHONY); - } - - if (!mUserInfo.isGuest()) { - mPhonePref.setChecked(!mUserManager.hasUserRestriction( - UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); - mRemoveUserPref.setTitle(R.string.user_remove_user); - } else { - // These are not for an existing user, just general Guest settings. - // Default title is for calling and SMS. Change to calling-only here - mPhonePref.setTitle(R.string.user_enable_calling); - mDefaultGuestRestrictions = mUserManager.getDefaultGuestRestrictions(); - mPhonePref.setChecked( - !mDefaultGuestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS)); - mRemoveUserPref.setTitle(R.string.user_exit_guest_title); - } - if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, - UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) { - removePreference(KEY_REMOVE_USER); - } - - mRemoveUserPref.setOnPreferenceClickListener(this); - mPhonePref.setOnPreferenceChangeListener(this); - } - } - - @VisibleForTesting - boolean canDeleteUser() { - if (!mUserManager.isAdminUser()) { - return false; - } - - Context context = getActivity(); - if (context == null) { - return false; - } - - final RestrictedLockUtils.EnforcedAdmin removeDisallowedAdmin = - RestrictedLockUtilsInternal.checkIfRestrictionEnforced(context, - UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); - if (removeDisallowedAdmin != null) { - RestrictedLockUtils.sendShowAdminSupportDetailsIntent(context, - removeDisallowedAdmin); - return false; - } - return true; - } - - @VisibleForTesting - boolean canSwitchUserNow() { - return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; - } - - @VisibleForTesting - void switchUser() { - try { - ActivityManager.getService().switchUser(mUserInfo.id); - } catch (RemoteException re) { - Log.e(TAG, "Error while switching to other user."); - } finally { - finishFragment(); - } - } - - private void enableCallsAndSms(boolean enabled) { + void enableCallsAndSms(boolean enabled) { mPhonePref.setChecked(enabled); - if (mUserInfo.isGuest()) { + if (mGuestUser) { mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, !enabled); // SMS is always disabled for guest mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true); @@ -262,7 +146,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment // TODO: Maybe setDefaultGuestRestrictions() can internally just set the restrictions // on any existing guest rather than do it here with multiple Binder calls. List users = mUserManager.getUsers(true); - for (UserInfo user : users) { + for (UserInfo user: users) { if (user.isGuest()) { UserHandle userHandle = UserHandle.of(user.id); for (String key : mDefaultGuestRestrictions.keySet()) { @@ -279,7 +163,51 @@ public class UserDetailsSettings extends SettingsPreferenceFragment } } - private void removeUser() { + @Override + public Dialog onCreateDialog(int dialogId) { + Context context = getActivity(); + if (context == null) return null; + switch (dialogId) { + case DIALOG_CONFIRM_REMOVE: + return UserDialogs.createRemoveDialog(getActivity(), mUserInfo.id, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + removeUser(); + } + }); + case DIALOG_CONFIRM_ENABLE_CALLING: + return UserDialogs.createEnablePhoneCallsDialog(getActivity(), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + enableCallsAndSms(true); + } + }); + case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: + return UserDialogs.createEnablePhoneCallsAndSmsDialog(getActivity(), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + enableCallsAndSms(true); + } + }); + } + throw new IllegalArgumentException("Unsupported dialogId " + dialogId); + } + + @Override + public int getDialogMetricsCategory(int dialogId) { + switch (dialogId) { + case DIALOG_CONFIRM_REMOVE: + return SettingsEnums.DIALOG_USER_REMOVE; + case DIALOG_CONFIRM_ENABLE_CALLING: + return SettingsEnums.DIALOG_USER_ENABLE_CALLING; + case DIALOG_CONFIRM_ENABLE_CALLING_AND_SMS: + return SettingsEnums.DIALOG_USER_ENABLE_CALLING_AND_SMS; + default: + return 0; + } + } + + void removeUser() { mUserManager.removeUser(mUserInfo.id); finishFragment(); } diff --git a/src/com/android/settings/users/UserPreference.java b/src/com/android/settings/users/UserPreference.java index 0b78d787608..3603d44ea09 100644 --- a/src/com/android/settings/users/UserPreference.java +++ b/src/com/android/settings/users/UserPreference.java @@ -21,16 +21,18 @@ import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserManager; import android.util.AttributeSet; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.ImageView; import androidx.preference.PreferenceViewHolder; +import com.android.settings.R; +import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; import java.util.Comparator; -/** - * Preference for a user that appear on {@link UserSettings} screen. - */ public class UserPreference extends RestrictedPreference { private static final int ALPHA_ENABLED = 255; private static final int ALPHA_DISABLED = 102; @@ -42,7 +44,8 @@ public class UserPreference extends RestrictedPreference { if (p1 == null) { return -1; - } else if (p2 == null) { + } + else if (p2 == null) { return 1; } int sn1 = p1.getSerialNumber(); @@ -55,15 +58,26 @@ public class UserPreference extends RestrictedPreference { return 0; }; + private OnClickListener mDeleteClickListener; + private OnClickListener mSettingsClickListener; private int mSerialNumber = -1; private int mUserId = USERID_UNKNOWN; + static final int SETTINGS_ID = R.id.manage_user; + static final int DELETE_ID = R.id.trash_user; public UserPreference(Context context, AttributeSet attrs) { - this(context, attrs, USERID_UNKNOWN); + this(context, attrs, USERID_UNKNOWN, null, null); } - UserPreference(Context context, AttributeSet attrs, int userId) { + UserPreference(Context context, AttributeSet attrs, int userId, + OnClickListener settingsListener, + OnClickListener deleteListener) { super(context, attrs); + if (deleteListener != null || settingsListener != null) { + setWidgetLayoutResource(R.layout.restricted_preference_user_delete_widget); + } + mDeleteClickListener = deleteListener; + mSettingsClickListener = settingsListener; mUserId = userId; useAdminDisabledSummary(true); } @@ -78,13 +92,62 @@ public class UserPreference extends RestrictedPreference { @Override protected boolean shouldHideSecondTarget() { - return true; + if (isDisabledByAdmin()) { + // Disabled by admin, show no secondary target. + return true; + } + if (canDeleteUser()) { + // Need to show delete user target so don't hide. + return false; + } + // Hide if don't have advanced setting listener. + return mSettingsClickListener == null; } @Override public void onBindViewHolder(PreferenceViewHolder view) { super.onBindViewHolder(view); - dimIcon(isDisabledByAdmin()); + final boolean disabledByAdmin = isDisabledByAdmin(); + dimIcon(disabledByAdmin); + View userDeleteWidget = view.findViewById(R.id.user_delete_widget); + if (userDeleteWidget != null) { + userDeleteWidget.setVisibility(disabledByAdmin ? View.GONE : View.VISIBLE); + } + if (!disabledByAdmin) { + View deleteDividerView = view.findViewById(R.id.divider_delete); + View manageDividerView = view.findViewById(R.id.divider_manage); + View deleteView = view.findViewById(R.id.trash_user); + if (deleteView != null) { + if (canDeleteUser()) { + deleteView.setVisibility(View.VISIBLE); + deleteDividerView.setVisibility(View.VISIBLE); + deleteView.setOnClickListener(mDeleteClickListener); + deleteView.setTag(this); + } else { + deleteView.setVisibility(View.GONE); + deleteDividerView.setVisibility(View.GONE); + } + } + ImageView manageView = (ImageView) view.findViewById(R.id.manage_user); + if (manageView != null) { + if (mSettingsClickListener != null) { + manageView.setVisibility(View.VISIBLE); + manageDividerView.setVisibility(mDeleteClickListener == null + ? View.VISIBLE : View.GONE); + manageView.setOnClickListener(mSettingsClickListener); + manageView.setTag(this); + } else { + manageView.setVisibility(View.GONE); + manageDividerView.setVisibility(View.GONE); + } + } + } + } + + private boolean canDeleteUser() { + return mDeleteClickListener != null + && !RestrictedLockUtilsInternal.hasBaseUserRestriction(getContext(), + UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); } private int getSerialNumber() { diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 7d4ab5d5312..38ef199c5cb 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -48,6 +48,7 @@ import android.util.SparseArray; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; +import android.view.View; import android.widget.SimpleAdapter; import androidx.annotation.VisibleForTesting; @@ -68,6 +69,7 @@ import com.android.settings.password.ChooseLockGeneric; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.widget.SwitchBar; import com.android.settings.widget.SwitchBarController; +import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.RestrictedPreference; @@ -87,14 +89,15 @@ import java.util.Random; /** * Screen that manages the list of users on the device. - * Secondary users and a guest user can be created if there is no restriction. + * Guest user is an always visible entry, even if the guest is not currently + * active/created. It is meant for controlling properties of a guest user. * - * The first user in the list is always the current user. + * The first one is always the current user. * Owner is the primary user. */ @SearchIndexable public class UserSettings extends SettingsPreferenceFragment - implements Preference.OnPreferenceClickListener, + implements Preference.OnPreferenceClickListener, View.OnClickListener, MultiUserSwitchBarController.OnMultiUserSwitchChangedListener, DialogInterface.OnDismissListener { @@ -108,7 +111,6 @@ public class UserSettings extends SettingsPreferenceFragment private static final String KEY_USER_LIST = "user_list"; private static final String KEY_USER_ME = "user_me"; private static final String KEY_USER_GUEST = "user_guest"; - private static final String KEY_ADD_GUEST = "guest_add"; private static final String KEY_ADD_USER = "user_add"; private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked"; private static final String KEY_MULTIUSER_FOOTER = "multiuser_footer"; @@ -154,11 +156,7 @@ public class UserSettings extends SettingsPreferenceFragment @VisibleForTesting UserPreference mMePreference; @VisibleForTesting - RestrictedPreference mAddGuest; - @VisibleForTesting RestrictedPreference mAddUser; - @VisibleForTesting - SparseArray mUserIcons = new SparseArray<>(); private int mRemovingUserId = -1; private int mAddedUserId = 0; private boolean mAddingUser; @@ -167,6 +165,7 @@ public class UserSettings extends SettingsPreferenceFragment private boolean mShouldUpdateUserList = true; private final Object mUserLock = new Object(); private UserManager mUserManager; + private SparseArray mUserIcons = new SparseArray<>(); private static SparseArray sDarkDefaultUserBitmapCache = new SparseArray<>(); private MultiUserSwitchBarController mSwitchBarController; @@ -272,17 +271,15 @@ public class UserSettings extends SettingsPreferenceFragment final int myUserId = UserHandle.myUserId(); mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST); - mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId); + mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId, + null /* settings icon handler */, + null /* delete icon handler */); mMePreference.setKey(KEY_USER_ME); mMePreference.setOnPreferenceClickListener(this); if (mUserCaps.mIsAdmin) { mMePreference.setSummary(R.string.user_admin); } - - mAddGuest = findPreference(KEY_ADD_GUEST); - mAddGuest.setOnPreferenceClickListener(this); - - mAddUser = findPreference(KEY_ADD_USER); + mAddUser = (RestrictedPreference) findPreference(KEY_ADD_USER); if (!mUserCaps.mCanAddRestrictedProfile) { // Label should only mention adding a "user", not a "profile" mAddUser.setTitle(R.string.user_add_user_menu); @@ -347,7 +344,8 @@ public class UserSettings extends SettingsPreferenceFragment @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { int pos = 0; - if (!mUserCaps.mIsAdmin && canSwitchUserNow()) { + final boolean canSwitchUsers = mUserManager.canSwitchUsers(); + if (!mUserCaps.mIsAdmin && canSwitchUsers) { String nickname = mUserManager.getUserName(); MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++, getResources().getString(R.string.user_remove_user_menu, nickname)); @@ -388,13 +386,10 @@ public class UserSettings extends SettingsPreferenceFragment * Loads profile information for the current user. */ private void loadProfile() { - if (isCurrentUserGuest()) { + if (mUserCaps.mIsGuest) { // No need to load profile information mMePreference.setIcon(getEncircledDefaultIcon()); mMePreference.setTitle(R.string.user_exit_guest_title); - mMePreference.setSelectable(true); - // removing a guest will result in switching back to the admin user - mMePreference.setEnabled(canSwitchUserNow()); return; } @@ -417,9 +412,7 @@ public class UserSettings extends SettingsPreferenceFragment } private void finishLoadProfile(String profileName) { - if (getActivity() == null) { - return; - } + if (getActivity() == null) return; mMePreference.setTitle(getString(R.string.user_you, profileName)); int myUserId = UserHandle.myUserId(); Bitmap b = mUserManager.getUserIcon(myUserId); @@ -484,28 +477,38 @@ public class UserSettings extends SettingsPreferenceFragment private void onManageUserClicked(int userId, boolean newUser) { mAddingUser = false; - UserInfo userInfo = mUserManager.getUserInfo(userId); - if (userInfo.isRestricted() && mUserCaps.mIsAdmin) { + if (userId == UserPreference.USERID_GUEST_DEFAULTS) { + Bundle extras = new Bundle(); + extras.putBoolean(UserDetailsSettings.EXTRA_USER_GUEST, true); + new SubSettingLauncher(getContext()) + .setDestination(UserDetailsSettings.class.getName()) + .setArguments(extras) + .setTitleRes(R.string.user_guest) + .setSourceMetricsCategory(getMetricsCategory()) + .launch(); + return; + } + UserInfo info = mUserManager.getUserInfo(userId); + if (info.isRestricted() && mUserCaps.mIsAdmin) { Bundle extras = new Bundle(); extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId); extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser); - extras.putBoolean(RestrictedProfileSettings.EXTRA_SHOW_SWITCH_USER, canSwitchUserNow()); new SubSettingLauncher(getContext()) .setDestination(RestrictedProfileSettings.class.getName()) .setArguments(extras) .setTitleRes(R.string.user_restrictions_title) .setSourceMetricsCategory(getMetricsCategory()) .launch(); - } else if (userId == UserHandle.myUserId()) { + } else if (info.id == UserHandle.myUserId()) { // Jump to owner info panel OwnerInfoSettings.show(this); - } else { - Bundle extras = new Bundle(); + } else if (mUserCaps.mIsAdmin) { + final Bundle extras = new Bundle(); extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId); new SubSettingLauncher(getContext()) .setDestination(UserDetailsSettings.class.getName()) .setArguments(extras) - .setTitleText(userInfo.name) + .setTitleText(info.name) .setSourceMetricsCategory(getMetricsCategory()) .launch(); } @@ -535,9 +538,7 @@ public class UserSettings extends SettingsPreferenceFragment @Override public Dialog onCreateDialog(int dialogId) { Context context = getActivity(); - if (context == null) { - return null; - } + if (context == null) return null; switch (dialogId) { case DIALOG_CONFIRM_REMOVE: { Dialog dlg = @@ -810,7 +811,7 @@ public class UserSettings extends SettingsPreferenceFragment } private void removeThisUser() { - if (!canSwitchUserNow()) { + if (!mUserManager.canSwitchUsers()) { Log.w(TAG, "Cannot remove current user when switching is disabled"); return; } @@ -881,14 +882,10 @@ public class UserSettings extends SettingsPreferenceFragment } private void switchUserNow(int userId) { - if (!canSwitchUserNow()) { - return; - } - try { ActivityManager.getService().switchUser(userId); } catch (RemoteException re) { - Log.e(TAG, "Error while switching to other user."); + // Nothing to do } } @@ -897,7 +894,7 @@ public class UserSettings extends SettingsPreferenceFragment */ private void exitGuest() { // Just to be safe - if (!isCurrentUserGuest()) { + if (!mUserCaps.mIsGuest) { return; } removeThisUser(); @@ -911,12 +908,12 @@ public class UserSettings extends SettingsPreferenceFragment } final List users = mUserManager.getUsers(true); + final boolean voiceCapable = Utils.isVoiceCapable(context); final ArrayList missingIcons = new ArrayList<>(); final ArrayList userPreferences = new ArrayList<>(); + int guestId = UserPreference.USERID_GUEST_DEFAULTS; userPreferences.add(mMePreference); - boolean canOpenUserDetails = - mUserCaps.mIsAdmin || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser); for (UserInfo user : users) { if (!user.supportsSwitchToByUser()) { // Only users that can be switched to should show up here. @@ -927,38 +924,37 @@ public class UserSettings extends SettingsPreferenceFragment if (user.id == UserHandle.myUserId()) { pref = mMePreference; } else if (user.isGuest()) { - pref = new UserPreference(getPrefContext(), null, user.id); - pref.setTitle(R.string.user_guest); - pref.setIcon(getEncircledDefaultIcon()); - pref.setKey(KEY_USER_GUEST); - userPreferences.add(pref); - pref.setEnabled(canOpenUserDetails); - pref.setSelectable(true); - - if (mUserCaps.mDisallowSwitchUser) { - pref.setDisabledByAdmin(RestrictedLockUtilsInternal.getDeviceOwner(context)); - } else { - pref.setDisabledByAdmin(null); - } - pref.setOnPreferenceClickListener(this); + // Skip over Guest. We add generic Guest settings after this loop + guestId = user.id; + continue; } else { - pref = new UserPreference(getPrefContext(), null, user.id); + // With Telephony: + // Secondary user: Settings + // Guest: Settings + // Restricted Profile: There is no Restricted Profile + // Without Telephony: + // Secondary user: Delete + // Guest: Nothing + // Restricted Profile: Settings + final boolean showSettings = mUserCaps.mIsAdmin + && (voiceCapable || user.isRestricted()); + final boolean showDelete = mUserCaps.mIsAdmin + && (!voiceCapable && !user.isRestricted() && !user.isGuest()); + pref = new UserPreference(getPrefContext(), null, user.id, + showSettings ? this : null, + showDelete ? this : null); pref.setKey("id=" + user.id); userPreferences.add(pref); if (user.isAdmin()) { pref.setSummary(R.string.user_admin); } pref.setTitle(user.name); - pref.setOnPreferenceClickListener(this); - pref.setEnabled(canOpenUserDetails); - pref.setSelectable(true); + pref.setSelectable(false); } if (pref == null) { continue; } - if (user.id != UserHandle.myUserId() && !user.isGuest() && !user.isInitialized()) { - // sometimes after creating a guest the initialized flag isn't immediately set - // and we don't want to show "Not set up" summary for them + if (!isInitialized(user)) { if (user.isRestricted()) { pref.setSummary(R.string.user_summary_restricted_not_set_up); } else { @@ -966,7 +962,10 @@ public class UserSettings extends SettingsPreferenceFragment } // Disallow setting up user which results in user switching when the restriction is // set. - pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow()); + if (!mUserCaps.mDisallowSwitchUser) { + pref.setOnPreferenceClickListener(this); + pref.setSelectable(mUserManager.canSwitchUsers()); + } } else if (user.isRestricted()) { pref.setSummary(R.string.user_summary_restricted_profile); } @@ -987,13 +986,53 @@ public class UserSettings extends SettingsPreferenceFragment // Add a temporary entry for the user being created if (mAddingUser) { UserPreference pref = new UserPreference(getPrefContext(), null, - UserPreference.USERID_UNKNOWN); + UserPreference.USERID_UNKNOWN, null, null); pref.setEnabled(false); pref.setTitle(mAddingUserName); pref.setIcon(getEncircledDefaultIcon()); userPreferences.add(pref); } + // Check if Guest tile should be added. + if (!mUserCaps.mIsGuest && (mUserCaps.mCanAddGuest || + mUserCaps.mDisallowAddUserSetByAdmin)) { + // Add a virtual Guest user for guest defaults + UserPreference pref = new UserPreference(getPrefContext(), null, + UserPreference.USERID_GUEST_DEFAULTS, + mUserCaps.mIsAdmin && voiceCapable ? this : null /* settings icon handler */, + null /* delete icon handler */); + pref.setTitle(R.string.user_guest); + pref.setIcon(getEncircledDefaultIcon()); + pref.setKey(KEY_USER_GUEST); + userPreferences.add(pref); + if (mUserCaps.mDisallowAddUser) { + pref.setDisabledByAdmin(mUserCaps.mEnforcedAdmin); + } else if (mUserCaps.mDisallowSwitchUser) { + pref.setDisabledByAdmin(RestrictedLockUtilsInternal.getDeviceOwner(context)); + } else { + pref.setDisabledByAdmin(null); + } + if (!mUserManager.canSwitchUsers()) { + pref.setSelectable(false); + } + int finalGuestId = guestId; + pref.setOnPreferenceClickListener(preference -> { + int id = finalGuestId; + if (id == UserPreference.USERID_GUEST_DEFAULTS) { + UserInfo guest = mUserManager.createGuest( + getContext(), preference.getTitle().toString()); + if (guest != null) { + id = guest.id; + } + } + try { + ActivityManager.getService().switchUser(id); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + return true; + }); + } // Sort list of users by serialNum Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR); @@ -1025,7 +1064,6 @@ public class UserSettings extends SettingsPreferenceFragment mMultiUserFooterPreferenceController.updateState(multiUserFooterPrefence); mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled); - updateAddGuest(context, users.stream().anyMatch(UserInfo::isGuest)); updateAddUser(context); if (!mUserCaps.mUserSwitcherEnabled) { @@ -1039,38 +1077,15 @@ public class UserSettings extends SettingsPreferenceFragment } - private boolean isCurrentUserGuest() { - return mUserCaps.mIsGuest; - } - - private boolean canSwitchUserNow() { - return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; - } - - private void updateAddGuest(Context context, boolean isGuestAlreadyCreated) { - if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest - && WizardManagerHelper.isDeviceProvisioned(context) - && mUserCaps.mUserSwitcherEnabled) { - mAddGuest.setVisible(true); - mAddGuest.setIcon(getEncircledDefaultIcon()); - mAddGuest.setEnabled(canSwitchUserNow()); - mAddGuest.setSelectable(true); - } else { - mAddGuest.setVisible(false); - } - } - private void updateAddUser(Context context) { if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) && WizardManagerHelper.isDeviceProvisioned(context) && mUserCaps.mUserSwitcherEnabled) { mAddUser.setVisible(true); - mAddUser.setSelectable(true); - final boolean canAddMoreUsers = mUserManager.canAddMoreUsers(); - mAddUser.setEnabled(canAddMoreUsers && !mAddingUser && canSwitchUserNow()); - if (!canAddMoreUsers) { - mAddUser.setSummary( - getString(R.string.user_add_max_count, getRealUsersCount())); + final boolean moreUsers = mUserManager.canAddMoreUsers(); + mAddUser.setEnabled(moreUsers && !mAddingUser && mUserManager.canSwitchUsers()); + if (!moreUsers) { + mAddUser.setSummary(getString(R.string.user_add_max_count, getMaxRealUsers())); } else { mAddUser.setSummary(null); } @@ -1083,15 +1098,18 @@ public class UserSettings extends SettingsPreferenceFragment } } - /** - * @return number of non-guest non-managed users - */ - @VisibleForTesting - int getRealUsersCount() { - return (int) mUserManager.getUsers() - .stream() - .filter(user -> !user.isGuest() && !user.isProfile()) - .count(); + private int getMaxRealUsers() { + // guest is not counted against getMaxSupportedUsers() number + final int maxUsersAndGuest = UserManager.getMaxSupportedUsers() + 1; + final List users = mUserManager.getUsers(); + // managed profiles are counted against getMaxSupportedUsers() + int managedProfiles = 0; + for (UserInfo user : users) { + if (user.isManagedProfile()) { + managedProfiles++; + } + } + return maxUsersAndGuest - managedProfiles; } private void loadIconsAsync(List missingIcons) { @@ -1133,12 +1151,12 @@ public class UserSettings extends SettingsPreferenceFragment @Override public boolean onPreferenceClick(Preference pref) { if (pref == mMePreference) { - if (isCurrentUserGuest()) { + if (mUserCaps.mIsGuest) { showDialog(DIALOG_CONFIRM_EXIT_GUEST); return true; } // If this is a limited user, launch the user info settings instead of profile editor - if (mUserManager.isRestrictedProfile()) { + if (mUserManager.isLinkedUser()) { onManageUserClicked(UserHandle.myUserId(), false); } else { showDialog(DIALOG_USER_PROFILE_EDITOR); @@ -1147,11 +1165,9 @@ public class UserSettings extends SettingsPreferenceFragment int userId = ((UserPreference) pref).getUserId(); // Get the latest status of the user UserInfo user = mUserManager.getUserInfo(userId); - if (!user.isInitialized()) { + if (!isInitialized(user)) { mHandler.sendMessage(mHandler.obtainMessage( MESSAGE_SETUP_USER, user.id, user.serialNumber)); - } else { - onManageUserClicked(userId, false); } } else if (pref == mAddUser) { // If we allow both types, show a picker, otherwise directly go to @@ -1161,19 +1177,39 @@ public class UserSettings extends SettingsPreferenceFragment } else { onAddUserClicked(USER_TYPE_USER); } - } else if (pref == mAddGuest) { - UserInfo guest = mUserManager.createGuest( - getContext(), getString(com.android.settingslib.R.string.user_guest)); - switchUserNow(guest.id); } return false; } + private boolean isInitialized(UserInfo user) { + return (user.flags & UserInfo.FLAG_INITIALIZED) != 0; + } + private Drawable encircle(Bitmap icon) { Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon); return circled; } + @Override + public void onClick(View v) { + if (v.getTag() instanceof UserPreference) { + int userId = ((UserPreference) v.getTag()).getUserId(); + if (v.getId() == UserPreference.DELETE_ID) { + final EnforcedAdmin removeDisallowedAdmin = + RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getContext(), + UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); + if (removeDisallowedAdmin != null) { + RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), + removeDisallowedAdmin); + } else { + onRemoveUserClicked(userId); + } + } else if (v.getId() == UserPreference.SETTINGS_ID) { + onManageUserClicked(userId, false); + } + } + } + @Override public void onDismiss(DialogInterface dialog) { synchronized (mUserLock) { diff --git a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java index 673c0fe2689..659c5de770c 100644 --- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java +++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java @@ -18,7 +18,6 @@ package com.android.settings.testutils.shadow; import android.annotation.UserIdInt; import android.content.pm.UserInfo; -import android.os.Bundle; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManager.EnforcingUser; @@ -44,18 +43,13 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager private static boolean sIsSupportsMultipleUsers; - private final List mBaseRestrictions = new ArrayList<>(); - private final List mUserRestrictions = new ArrayList<>(); - private final List mGuestRestrictions = new ArrayList<>(); + private final List mRestrictions = new ArrayList<>(); private final Map> mRestrictionSources = new HashMap<>(); private final List mUserProfileInfos = new ArrayList<>(); private final Set mManagedProfiles = new HashSet<>(); private boolean mIsQuietModeEnabled = false; private int[] profileIdsForUser = new int[0]; private boolean mUserSwitchEnabled; - - private @UserManager.UserSwitchabilityResult int mSwitchabilityStatus = - UserManager.SWITCHABILITY_STATUS_OK; private final Map mSameProfileGroupIds = Maps.newHashMap(); public void addProfile(UserInfo userInfo) { @@ -88,32 +82,11 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager @Implementation protected boolean hasBaseUserRestriction(String restrictionKey, UserHandle userHandle) { - return mBaseRestrictions.contains(restrictionKey); + return mRestrictions.contains(restrictionKey); } public void addBaseUserRestriction(String restriction) { - mBaseRestrictions.add(restriction); - } - - @Implementation - protected boolean hasUserRestriction(@UserManager.UserRestrictionKey String restrictionKey, - UserHandle userHandle) { - return mUserRestrictions.contains(restrictionKey); - } - - public void addUserRestriction(String restriction) { - mUserRestrictions.add(restriction); - } - - @Implementation - protected Bundle getDefaultGuestRestrictions() { - Bundle bundle = new Bundle(); - mGuestRestrictions.forEach(restriction -> bundle.putBoolean(restriction, true)); - return bundle; - } - - public void addGuestUserRestriction(String restriction) { - mGuestRestrictions.add(restriction); + mRestrictions.add(restriction); } public static ShadowUserManager getShadow() { @@ -193,21 +166,4 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager public void setSupportsMultipleUsers(boolean supports) { sIsSupportsMultipleUsers = supports; } - - @Implementation - protected UserInfo getUserInfo(@UserIdInt int userId) { - return mUserProfileInfos.stream() - .filter(userInfo -> userInfo.id == userId) - .findFirst() - .orElse(super.getUserInfo(userId)); - } - - @Implementation - protected @UserManager.UserSwitchabilityResult int getUserSwitchability() { - return mSwitchabilityStatus; - } - - public void setSwitchabilityStatus(@UserManager.UserSwitchabilityResult int newStatus) { - mSwitchabilityStatus = newStatus; - } } diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java deleted file mode 100644 index c51b2fc67d8..00000000000 --- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settings.users; - -import static android.os.UserManager.SWITCHABILITY_STATUS_OK; -import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; -import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -import android.content.ComponentName; -import android.content.Context; -import android.content.pm.UserInfo; -import android.os.Bundle; -import android.os.UserHandle; -import android.os.UserManager; -import android.telephony.TelephonyManager; - -import androidx.fragment.app.FragmentActivity; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; -import androidx.preference.SwitchPreference; - -import com.android.settings.R; -import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; -import com.android.settings.testutils.shadow.ShadowUserManager; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.android.controller.ActivityController; -import org.robolectric.annotation.Config; -import org.robolectric.shadow.api.Shadow; -import org.robolectric.util.ReflectionHelpers; - -import java.util.ArrayList; -import java.util.List; - -@RunWith(RobolectricTestRunner.class) -@Config(shadows = { - ShadowUserManager.class, - ShadowDevicePolicyManager.class -}) -public class UserDetailsSettingsTest { - - private static final String KEY_SWITCH_USER = "switch_user"; - private static final String KEY_ENABLE_TELEPHONY = "enable_calling"; - private static final String KEY_REMOVE_USER = "remove_user"; - - private static final int DIALOG_CONFIRM_REMOVE = 1; - - @Mock - private TelephonyManager mTelephonyManager; - - private ShadowUserManager mUserManager; - - @Mock - private Preference mSwitchUserPref; - @Mock - private SwitchPreference mPhonePref; - @Mock - private Preference mRemoveUserPref; - - private FragmentActivity mActivity; - private Context mContext; - private UserDetailsSettings mFragment; - private Bundle mArguments; - private UserInfo mUserInfo; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mActivity = spy(ActivityController.of(new FragmentActivity()).get()); - mContext = spy(RuntimeEnvironment.application); - mFragment = spy(new UserDetailsSettings()); - mArguments = new Bundle(); - - UserManager userManager = (UserManager) mContext.getSystemService( - Context.USER_SERVICE); - mUserManager = Shadow.extract(userManager); - - doReturn(mTelephonyManager).when(mActivity).getSystemService(Context.TELEPHONY_SERVICE); - - ReflectionHelpers.setField(mFragment, "mUserManager", userManager); - doReturn(mActivity).when(mFragment).getActivity(); - doReturn(mContext).when(mFragment).getContext(); - - doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); - doReturn("").when(mActivity).getString(anyInt(), anyString()); - - doReturn(mSwitchUserPref).when(mFragment).findPreference(KEY_SWITCH_USER); - doReturn(mPhonePref).when(mFragment).findPreference(KEY_ENABLE_TELEPHONY); - doReturn(mRemoveUserPref).when(mFragment).findPreference(KEY_REMOVE_USER); - } - - @After - public void tearDown() { - ShadowUserManager.reset(); - } - - @Test(expected = IllegalStateException.class) - public void initialize_nullArguments_shouldThrowException() { - mFragment.initialize(mActivity, null); - } - - @Test(expected = IllegalStateException.class) - public void initialize_emptyArguments_shouldThrowException() { - mFragment.initialize(mActivity, new Bundle()); - } - - @Test - public void initialize_userSelected_shouldSetupSwitchPref() { - setupSelectedUser(); - doReturn("Switch to " + mUserInfo.name) - .when(mActivity).getString(anyInt(), anyString()); - - mFragment.initialize(mActivity, mArguments); - - verify(mActivity).getString(com.android.settingslib.R.string.user_switch_to_user, - mUserInfo.name); - verify(mSwitchUserPref).setTitle("Switch to " + mUserInfo.name); - verify(mSwitchUserPref).setOnPreferenceClickListener(mFragment); - verify(mFragment, never()).removePreference(KEY_SWITCH_USER); - } - - @Test - public void initialize_guestSelected_shouldSetupSwitchPref() { - setupSelectedGuest(); - doReturn("Switch to " + mUserInfo.name) - .when(mActivity).getString(anyInt(), anyString()); - - mFragment.initialize(mActivity, mArguments); - - verify(mActivity).getString(com.android.settingslib.R.string.user_switch_to_user, - mUserInfo.name); - verify(mSwitchUserPref).setTitle("Switch to " + mUserInfo.name); - verify(mSwitchUserPref).setOnPreferenceClickListener(mFragment); - verify(mFragment, never()).removePreference(KEY_SWITCH_USER); - } - - @Test - public void onResume_canSwitch_shouldEnableSwitchPref() { - mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.onAttach(mContext); - - mFragment.onResume(); - - verify(mSwitchUserPref).setEnabled(true); - } - - @Test - public void onResume_userInCall_shouldDisableSwitchPref() { - mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_IN_CALL); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.onAttach(mContext); - - mFragment.onResume(); - - verify(mSwitchUserPref).setEnabled(false); - } - - @Test - public void onResume_switchDisallowed_shouldDisableSwitchPref() { - mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.onAttach(mContext); - - mFragment.onResume(); - - verify(mSwitchUserPref).setEnabled(false); - } - - @Test - public void onResume_systemUserLocked_shouldDisableSwitchPref() { - mUserManager.setSwitchabilityStatus(UserManager.SWITCHABILITY_STATUS_SYSTEM_USER_LOCKED); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.onAttach(mContext); - - mFragment.onResume(); - - verify(mSwitchUserPref).setEnabled(false); - } - - @Test - public void initialize_adminWithTelephony_shouldShowPhonePreference() { - setupSelectedUser(); - doReturn(true).when(mTelephonyManager).isVoiceCapable(); - mUserManager.setIsAdminUser(true); - - mFragment.initialize(mActivity, mArguments); - - verify(mFragment, never()).removePreference(KEY_ENABLE_TELEPHONY); - verify(mPhonePref).setOnPreferenceChangeListener(mFragment); - } - - @Test - public void initialize_adminNoTelephony_shouldNotShowPhonePreference() { - setupSelectedUser(); - doReturn(false).when(mTelephonyManager).isVoiceCapable(); - mUserManager.setIsAdminUser(true); - doReturn(null).when(mActivity).getSystemService(Context.TELEPHONY_SERVICE); - - mFragment.initialize(mActivity, mArguments); - - verify(mFragment).removePreference(KEY_ENABLE_TELEPHONY); - } - - @Test - public void initialize_nonAdminWithTelephony_shouldNotShowPhonePreference() { - setupSelectedUser(); - doReturn(true).when(mTelephonyManager).isVoiceCapable(); - mUserManager.setIsAdminUser(false); - - mFragment.initialize(mActivity, mArguments); - - verify(mFragment).removePreference(KEY_ENABLE_TELEPHONY); - } - - @Test - public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - - mFragment.initialize(mActivity, mArguments); - - verify(mRemoveUserPref).setOnPreferenceClickListener(mFragment); - verify(mRemoveUserPref).setTitle(R.string.user_remove_user); - verify(mFragment, never()).removePreference(KEY_REMOVE_USER); - } - - @Test - public void initialize_adminSelectsGuest_shouldShowRemovePreference() { - setupSelectedGuest(); - mUserManager.setIsAdminUser(true); - - mFragment.initialize(mActivity, mArguments); - - verify(mRemoveUserPref).setOnPreferenceClickListener(mFragment); - verify(mRemoveUserPref).setTitle(R.string.user_exit_guest_title); - verify(mFragment, never()).removePreference(KEY_REMOVE_USER); - } - - @Test - public void initialize_nonAdmin_shouldNotShowRemovePreference() { - setupSelectedUser(); - mUserManager.setIsAdminUser(false); - - mFragment.initialize(mActivity, mArguments); - - verify(mFragment).removePreference(KEY_REMOVE_USER); - } - - @Test - public void initialize_disallowRemoveUserRestriction_shouldNotShowRemovePreference() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - mUserManager.addBaseUserRestriction(UserManager.DISALLOW_REMOVE_USER); - - mFragment.initialize(mActivity, mArguments); - - verify(mFragment).removePreference(KEY_REMOVE_USER); - } - - @Test - public void initialize_userHasCallRestriction_shouldSetPhoneSwitchUnChecked() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - mUserManager.addUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS); - - mFragment.initialize(mActivity, mArguments); - - verify(mPhonePref).setChecked(false); - } - - @Test - public void initialize_noCallRestriction_shouldSetPhoneSwitchChecked() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - - mFragment.initialize(mActivity, mArguments); - - verify(mPhonePref).setChecked(true); - } - - @Test - public void initialize_guestSelected_noCallRestriction_shouldSetPhonePreference() { - setupSelectedGuest(); - mUserManager.setIsAdminUser(true); - - mFragment.initialize(mActivity, mArguments); - - verify(mPhonePref).setTitle(R.string.user_enable_calling); - verify(mPhonePref).setChecked(true); - } - - @Test - public void initialize_guestSelected_callRestriction_shouldSetPhonePreference() { - setupSelectedGuest(); - mUserManager.setIsAdminUser(true); - mUserManager.addGuestUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS); - - mFragment.initialize(mActivity, mArguments); - - verify(mPhonePref).setTitle(R.string.user_enable_calling); - verify(mPhonePref).setChecked(false); - } - - @Test - public void onPreferenceClick_switchClicked_canSwitch_shouldSwitch() { - setupSelectedUser(); - mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.mRemoveUserPref = mRemoveUserPref; - mFragment.mUserInfo = mUserInfo; - - mFragment.onPreferenceClick(mSwitchUserPref); - - verify(mFragment).switchUser(); - } - - @Test - public void onPreferenceClick_switchClicked_canNotSwitch_doNothing() { - setupSelectedUser(); - mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.mRemoveUserPref = mRemoveUserPref; - mFragment.mUserInfo = mUserInfo; - - mFragment.onPreferenceClick(mSwitchUserPref); - - verify(mFragment, never()).switchUser(); - } - - @Test - public void onPreferenceClick_removeClicked_canDelete_shouldShowDialog() { - setupSelectedUser(); - mFragment.mUserInfo = mUserInfo; - mUserManager.setIsAdminUser(true); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.mRemoveUserPref = mRemoveUserPref; - doNothing().when(mFragment).showDialog(anyInt()); - - mFragment.onPreferenceClick(mRemoveUserPref); - - verify(mFragment).canDeleteUser(); - verify(mFragment).showDialog(DIALOG_CONFIRM_REMOVE); - } - - @Test - public void onPreferenceClick_removeClicked_canNotDelete_doNothing() { - setupSelectedUser(); - mFragment.mUserInfo = mUserInfo; - mUserManager.setIsAdminUser(false); - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.mRemoveUserPref = mRemoveUserPref; - doNothing().when(mFragment).showDialog(anyInt()); - - mFragment.onPreferenceClick(mRemoveUserPref); - - verify(mFragment).canDeleteUser(); - verify(mFragment, never()).showDialog(DIALOG_CONFIRM_REMOVE); - } - - @Test - public void onPreferenceClick_unknownPreferenceClicked_doNothing() { - setupSelectedUser(); - mFragment.mUserInfo = mUserInfo; - mFragment.mSwitchUserPref = mSwitchUserPref; - mFragment.mRemoveUserPref = mRemoveUserPref; - - mFragment.onPreferenceClick(mock(UserPreference.class)); - - verify(mFragment).onPreferenceClick(any()); - verifyNoMoreInteractions(mFragment); - } - - @Test - public void canDeleteUser_nonAdminUser_shouldReturnFalse() { - mUserManager.setIsAdminUser(false); - - boolean result = mFragment.canDeleteUser(); - - assertThat(result).isFalse(); - } - - @Test - public void canDeleteUser_adminSelectsUser_noRestrictions_shouldReturnTrue() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - - boolean result = mFragment.canDeleteUser(); - - assertThat(result).isTrue(); - } - - @Test - public void canDeleteUser_adminSelectsUser_hasRemoveRestriction_shouldReturnFalse() { - setupSelectedUser(); - mUserManager.setIsAdminUser(true); - ComponentName componentName = new ComponentName("test", "test"); - ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser(componentName); - ShadowDevicePolicyManager.getShadow().setDeviceOwnerUserId(UserHandle.myUserId()); - List enforcingUsers = new ArrayList<>(); - enforcingUsers.add(new UserManager.EnforcingUser(UserHandle.myUserId(), - UserManager.RESTRICTION_SOURCE_DEVICE_OWNER)); - mUserManager.setUserRestrictionSources( - UserManager.DISALLOW_REMOVE_USER, - UserHandle.of(UserHandle.myUserId()), - enforcingUsers - ); - - boolean result = mFragment.canDeleteUser(); - - assertThat(result).isFalse(); - } - - private void setupSelectedUser() { - mArguments.putInt("user_id", 1); - mUserInfo = new UserInfo(1, "Tom", null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, - UserManager.USER_TYPE_FULL_SECONDARY); - - mUserManager.addProfile(mUserInfo); - } - - private void setupSelectedGuest() { - mArguments.putInt("user_id", 23); - mUserInfo = new UserInfo(23, "Guest", null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST, - UserManager.USER_TYPE_FULL_GUEST); - - mUserManager.addProfile(mUserInfo); - } -} diff --git a/tests/robotests/src/com/android/settings/users/UserPreferenceTest.java b/tests/robotests/src/com/android/settings/users/UserPreferenceTest.java index 28f415e362b..345784aa51e 100644 --- a/tests/robotests/src/com/android/settings/users/UserPreferenceTest.java +++ b/tests/robotests/src/com/android/settings/users/UserPreferenceTest.java @@ -18,8 +18,12 @@ package com.android.settings.users; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + import android.content.Context; import android.os.UserHandle; +import android.view.View; import com.android.settingslib.RestrictedPreferenceHelper; @@ -44,12 +48,28 @@ public class UserPreferenceTest { public void setUp() { MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application; - mUserPreference = new UserPreference(mContext, null /* attrs */, UserHandle.USER_CURRENT); + mUserPreference = new UserPreference(mContext, null /* attrs */, UserHandle.USER_CURRENT, + null /* settingsListener */, null /* deleteListener */); ReflectionHelpers.setField(mUserPreference, "mHelper", mRestrictedPreferenceHelper); } @Test - public void testShouldHideSecondTarget_shouldHide() { + public void testShouldHideSecondTarget_noListener_shouldHide() { assertThat(mUserPreference.shouldHideSecondTarget()).isTrue(); } + + @Test + public void testShouldHideSecondTarget_disabledByAdmin_shouldHide() { + when(mRestrictedPreferenceHelper.isDisabledByAdmin()).thenReturn(true); + + assertThat(mUserPreference.shouldHideSecondTarget()).isTrue(); + } + + @Test + public void testShouldHideSecondTarget_hasSettingListener_shouldNotHide() { + ReflectionHelpers.setField(mUserPreference, "mSettingsClickListener", + mock(View.OnClickListener.class)); + + assertThat(mUserPreference.shouldHideSecondTarget()).isFalse(); + } } diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java index d2a18403773..5853308c601 100644 --- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java +++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java @@ -16,14 +16,9 @@ package com.android.settings.users; -import static android.os.UserManager.SWITCHABILITY_STATUS_OK; -import static android.os.UserManager.SWITCHABILITY_STATUS_USER_IN_CALL; -import static android.os.UserManager.SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED; - import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.notNull; @@ -31,14 +26,12 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; -import android.content.pm.UserInfo; -import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.os.UserManager; @@ -49,13 +42,14 @@ import android.view.MenuInflater; import android.view.MenuItem; import androidx.fragment.app.FragmentActivity; +import androidx.preference.Preference; import androidx.preference.PreferenceCategory; +import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceManager; import androidx.preference.PreferenceScreen; import com.android.settings.testutils.shadow.ShadowDevicePolicyManager; import com.android.settings.testutils.shadow.ShadowUserManager; -import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedPreference; import org.junit.After; @@ -63,7 +57,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.AdditionalMatchers; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; @@ -72,7 +65,6 @@ import org.robolectric.android.controller.ActivityController; import org.robolectric.annotation.Config; import org.robolectric.util.ReflectionHelpers; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -81,18 +73,7 @@ import java.util.List; public class UserSettingsTest { private static final String KEY_USER_GUEST = "user_guest"; - private static final int ACTIVE_USER_ID = 0; - private static final int INACTIVE_ADMIN_USER_ID = 1; - private static final int INACTIVE_SECONDARY_USER_ID = 14; - private static final int INACTIVE_RESTRICTED_USER_ID = 21; - private static final int INACTIVE_GUEST_USER_ID = 23; - private static final int MANAGED_USER_ID = 11; - private static final String ADMIN_USER_NAME = "Owner"; - private static final String SECONDARY_USER_NAME = "Tom"; - private static final String RESTRICTED_USER_NAME = "Bob"; - private static final String GUEST_USER_NAME = "Guest"; - private static final String MANAGED_USER_NAME = "Work profile"; - private int mProvisionedBackupValue; + private int mProvisioned; @Mock private Drawable mDefaultIconDrawable; @@ -101,10 +82,6 @@ public class UserSettingsTest { @Mock private UserPreference mMePreference; @Mock - private RestrictedPreference mAddUserPreference; - @Mock - private RestrictedPreference mAddGuestPreference; - @Mock private UserManager mUserManager; private FragmentActivity mActivity; @@ -118,7 +95,6 @@ public class UserSettingsTest { mActivity = spy(ActivityController.of(new FragmentActivity()).get()); mContext = spy(RuntimeEnvironment.application); mUserCapabilities = UserCapabilities.create(mContext); - mUserCapabilities.mUserSwitcherEnabled = true; mFragment = spy(new UserSettings()); ReflectionHelpers.setField(mFragment, "mAddUserWhenLockedPreferenceController", @@ -129,41 +105,100 @@ public class UserSettingsTest { ReflectionHelpers.setField(mFragment, "mUserCaps", mUserCapabilities); ReflectionHelpers.setField(mFragment, "mDefaultIconDrawable", mDefaultIconDrawable); ReflectionHelpers.setField(mFragment, "mAddingUser", false); + mFragment.mMePreference = mMePreference; - doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class); - + when((Object) mActivity.getSystemService(UserManager.class)).thenReturn(mUserManager); doReturn(mActivity).when(mFragment).getActivity(); doReturn(mContext).when(mFragment).getContext(); doReturn(mMockPreferenceManager).when(mFragment).getPreferenceManager(); doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); - - mProvisionedBackupValue = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0); - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 1); //default state - - final SharedPreferences prefs = mock(SharedPreferences.class); - - doReturn(prefs).when(mMockPreferenceManager).getSharedPreferences(); - doReturn(mContext).when(mMockPreferenceManager).getContext(); - doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); - - mFragment.mMePreference = mMePreference; - mFragment.mAddUser = mAddUserPreference; - mFragment.mAddGuest = mAddGuestPreference; - mFragment.mUserListCategory = mock(PreferenceCategory.class); + mProvisioned = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 0); + final SharedPreferences prefs = mock(SharedPreferences .class); + when(mMockPreferenceManager.getSharedPreferences()).thenReturn(prefs); + when(mMockPreferenceManager.getContext()).thenReturn(mContext); } @After public void tearDown() { Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, mProvisionedBackupValue); + Settings.Global.DEVICE_PROVISIONED, mProvisioned); } @Test public void testAssignDefaultPhoto_ContextNull_ReturnFalseAndNotCrash() { // Should not crash here - assertThat(UserSettings.assignDefaultPhoto(null, ACTIVE_USER_ID)).isFalse(); + assertThat(UserSettings.assignDefaultPhoto(null, 0)).isFalse(); + } + + @Test + public void updateUserList_cannotSwitchUser_shouldNotBeSelectableForGuest() { + final RestrictedPreference addUser = spy(new RestrictedPreference(mContext)); + final PreferenceGroup userListCategory = spy(new PreferenceCategory(mContext)); + + mUserCapabilities.mIsGuest = false; + mUserCapabilities.mCanAddGuest = true; + mUserCapabilities.mDisallowAddUser = false; + mUserCapabilities.mDisallowSwitchUser = false; + mUserCapabilities.mUserSwitcherEnabled = true; + + mFragment.mUserListCategory = userListCategory; + mFragment.mAddUser = addUser; + + when(mUserManager.canSwitchUsers()).thenReturn(false); + doReturn(mMockPreferenceManager).when(mFragment).getPreferenceManager(); + doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); + doReturn(mMockPreferenceManager).when(userListCategory).getPreferenceManager(); + + mFragment.updateUserList(); + + final Preference guest = userListCategory.findPreference(KEY_USER_GUEST); + assertThat(guest.isSelectable()).isFalse(); + } + + @Test + public void updateUserList_cannotSwitchUser_shouldDisableAddUser() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 1); + final RestrictedPreference addUser = spy(new RestrictedPreference(mContext)); + final PreferenceGroup userListCategory = spy(new PreferenceCategory(mContext)); + + mUserCapabilities.mCanAddUser = true; + mUserCapabilities.mDisallowAddUser = false; + mUserCapabilities.mUserSwitcherEnabled = true; + + mFragment.mUserListCategory = userListCategory; + mFragment.mAddUser = addUser; + + when(mUserManager.canSwitchUsers()).thenReturn(false); + when(mUserManager.canAddMoreUsers()).thenReturn(true); + doReturn(mMockPreferenceManager).when(mFragment).getPreferenceManager(); + doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); + doReturn(mMockPreferenceManager).when(userListCategory).getPreferenceManager(); + + mFragment.updateUserList(); + + assertThat(addUser.isEnabled()).isFalse(); + } + + @Test + public void updateUserList_cannotAddUserButCanSwitchUser_shouldNotShowAddUser() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 1); + final RestrictedPreference addUser = mock(RestrictedPreference.class); + + mUserCapabilities.mCanAddUser = false; + mUserCapabilities.mDisallowAddUser = true; + mUserCapabilities.mUserSwitcherEnabled = true; + + mFragment.mUserListCategory = mock(PreferenceCategory.class); + mFragment.mAddUser = addUser; + + doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); + + mFragment.updateUserList(); + + verify(addUser, never()).setVisible(true); } @Test @@ -183,7 +218,7 @@ public class UserSettingsTest { ShadowDevicePolicyManager.getShadow().setDeviceOwnerComponentOnAnyUser( new ComponentName("test", "test")); - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); + doReturn(true).when(mUserManager).canSwitchUsers(); mUserCapabilities.mIsAdmin = false; Menu menu = mock(Menu.class); @@ -208,7 +243,7 @@ public class UserSettingsTest { @Test public void withoutDisallowRemoveUser_ShouldNotDisableRemoveUser() { // Arrange - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); + doReturn(true).when(mUserManager).canSwitchUsers(); mUserCapabilities.mIsAdmin = false; Menu menu = mock(Menu.class); @@ -231,457 +266,44 @@ public class UserSettingsTest { @Test public void updateUserList_canAddUserAndSwitchUser_shouldShowAddUser() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 1); + final RestrictedPreference addUser = mock(RestrictedPreference.class); + mUserCapabilities.mCanAddUser = true; - doReturn(true).when(mUserManager).canAddMoreUsers(); - doReturn(true).when(mAddUserPreference).isEnabled(); - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); + mUserCapabilities.mDisallowAddUser = false; + mUserCapabilities.mUserSwitcherEnabled = true; + + mFragment.mAddUser = addUser; + mFragment.mUserListCategory = mock(PreferenceCategory.class); + + doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); + doReturn("Test summary").when(mFragment).getString(anyInt(), anyInt()); mFragment.updateUserList(); - verify(mAddUserPreference).setVisible(true); - verify(mAddUserPreference).setSummary(null); - verify(mAddUserPreference).setEnabled(true); - verify(mAddUserPreference).setDisabledByAdmin(null); - verify(mAddUserPreference).setSelectable(true); + verify(addUser).setVisible(true); } @Test - public void updateUserList_canAddGuestAndSwitchUser_shouldShowAddGuest() { - mUserCapabilities.mCanAddGuest = true; - doReturn(true).when(mUserManager).canAddMoreUsers(); - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); + public void updateUserList_addUserDisallowedByAdmin_shouldShowAddUserDisabled() { + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.DEVICE_PROVISIONED, 1); + final RestrictedPreference addUser = mock(RestrictedPreference.class); - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(true); - verify(mAddGuestPreference).setEnabled(true); - verify(mAddGuestPreference).setIcon(any(Drawable.class)); - verify(mAddGuestPreference).setSelectable(true); - } - - @Test - public void updateUserList_cannotSwitchUser_shouldDisableAddUser() { - mUserCapabilities.mCanAddUser = true; - doReturn(true).when(mUserManager).canAddMoreUsers(); - doReturn(true).when(mAddUserPreference).isEnabled(); - doReturn(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED) - .when(mUserManager).getUserSwitchability(); - - mFragment.updateUserList(); - - verify(mAddUserPreference).setVisible(true); - verify(mAddUserPreference).setSummary(null); - verify(mAddUserPreference).setEnabled(false); - verify(mAddUserPreference).setSelectable(true); - } - - @Test - public void updateUserList_canNotAddMoreUsers_shouldDisableAddUserWithSummary() { - doReturn(false).when(mUserManager).canAddMoreUsers(); - doReturn(false).when(mAddUserPreference).isEnabled(); - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); - doReturn(4).when(mFragment).getRealUsersCount(); - - mFragment.updateUserList(); - - verify(mAddUserPreference).setVisible(true); - verify(mAddUserPreference).setSummary("You can add up to 4 users"); - verify(mAddUserPreference).setEnabled(false); - verify(mAddUserPreference).setSelectable(true); - } - - @Test - public void updateUserList_cannotSwitchUser_shouldDisableAddGuest() { - mUserCapabilities.mCanAddGuest = true; - doReturn(true).when(mUserManager).canAddMoreUsers(); - doReturn(SWITCHABILITY_STATUS_USER_IN_CALL).when(mUserManager).getUserSwitchability(); - - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(true); - verify(mAddGuestPreference).setEnabled(false); - verify(mAddGuestPreference).setIcon(any(Drawable.class)); - verify(mAddGuestPreference).setSelectable(true); - } - - @Test - public void updateUserList_addUserDisallowedByAdmin_shouldShowDisabledAddUser() { - RestrictedLockUtils.EnforcedAdmin enforcedAdmin = mock( - RestrictedLockUtils.EnforcedAdmin.class); - mUserCapabilities.mEnforcedAdmin = enforcedAdmin; mUserCapabilities.mCanAddUser = false; mUserCapabilities.mDisallowAddUser = true; mUserCapabilities.mDisallowAddUserSetByAdmin = true; - doReturn(true).when(mAddUserPreference).isEnabled(); + mUserCapabilities.mUserSwitcherEnabled = true; + + mFragment.mUserListCategory = mock(PreferenceCategory.class); + mFragment.mAddUser = addUser; + + doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen(); mFragment.updateUserList(); - verify(mAddUserPreference).setVisible(true); - ArgumentCaptor captor = ArgumentCaptor.forClass( - RestrictedLockUtils.EnforcedAdmin.class); - verify(mAddUserPreference).setDisabledByAdmin(captor.capture()); - assertThat(captor.getValue()).isEqualTo(enforcedAdmin); + verify(addUser).setVisible(true); + assertThat(addUser.isEnabled()).isFalse(); } - - @Test - public void updateUserList_cannotAddUserButCanSwitchUser_shouldNotShowAddUser() { - mUserCapabilities.mCanAddUser = false; - - mFragment.updateUserList(); - - verify(mAddUserPreference).setVisible(false); - } - - @Test - public void updateUserList_canNotAddGuest_shouldNotShowAddGuest() { - mUserCapabilities.mCanAddGuest = false; - - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(false); - } - - @Test - public void updateUserList_notProvisionedDevice_shouldNotShowAddUser() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0); - mUserCapabilities.mCanAddUser = true; - - mFragment.updateUserList(); - - verify(mAddUserPreference).setVisible(false); - } - - @Test - public void updateUserList_notProvisionedDevice_shouldNotShowAddGuest() { - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.DEVICE_PROVISIONED, 0); - mUserCapabilities.mCanAddGuest = true; - - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(false); - } - - @Test - public void updateUserList_userSwitcherDisabled_shouldNotShowAddUser() { - mUserCapabilities.mCanAddUser = true; - mUserCapabilities.mUserSwitcherEnabled = false; - - mFragment.updateUserList(); - - verify(mAddUserPreference).setVisible(false); - } - - @Test - public void updateUserList_userSwitcherDisabled_shouldNotShowAddGuest() { - mUserCapabilities.mCanAddGuest = true; - mUserCapabilities.mUserSwitcherEnabled = false; - - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(false); - } - - @Test - public void updateUserList_shouldAddAdminUserPreference() { - givenUsers(getAdminUser(true)); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory).addPreference(captor.capture()); - UserPreference adminPref = captor.getValue(); - assertThat(adminPref).isSameAs(mMePreference); - } - - @Test - public void updateUserList_existingGuest_shouldAddGuestUserPreference() { - givenUsers(getAdminUser(true), getGuest(false)); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference guestPref = captor.getAllValues().get(1); - assertThat(guestPref.getUserId()).isEqualTo(INACTIVE_GUEST_USER_ID); - assertThat(guestPref.getTitle()).isEqualTo("Guest"); - assertThat(guestPref.getIcon()).isNotNull(); - assertThat(guestPref.getKey()).isEqualTo(KEY_USER_GUEST); - assertThat(guestPref.isEnabled()).isEqualTo(true); - assertThat(guestPref.isSelectable()).isEqualTo(true); - assertThat(guestPref.getOnPreferenceClickListener()).isSameAs(mFragment); - } - - @Test - public void updateUserList_existingSecondaryUser_shouldAddSecondaryUserPreference() { - givenUsers(getAdminUser(true), getSecondaryUser(false)); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference userPref = captor.getAllValues().get(1); - assertThat(userPref.getUserId()).isEqualTo(INACTIVE_SECONDARY_USER_ID); - assertThat(userPref.getTitle()).isEqualTo(SECONDARY_USER_NAME); - assertThat(userPref.getIcon()).isNotNull(); - assertThat(userPref.getKey()).isEqualTo("id=" + INACTIVE_SECONDARY_USER_ID); - assertThat(userPref.isEnabled()).isEqualTo(true); - assertThat(userPref.isSelectable()).isEqualTo(true); - assertThat(userPref.getOnPreferenceClickListener()).isSameAs(mFragment); - } - - @Test - public void updateUserList_existingRestrictedUser_shouldAddRestrictedUserPreference() { - givenUsers(getAdminUser(true), getRestrictedUser(false)); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference userPref = captor.getAllValues().get(1); - assertThat(userPref.getUserId()).isEqualTo(INACTIVE_RESTRICTED_USER_ID); - assertThat(userPref.getTitle()).isEqualTo(RESTRICTED_USER_NAME); - assertThat(userPref.getIcon()).isNotNull(); - assertThat(userPref.getKey()).isEqualTo("id=" + INACTIVE_RESTRICTED_USER_ID); - assertThat(userPref.getSummary()).isEqualTo("Restricted profile"); - assertThat(userPref.isEnabled()).isEqualTo(true); - assertThat(userPref.isSelectable()).isEqualTo(true); - assertThat(userPref.getOnPreferenceClickListener()).isSameAs(mFragment); - } - - @Test - public void updateUserList_existingManagedUser_shouldNotAddUserPreference() { - givenUsers(getAdminUser(true), getManagedUser()); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory).addPreference(captor.capture()); - List userPreferences = captor.getAllValues(); - assertThat(userPreferences.size()).isEqualTo(1); - assertThat(userPreferences.get(0).getUserId()).isEqualTo(ACTIVE_USER_ID); - } - - @Test - public void updateUserList_uninitializedRestrictedUser_shouldAddUserPreference() { - UserInfo restrictedUser = getRestrictedUser(false); - removeFlag(restrictedUser, UserInfo.FLAG_INITIALIZED); - givenUsers(getAdminUser(true), restrictedUser); - doReturn(SWITCHABILITY_STATUS_OK).when(mUserManager).getUserSwitchability(); - mUserCapabilities.mDisallowSwitchUser = false; - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference userPref = captor.getAllValues().get(1); - assertThat(userPref.getUserId()).isEqualTo(INACTIVE_RESTRICTED_USER_ID); - assertThat(userPref.getTitle()).isEqualTo(RESTRICTED_USER_NAME); - assertThat(userPref.getIcon()).isNotNull(); - assertThat(userPref.getKey()).isEqualTo("id=" + INACTIVE_RESTRICTED_USER_ID); - assertThat(userPref.getSummary()).isEqualTo("Not set up - Restricted profile"); - assertThat(userPref.isEnabled()).isEqualTo(true); - assertThat(userPref.isSelectable()).isEqualTo(true); - assertThat(userPref.getOnPreferenceClickListener()).isSameAs(mFragment); - } - - @Test - public void updateUserList_uninitializedUserAndCanNotSwitchUser_shouldDisablePref() { - UserInfo uninitializedUser = getSecondaryUser(false); - removeFlag(uninitializedUser, UserInfo.FLAG_INITIALIZED); - givenUsers(getAdminUser(true), uninitializedUser); - doReturn(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED) - .when(mUserManager).getUserSwitchability(); - mUserCapabilities.mDisallowSwitchUser = false; - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference userPref = captor.getAllValues().get(1); - assertThat(userPref.getUserId()).isEqualTo(INACTIVE_SECONDARY_USER_ID); - assertThat(userPref.getTitle()).isEqualTo(SECONDARY_USER_NAME); - assertThat(userPref.getIcon()).isNotNull(); - assertThat(userPref.getKey()).isEqualTo("id=" + INACTIVE_SECONDARY_USER_ID); - assertThat(userPref.getSummary()).isEqualTo("Not set up"); - assertThat(userPref.isEnabled()).isEqualTo(false); - assertThat(userPref.isSelectable()).isEqualTo(true); - assertThat(userPref.getOnPreferenceClickListener()).isSameAs(mFragment); - } - - @Test - public void updateUserList_guestWithoutInitializedFlag_shouldNotSetSummary() { - UserInfo guest = getGuest(false); - removeFlag(guest, UserInfo.FLAG_INITIALIZED); - givenUsers(getAdminUser(true), guest); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory, times(2)) - .addPreference(captor.capture()); - UserPreference userPref = captor.getAllValues().get(1); - assertThat(userPref.getUserId()).isEqualTo(INACTIVE_GUEST_USER_ID); - assertThat(userPref.getSummary()).isNull(); - } - - @Test - public void updateUserList_activeUserWithoutInitializedFlag_shouldNotSetSummary() { - UserInfo activeUser = getSecondaryUser(true); - removeFlag(activeUser, UserInfo.FLAG_INITIALIZED); - givenUsers(activeUser); - - mFragment.updateUserList(); - - ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class); - verify(mFragment.mUserListCategory).addPreference(captor.capture()); - UserPreference userPref = captor.getValue(); - assertThat(userPref.getUserId()).isEqualTo(ACTIVE_USER_ID); - assertThat(userPref.getSummary()).isNull(); - } - - @Test - public void updateUserList_guestIsAlreadyCreated_shouldNotShowAddGuest() { - givenUsers(getAdminUser(true), getGuest(true)); - mUserCapabilities.mCanAddGuest = true; - - mFragment.updateUserList(); - - verify(mAddGuestPreference).setVisible(false); - } - - @Test - public void updateUserList_userIconLoaded_shouldNotLoadIcon() { - UserInfo currentUser = getAdminUser(true); - currentUser.iconPath = "/data/system/users/0/photo.png"; - givenUsers(currentUser); - mFragment.mUserIcons.put(ACTIVE_USER_ID, - Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888)); - - mFragment.updateUserList(); - - verify(mUserManager, never()).getUserIcon(anyInt()); - // updateUserList should be called only once - verify(mUserManager).getUsers(true); - } - - @Test - public void updateUserList_userIconMissing_shouldLoadIcon() { - UserInfo currentUser = getAdminUser(true); - currentUser.iconPath = "/data/system/users/0/photo.png"; - givenUsers(currentUser); - // create a non-empty sparsearray - mFragment.mUserIcons.put(5, Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888)); - Bitmap userIcon = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); - doReturn(userIcon).when(mUserManager).getUserIcon(ACTIVE_USER_ID); - - mFragment.updateUserList(); - - verify(mUserManager).getUserIcon(ACTIVE_USER_ID); - // updateUserList should be called another time after loading the icons - verify(mUserManager, times(2)).getUsers(true); - } - - @Test - public void getRealUsersCount_onlyAdmin_shouldCount() { - givenUsers(getAdminUser(true)); - - int result = mFragment.getRealUsersCount(); - - assertThat(result).isEqualTo(1); - verify(mUserManager).getUsers(); - } - - @Test - public void getRealUsersCount_secondaryUser_shouldCount() { - givenUsers(getAdminUser(true), getSecondaryUser(false)); - - int result = mFragment.getRealUsersCount(); - - assertThat(result).isEqualTo(2); - verify(mUserManager).getUsers(); - } - - @Test - public void getRealUsersCount_restrictedUser_shouldCount() { - givenUsers(getAdminUser(true), getSecondaryUser(false)); - - int result = mFragment.getRealUsersCount(); - - assertThat(result).isEqualTo(2); - verify(mUserManager).getUsers(); - } - - @Test - public void getRealUsersCount_guest_shouldNotCount() { - givenUsers(getAdminUser(true), getGuest(false)); - - int result = mFragment.getRealUsersCount(); - - assertThat(result).isEqualTo(1); - verify(mUserManager).getUsers(); - } - - @Test - public void getRealUsersCount_managedUser_shouldNotCount() { - givenUsers(getAdminUser(true), getManagedUser()); - - int result = mFragment.getRealUsersCount(); - - assertThat(result).isEqualTo(1); - verify(mUserManager).getUsers(); - } - - private void givenUsers(UserInfo... userInfo) { - List users = Arrays.asList(userInfo); - doReturn(users).when(mUserManager).getUsers(); - doReturn(users).when(mUserManager).getUsers(anyBoolean()); - } - - private static void removeFlag(UserInfo userInfo, int flag) { - userInfo.flags &= ~flag; - } - - private static UserInfo getAdminUser(boolean active) { - return new UserInfo(active ? ACTIVE_USER_ID : INACTIVE_ADMIN_USER_ID, ADMIN_USER_NAME, - null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN, - UserManager.USER_TYPE_FULL_SYSTEM); - } - - private static UserInfo getSecondaryUser(boolean active) { - return new UserInfo(active ? ACTIVE_USER_ID : INACTIVE_SECONDARY_USER_ID, - SECONDARY_USER_NAME, null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED, - UserManager.USER_TYPE_FULL_SECONDARY); - } - - private static UserInfo getRestrictedUser(boolean active) { - return new UserInfo(active ? ACTIVE_USER_ID : INACTIVE_RESTRICTED_USER_ID, - RESTRICTED_USER_NAME, null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED, - UserManager.USER_TYPE_FULL_RESTRICTED); - } - - private static UserInfo getManagedUser() { - return new UserInfo(MANAGED_USER_ID, - MANAGED_USER_NAME, null, - UserInfo.FLAG_PROFILE | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_MANAGED_PROFILE, - UserManager.USER_TYPE_PROFILE_MANAGED); - } - - private static UserInfo getGuest(boolean active) { - return new UserInfo(active ? ACTIVE_USER_ID : INACTIVE_GUEST_USER_ID, GUEST_USER_NAME, - null, - UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_GUEST, - UserManager.USER_TYPE_FULL_GUEST); - } - - }