Add a switchbar to turn off multi-user feature entirely

Change-Id: Ibf88bf37899af5065c68aeb2337acd4ee48bc13c
Fixes: 72319180
Test: robotest on new controller. Manual test on UserSettings fragment.
This commit is contained in:
Fan Zhang
2018-06-05 15:36:40 -07:00
parent ddd9283ee0
commit c39238c63f
12 changed files with 352 additions and 100 deletions

View File

@@ -31,7 +31,8 @@
<com.android.settingslib.RestrictedSwitchPreference <com.android.settingslib.RestrictedSwitchPreference
android:key="security_lockscreen_add_users_when_locked" android:key="security_lockscreen_add_users_when_locked"
android:title="@string/user_add_on_lockscreen_menu" /> android:title="@string/user_add_on_lockscreen_menu"
settings:controller="com.android.settings.users.AddUserWhenLockedPreferenceController" />
<com.android.settingslib.RestrictedPreference <com.android.settingslib.RestrictedPreference
android:key="owner_info_settings" android:key="owner_info_settings"

View File

@@ -21,7 +21,6 @@ package com.android.settings.core;
*/ */
public class FeatureFlags { public class FeatureFlags {
public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list"; public static final String BATTERY_DISPLAY_APP_LIST = "settings_battery_display_app_list";
public static final String ZONE_PICKER_V2 = "settings_zone_picker_v2";
public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving"; public static final String BLUETOOTH_WHILE_DRIVING = "settings_bluetooth_while_driving";
public static final String DATA_USAGE_SETTINGS_V2 = "settings_data_usage_v2"; public static final String DATA_USAGE_SETTINGS_V2 = "settings_data_usage_v2";
public static final String AUDIO_SWITCHER_SETTINGS = "settings_audio_switcher"; public static final String AUDIO_SWITCHER_SETTINGS = "settings_audio_switcher";

View File

@@ -29,7 +29,6 @@ import com.android.settings.gestures.DoubleTapScreenPreferenceController;
import com.android.settings.gestures.PickupGesturePreferenceController; import com.android.settings.gestures.PickupGesturePreferenceController;
import com.android.settings.notification.LockScreenNotificationPreferenceController; import com.android.settings.notification.LockScreenNotificationPreferenceController;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.users.AddUserWhenLockedPreferenceController;
import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle; import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable; import com.android.settingslib.search.SearchIndexable;
@@ -109,8 +108,6 @@ public class LockscreenDashboardFragment extends DashboardFragment
KEY_LOCK_SCREEN_NOTIFICATON_WORK_PROFILE); KEY_LOCK_SCREEN_NOTIFICATON_WORK_PROFILE);
lifecycle.addObserver(notificationController); lifecycle.addObserver(notificationController);
controllers.add(notificationController); controllers.add(notificationController);
controllers.add(new AddUserWhenLockedPreferenceController(
context, KEY_ADD_USER_FROM_LOCK_SCREEN, lifecycle));
mOwnerInfoPreferenceController = mOwnerInfoPreferenceController =
new OwnerInfoPreferenceController(context, this, lifecycle); new OwnerInfoPreferenceController(context, this, lifecycle);
controllers.add(mOwnerInfoPreferenceController); controllers.add(mOwnerInfoPreferenceController);
@@ -147,8 +144,6 @@ public class LockscreenDashboardFragment extends DashboardFragment
Context context) { Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>(); final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new LockScreenNotificationPreferenceController(context)); controllers.add(new LockScreenNotificationPreferenceController(context));
controllers.add(new AddUserWhenLockedPreferenceController(context,
KEY_ADD_USER_FROM_LOCK_SCREEN, null /* lifecycle */));
controllers.add(new OwnerInfoPreferenceController( controllers.add(new OwnerInfoPreferenceController(
context, null /* fragment */, null /* lifecycle */)); context, null /* fragment */, null /* lifecycle */));
return controllers; return controllers;

View File

@@ -16,72 +16,53 @@
package com.android.settings.users; package com.android.settings.users;
import android.content.Context; import android.content.Context;
import android.provider.Settings.Global; import android.provider.Settings;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.RestrictedSwitchPreference;
import androidx.preference.Preference; import androidx.preference.Preference;
import com.android.settings.core.PreferenceControllerMixin; public class AddUserWhenLockedPreferenceController extends TogglePreferenceController {
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
public class AddUserWhenLockedPreferenceController extends AbstractPreferenceController
implements PreferenceControllerMixin, Preference.OnPreferenceChangeListener,
LifecycleObserver, OnPause, OnResume {
private final String mPrefKey;
private final UserCapabilities mUserCaps; private final UserCapabilities mUserCaps;
private boolean mShouldUpdateUserList;
public AddUserWhenLockedPreferenceController(Context context, String key, Lifecycle lifecycle) { public AddUserWhenLockedPreferenceController(Context context, String key) {
super(context); super(context, key);
mPrefKey = key;
mUserCaps = UserCapabilities.create(context); mUserCaps = UserCapabilities.create(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
} }
@Override @Override
public void updateState(Preference preference) { public void updateState(Preference preference) {
RestrictedSwitchPreference restrictedSwitchPreference = super.updateState(preference);
mUserCaps.updateAddUserCapabilities(mContext);
final RestrictedSwitchPreference restrictedSwitchPreference =
(RestrictedSwitchPreference) preference; (RestrictedSwitchPreference) preference;
int value = Global.getInt(mContext.getContentResolver(), Global.ADD_USERS_WHEN_LOCKED, 0);
restrictedSwitchPreference.setChecked(value == 1);
restrictedSwitchPreference.setDisabledByAdmin( restrictedSwitchPreference.setDisabledByAdmin(
mUserCaps.disallowAddUser() ? mUserCaps.getEnforcedAdmin() : null); mUserCaps.disallowAddUser() ? mUserCaps.getEnforcedAdmin() : null);
restrictedSwitchPreference.setVisible(mUserCaps.mUserSwitcherEnabled);
} }
@Override @Override
public boolean onPreferenceChange(Preference preference, Object newValue) { public int getAvailabilityStatus() {
Boolean value = (Boolean) newValue; if (!mUserCaps.isAdmin()) {
Global.putInt(mContext.getContentResolver(), return DISABLED_FOR_USER;
Global.ADD_USERS_WHEN_LOCKED, value != null && value ? 1 : 0); } else if (mUserCaps.disallowAddUser() || mUserCaps.disallowAddUserSetByAdmin()) {
return true; return DISABLED_FOR_USER;
} } else {
return mUserCaps.mUserSwitcherEnabled ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
@Override
public void onPause() {
mShouldUpdateUserList = true;
}
@Override
public void onResume() {
if (mShouldUpdateUserList) {
mUserCaps.updateAddUserCapabilities(mContext);
} }
} }
@Override @Override
public boolean isAvailable() { public boolean isChecked() {
return mUserCaps.isAdmin() && return Settings.Global.getInt(mContext.getContentResolver(),
(!mUserCaps.disallowAddUser() || mUserCaps.disallowAddUserSetByAdmin()); Settings.Global.ADD_USERS_WHEN_LOCKED, 0) == 1;
} }
@Override @Override
public String getPreferenceKey() { public boolean setChecked(boolean isChecked) {
return mPrefKey; return Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADD_USERS_WHEN_LOCKED, isChecked ? 1 : 0);
} }
} }

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2018 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 android.content.Context;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.FooterPreferenceMixin;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
public class MultiUserFooterPreferenceController extends BasePreferenceController {
@VisibleForTesting
final UserCapabilities mUserCaps;
private FooterPreferenceMixin mFooterMixin;
public MultiUserFooterPreferenceController(Context context) {
super(context, "dummy_key");
mUserCaps = UserCapabilities.create(context);
}
public MultiUserFooterPreferenceController setFooterMixin(FooterPreferenceMixin footerMixin) {
mFooterMixin = footerMixin;
return this;
}
@Override
public int getAvailabilityStatus() {
return (mUserCaps.mEnabled && !mUserCaps.mUserSwitcherEnabled)
? AVAILABLE_UNSEARCHABLE
: DISABLED_FOR_USER;
}
@Override
public void updateState(Preference preference) {
mUserCaps.updateAddUserCapabilities(mContext);
final FooterPreference pref = mFooterMixin.createFooterPreference();
pref.setTitle(R.string.user_settings_footer_text);
pref.setVisible(isAvailable());
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright (C) 2018 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 android.content.Context;
import android.provider.Settings;
import android.util.Log;
import com.android.settings.widget.SwitchWidgetController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
public class MultiUserSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener,
LifecycleObserver, OnStart, OnStop {
interface OnMultiUserSwitchChangedListener {
void onMultiUserSwitchChanged(boolean newState);
}
private static final String TAG = "MultiUserSwitchBarCtrl";
private final Context mContext;
private final SwitchWidgetController mSwitchBar;
private final UserCapabilities mUserCapabilities;
private final OnMultiUserSwitchChangedListener mListener;
MultiUserSwitchBarController(Context context, SwitchWidgetController switchBar,
OnMultiUserSwitchChangedListener listener) {
mContext = context;
mSwitchBar = switchBar;
mListener = listener;
mUserCapabilities = UserCapabilities.create(context);
mSwitchBar.setChecked(mUserCapabilities.mUserSwitcherEnabled);
mSwitchBar.setEnabled(!mUserCapabilities.mDisallowSwitchUser
&& !mUserCapabilities.mIsGuest && mUserCapabilities.isAdmin());
mSwitchBar.setListener(this);
}
@Override
public void onStart() {
mSwitchBar.startListening();
}
@Override
public void onStop() {
mSwitchBar.stopListening();
}
@Override
public boolean onSwitchToggled(boolean isChecked) {
Log.d(TAG, "Toggling multi-user feature enabled state to: " + isChecked);
final boolean success = Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.USER_SWITCHER_ENABLED, isChecked ? 1 : 0);
if (success && mListener != null) {
mListener.onMultiUserSwitchChanged(isChecked);
}
return success;
}
}

View File

@@ -22,6 +22,7 @@ import android.content.pm.UserInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.provider.Settings; import android.provider.Settings;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
@@ -31,13 +32,15 @@ public class UserCapabilities {
boolean mCanAddRestrictedProfile = true; boolean mCanAddRestrictedProfile = true;
boolean mIsAdmin; boolean mIsAdmin;
boolean mIsGuest; boolean mIsGuest;
boolean mUserSwitcherEnabled;
boolean mCanAddGuest; boolean mCanAddGuest;
boolean mDisallowAddUser; boolean mDisallowAddUser;
boolean mDisallowAddUserSetByAdmin; boolean mDisallowAddUserSetByAdmin;
boolean mDisallowSwitchUser; boolean mDisallowSwitchUser;
RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin; RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
private UserCapabilities() {} private UserCapabilities() {
}
public static UserCapabilities create(Context context) { public static UserCapabilities create(Context context) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -62,14 +65,15 @@ public class UserCapabilities {
} }
public void updateAddUserCapabilities(Context context) { public void updateAddUserCapabilities(Context context) {
final UserManager userManager =
(UserManager) context.getSystemService(Context.USER_SERVICE);
mEnforcedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(context, mEnforcedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(context,
UserManager.DISALLOW_ADD_USER, UserHandle.myUserId()); UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
final boolean hasBaseUserRestriction = RestrictedLockUtils.hasBaseUserRestriction( final boolean hasBaseUserRestriction = RestrictedLockUtils.hasBaseUserRestriction(
context, UserManager.DISALLOW_ADD_USER, UserHandle.myUserId()); context, UserManager.DISALLOW_ADD_USER, UserHandle.myUserId());
mDisallowAddUserSetByAdmin = mDisallowAddUserSetByAdmin = mEnforcedAdmin != null && !hasBaseUserRestriction;
mEnforcedAdmin != null && !hasBaseUserRestriction; mDisallowAddUser = (mEnforcedAdmin != null || hasBaseUserRestriction);
mDisallowAddUser = mUserSwitcherEnabled = userManager.isUserSwitcherEnabled();
(mEnforcedAdmin != null || hasBaseUserRestriction);
mCanAddUser = true; mCanAddUser = true;
if (!mIsAdmin || UserManager.getMaxSupportedUsers() < 2 if (!mIsAdmin || UserManager.getMaxSupportedUsers() < 2
|| !UserManager.supportsMultipleUsers() || !UserManager.supportsMultipleUsers()
@@ -81,7 +85,6 @@ public class UserCapabilities {
context.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) == 1; context.getContentResolver(), Settings.Global.ADD_USERS_WHEN_LOCKED, 0) == 1;
mCanAddGuest = !mIsGuest && !mDisallowAddUser && canAddUsersWhenLocked; mCanAddGuest = !mIsGuest && !mDisallowAddUser && canAddUsersWhenLocked;
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mDisallowSwitchUser = userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH); mDisallowSwitchUser = userManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH);
} }

View File

@@ -48,13 +48,13 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.widget.SimpleAdapter; import android.widget.SimpleAdapter;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.UserIcons; import com.android.internal.util.UserIcons;
import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils; import com.android.settings.Utils;
import com.android.settings.core.SubSettingLauncher; import com.android.settings.core.SubSettingLauncher;
@@ -62,6 +62,8 @@ import com.android.settings.dashboard.SummaryLoader;
import com.android.settings.password.ChooseLockGeneric; import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settings.search.Indexable; import com.android.settings.search.Indexable;
import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.SwitchBarController;
import com.android.settingslib.RestrictedLockUtils; import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedPreference; import com.android.settingslib.RestrictedPreference;
@@ -78,7 +80,6 @@ import java.util.List;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import androidx.preference.Preference; import androidx.preference.Preference;
import androidx.preference.Preference.OnPreferenceClickListener;
import androidx.preference.PreferenceGroup; import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen; import androidx.preference.PreferenceScreen;
@@ -92,7 +93,9 @@ import androidx.preference.PreferenceScreen;
*/ */
@SearchIndexable @SearchIndexable
public class UserSettings extends SettingsPreferenceFragment public class UserSettings extends SettingsPreferenceFragment
implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener, implements Preference.OnPreferenceClickListener, View.OnClickListener,
MultiUserSwitchBarController.OnMultiUserSwitchChangedListener,
DialogInterface.OnDismissListener,
EditUserInfoController.OnContentChangedCallback, Indexable { EditUserInfoController.OnContentChangedCallback, Indexable {
private static final String TAG = "UserSettings"; private static final String TAG = "UserSettings";
@@ -155,8 +158,10 @@ public class UserSettings extends SettingsPreferenceFragment
private SparseArray<Bitmap> mUserIcons = new SparseArray<>(); private SparseArray<Bitmap> mUserIcons = new SparseArray<>();
private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>(); private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
private MultiUserSwitchBarController mSwitchBarController;
private EditUserInfoController mEditUserInfoController = new EditUserInfoController(); private EditUserInfoController mEditUserInfoController = new EditUserInfoController();
private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
private MultiUserFooterPreferenceController mMultiUserFooterPreferenceController;
// A place to cache the generated default avatar // A place to cache the generated default avatar
private Drawable mDefaultIconDrawable; private Drawable mDefaultIconDrawable;
@@ -198,20 +203,37 @@ public class UserSettings extends SettingsPreferenceFragment
return MetricsEvent.USER; return MetricsEvent.USER;
} }
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Assume we are in a SettingsActivity. This is only safe because we currently use
// SettingsActivity as base for all preference fragments.
final SettingsActivity activity = (SettingsActivity) getActivity();
final SwitchBar switchBar = activity.getSwitchBar();
mSwitchBarController = new MultiUserSwitchBarController(activity,
new SwitchBarController(switchBar), this /* listener */);
getLifecycle().addObserver(mSwitchBarController);
switchBar.show();
}
@Override @Override
public void onCreate(Bundle icicle) { public void onCreate(Bundle icicle) {
super.onCreate(icicle); super.onCreate(icicle);
addPreferencesFromResource(R.xml.user_settings); addPreferencesFromResource(R.xml.user_settings);
final Activity activity = getActivity(); final Activity activity = getActivity();
if (!Utils.isDeviceProvisioned(getActivity())) { if (!Utils.isDeviceProvisioned(activity)) {
activity.finish(); activity.finish();
return; return;
} }
mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController( mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
activity, KEY_ADD_USER_WHEN_LOCKED, getLifecycle()); activity, KEY_ADD_USER_WHEN_LOCKED);
mMultiUserFooterPreferenceController = new MultiUserFooterPreferenceController(activity)
.setFooterMixin(mFooterPreferenceMixin);
final PreferenceScreen screen = getPreferenceScreen(); final PreferenceScreen screen = getPreferenceScreen();
mAddUserWhenLockedPreferenceController.displayPreference(screen); mAddUserWhenLockedPreferenceController.displayPreference(screen);
mMultiUserFooterPreferenceController.displayPreference(screen);
screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey()) screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
.setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController); .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
@@ -246,7 +268,7 @@ public class UserSettings extends SettingsPreferenceFragment
mAddUser = (RestrictedPreference) findPreference(KEY_ADD_USER); mAddUser = (RestrictedPreference) findPreference(KEY_ADD_USER);
mAddUser.useAdminDisabledSummary(false); mAddUser.useAdminDisabledSummary(false);
// Determine if add user/profile button should be visible // Determine if add user/profile button should be visible
if (mUserCaps.mCanAddUser && Utils.isDeviceProvisioned(getActivity())) { if (mUserCaps.mCanAddUser && Utils.isDeviceProvisioned(activity)) {
mAddUser.setVisible(true); mAddUser.setVisible(true);
mAddUser.setOnPreferenceClickListener(this); mAddUser.setOnPreferenceClickListener(this);
// change label to only mention user, if restricted profiles are not supported // change label to only mention user, if restricted profiles are not supported
@@ -260,8 +282,7 @@ public class UserSettings extends SettingsPreferenceFragment
activity.registerReceiverAsUser( activity.registerReceiverAsUser(
mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler); mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler);
loadProfile(); updateUI();
updateUserList();
mShouldUpdateUserList = false; mShouldUpdateUserList = false;
} }
@@ -280,9 +301,7 @@ public class UserSettings extends SettingsPreferenceFragment
} }
if (mShouldUpdateUserList) { if (mShouldUpdateUserList) {
mUserCaps.updateAddUserCapabilities(getActivity()); updateUI();
loadProfile();
updateUserList();
} }
} }
@@ -343,6 +362,17 @@ public class UserSettings extends SettingsPreferenceFragment
} }
} }
@Override
public void onMultiUserSwitchChanged(boolean newState) {
updateUI();
}
private void updateUI() {
mUserCaps.updateAddUserCapabilities(getActivity());
loadProfile();
updateUserList();
}
/** /**
* Loads profile information for the current user. * Loads profile information for the current user.
*/ */
@@ -909,8 +939,6 @@ public class UserSettings extends SettingsPreferenceFragment
loadIconsAsync(missingIcons); loadIconsAsync(missingIcons);
} }
// Remove everything from mUserListCategory and add new users.
mUserListCategory.removeAll();
// If profiles are supported, mUserListCategory will have a special title // If profiles are supported, mUserListCategory will have a special title
if (mUserCaps.mCanAddRestrictedProfile) { if (mUserCaps.mCanAddRestrictedProfile) {
mUserListCategory.setTitle(R.string.user_list_title); mUserListCategory.setTitle(R.string.user_list_title);
@@ -918,6 +946,20 @@ public class UserSettings extends SettingsPreferenceFragment
mUserListCategory.setTitle(null); mUserListCategory.setTitle(null);
} }
// Remove everything from mUserListCategory and add new users.
mUserListCategory.removeAll();
// If multi-user is disabled, just show footer and return.
final Preference addUserOnLockScreen = getPreferenceScreen().findPreference(
mAddUserWhenLockedPreferenceController.getPreferenceKey());
mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen);
mMultiUserFooterPreferenceController.updateState(null /* preference */);
mAddUser.setVisible(mUserCaps.mUserSwitcherEnabled);
mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled);
if (!mUserCaps.mUserSwitcherEnabled) {
return;
}
for (UserPreference userPreference : userPreferences) { for (UserPreference userPreference : userPreferences) {
userPreference.setOrder(Preference.DEFAULT_ORDER); userPreference.setOrder(Preference.DEFAULT_ORDER);
mUserListCategory.addPreference(userPreference); mUserListCategory.addPreference(userPreference);
@@ -925,7 +967,7 @@ public class UserSettings extends SettingsPreferenceFragment
// Append Add user to the end of the list // Append Add user to the end of the list
if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) && if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) &&
Utils.isDeviceProvisioned(getActivity())) { Utils.isDeviceProvisioned(context)) {
boolean moreUsers = mUserManager.canAddMoreUsers(); boolean moreUsers = mUserManager.canAddMoreUsers();
mAddUser.setEnabled(moreUsers && !mAddingUser); mAddUser.setEnabled(moreUsers && !mAddingUser);
if (!moreUsers) { if (!moreUsers) {
@@ -938,7 +980,6 @@ public class UserSettings extends SettingsPreferenceFragment
mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null); mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null);
} }
} }
} }
private int getMaxRealUsers() { private int getMaxRealUsers() {
@@ -1190,8 +1231,7 @@ public class UserSettings extends SettingsPreferenceFragment
@Override @Override
public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId) { public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId) {
final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId); final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId);
new AddUserWhenLockedPreferenceController( new AddUserWhenLockedPreferenceController(context, KEY_ADD_USER_WHEN_LOCKED)
context, KEY_ADD_USER_WHEN_LOCKED, null /* lifecycle */)
.updateNonIndexableKeys(niks); .updateNonIndexableKeys(niks);
new AutoSyncDataPreferenceController(context, null /* parent */) new AutoSyncDataPreferenceController(context, null /* parent */)
.updateNonIndexableKeys(niks); .updateNonIndexableKeys(niks);

View File

@@ -47,6 +47,8 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
private final Set<Integer> mManagedProfiles = new HashSet<>(); private final Set<Integer> mManagedProfiles = new HashSet<>();
private boolean mIsQuietModeEnabled = false; private boolean mIsQuietModeEnabled = false;
private int[] profileIdsForUser; private int[] profileIdsForUser;
private boolean mUserSwitchEnabled;
@Resetter @Resetter
public void reset() { public void reset() {
@@ -56,6 +58,7 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
mRestrictionSources.clear(); mRestrictionSources.clear();
mManagedProfiles.clear(); mManagedProfiles.clear();
mIsQuietModeEnabled = false; mIsQuietModeEnabled = false;
mUserSwitchEnabled = false;
} }
public void setUserInfo(int userHandle, UserInfo userInfo) { public void setUserInfo(int userHandle, UserInfo userInfo) {
@@ -136,4 +139,13 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
public void setProfileIdsWithDisabled(int[] profileIds) { public void setProfileIdsWithDisabled(int[] profileIds) {
profileIdsForUser = profileIds; profileIdsForUser = profileIds;
} }
@Implementation
public boolean isUserSwitcherEnabled() {
return mUserSwitchEnabled;
}
public void setUserSwitcherEnabled(boolean userSwitchEnabled) {
mUserSwitchEnabled = userSwitchEnabled;
}
} }

View File

@@ -17,58 +17,59 @@ package com.android.settings.users;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import androidx.lifecycle.LifecycleOwner;
import android.content.Context; import android.content.Context;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.UserManager;
import android.provider.Settings.Global; import android.provider.Settings.Global;
import androidx.preference.PreferenceScreen;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUserManager;
import com.android.settingslib.RestrictedSwitchPreference; import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowApplication; import org.robolectric.shadows.ShadowApplication;
import androidx.preference.PreferenceScreen;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class})
public class AddUserWhenLockedPreferenceControllerTest { public class AddUserWhenLockedPreferenceControllerTest {
@Mock(answer = RETURNS_DEEP_STUBS) @Mock(answer = RETURNS_DEEP_STUBS)
private PreferenceScreen mScreen; private PreferenceScreen mScreen;
@Mock(answer = RETURNS_DEEP_STUBS) @Mock(answer = RETURNS_DEEP_STUBS)
private UserInfo mUserInfo; private UserInfo mUserInfo;
@Mock(answer = RETURNS_DEEP_STUBS)
private UserManager mUserManager;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private Context mContext; private Context mContext;
private ShadowUserManager mUserManager;
private AddUserWhenLockedPreferenceController mController; private AddUserWhenLockedPreferenceController mController;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
ShadowApplication shadowContext = ShadowApplication.getInstance(); ShadowApplication shadowContext = ShadowApplication.getInstance();
shadowContext.setSystemService(Context.USER_SERVICE, mUserManager); mUserManager = ShadowUserManager.getShadow();
mContext = shadowContext.getApplicationContext(); mContext = shadowContext.getApplicationContext();
mLifecycleOwner = () -> mLifecycle; mController = new AddUserWhenLockedPreferenceController(mContext, "fake_key");
mLifecycle = new Lifecycle(mLifecycleOwner); }
mController = new AddUserWhenLockedPreferenceController(mContext, "fake_key", mLifecycle);
@After
public void tearDown() {
mUserManager.reset();
} }
@Test @Test
public void displayPref_NotAdmin_shouldNotDisplay() { public void displayPref_NotAdmin_shouldNotDisplay() {
when(mUserManager.getUserInfo(anyInt())).thenReturn(mUserInfo); mUserManager.setUserInfo(0, mUserInfo);
when(mUserInfo.isAdmin()).thenReturn(false); when(mUserInfo.isAdmin()).thenReturn(false);
final RestrictedSwitchPreference preference = mock(RestrictedSwitchPreference.class); final RestrictedSwitchPreference preference = mock(RestrictedSwitchPreference.class);
when(preference.getKey()).thenReturn(mController.getPreferenceKey()); when(preference.getKey()).thenReturn(mController.getPreferenceKey());

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2018 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 com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.DISABLED_FOR_USER;
import static com.google.common.truth.Truth.assertThat;
import android.content.Context;
import com.android.settings.testutils.SettingsRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RuntimeEnvironment;
@RunWith(SettingsRobolectricTestRunner.class)
public class MultiUserFooterPreferenceControllerTest {
private Context mContext;
private MultiUserFooterPreferenceController mController;
@Before
public void setUp() {
mContext = RuntimeEnvironment.application;
mController = new MultiUserFooterPreferenceController(mContext);
}
@Test
public void getAvailabilityStatus_multiUserOff_shouldReturnEnabled() {
mController.mUserCaps.mEnabled = true;
mController.mUserCaps.mUserSwitcherEnabled = false;
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
@Test
public void getAvailabilityStatus_multiUserOn_shouldReturnDisabled() {
mController.mUserCaps.mEnabled = true;
mController.mUserCaps.mUserSwitcherEnabled = true;
assertThat(mController.getAvailabilityStatus()).isEqualTo(DISABLED_FOR_USER);
}
}

View File

@@ -17,36 +17,43 @@
package com.android.settings.users; package com.android.settings.users;
import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import com.android.settings.testutils.SettingsRobolectricTestRunner; import com.android.settings.testutils.SettingsRobolectricTestRunner;
import com.android.settings.testutils.shadow.ShadowUserManager;
import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.Mock; import org.robolectric.RuntimeEnvironment;
import org.mockito.MockitoAnnotations; import org.robolectric.annotation.Config;
@RunWith(SettingsRobolectricTestRunner.class) @RunWith(SettingsRobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class})
public class UserCapabilitiesTest { public class UserCapabilitiesTest {
@Mock
private Context mContext; private Context mContext;
@Mock private ShadowUserManager mUserManager;
private UserManager mUserManager;
@Before @Before
public void setUp() { public void setUp() {
MockitoAnnotations.initMocks(this); mContext = RuntimeEnvironment.application;
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); mUserManager = ShadowUserManager.getShadow();
}
@After
public void tearDown() {
mUserManager.reset();
} }
@Test @Test
public void disallowUserSwitchWhenRestrictionIsSet() { public void disallowUserSwitch_restrictionIsSet_true() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(true); mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()),
UserManager.DISALLOW_USER_SWITCH, true);
UserCapabilities userCapabilities = UserCapabilities.create(mContext); UserCapabilities userCapabilities = UserCapabilities.create(mContext);
userCapabilities.updateAddUserCapabilities(mContext); userCapabilities.updateAddUserCapabilities(mContext);
@@ -55,12 +62,33 @@ public class UserCapabilitiesTest {
} }
@Test @Test
public void allowUserSwitchWhenRestrictionIsNotSet() { public void disallowUserSwitch_restrictionIsNotSet_false() {
when(mUserManager.hasUserRestriction(UserManager.DISALLOW_USER_SWITCH)).thenReturn(false); mUserManager.setUserRestriction(UserHandle.of(UserHandle.myUserId()),
UserManager.DISALLOW_USER_SWITCH, false);
UserCapabilities userCapabilities = UserCapabilities.create(mContext); UserCapabilities userCapabilities = UserCapabilities.create(mContext);
userCapabilities.updateAddUserCapabilities(mContext); userCapabilities.updateAddUserCapabilities(mContext);
assertThat(userCapabilities.mDisallowSwitchUser).isFalse(); assertThat(userCapabilities.mDisallowSwitchUser).isFalse();
} }
@Test
public void userSwitchEnabled_off() {
mUserManager.setUserSwitcherEnabled(false);
final UserCapabilities userCapabilities = UserCapabilities.create(mContext);
userCapabilities.updateAddUserCapabilities(mContext);
assertThat(userCapabilities.mUserSwitcherEnabled).isFalse();
}
@Test
public void userSwitchEnabled_on() {
mUserManager.setUserSwitcherEnabled(true);
final UserCapabilities userCapabilities = UserCapabilities.create(mContext);
userCapabilities.updateAddUserCapabilities(mContext);
assertThat(userCapabilities.mUserSwitcherEnabled).isTrue();
}
} }