diff --git a/res/drawable/ic_account_circle.xml b/res/drawable/ic_account_circle.xml
new file mode 100644
index 00000000000..2c4d5a6a188
--- /dev/null
+++ b/res/drawable/ic_account_circle.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/res/drawable/ic_guest_exit.xml b/res/drawable/ic_guest_exit.xml
new file mode 100644
index 00000000000..2f7ca09e4d2
--- /dev/null
+++ b/res/drawable/ic_guest_exit.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/res/drawable/ic_guest_reset.xml b/res/drawable/ic_guest_reset.xml
new file mode 100644
index 00000000000..243a322acc1
--- /dev/null
+++ b/res/drawable/ic_guest_reset.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f3da56d04a2..772e1367983 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -7712,6 +7712,22 @@
All apps and data in this session will be deleted.
Remove
+
+ Guest (You)
+
+ Users
+
+ Other users
+
+ Delete guest activity
+
+ Delete all guest apps and data
+ when exiting guest mode
+
+ Delete guest activity?
+
+ Apps and data from this guest session will be
+ deleted now, and all future guest activity will be deleted each time you exit guest mode
Turn on phone calls
diff --git a/res/xml/user_settings.xml b/res/xml/user_settings.xml
index ab54989128b..58910e876f1 100644
--- a/res/xml/user_settings.xml
+++ b/res/xml/user_settings.xml
@@ -26,27 +26,64 @@
settings:controller="com.android.settings.users.MultiUserTopIntroPreferenceController"/>
-
+ android:key="guest_category"
+ android:title="@string/guest_category_title"
+ android:order="2"
+ settings:searchable="false"/>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
resetGuest());
}
+ case DIALOG_CONFIRM_RESET_GUEST_AND_SWITCH_USER:
+ if (mGuestUserAutoCreated) {
+ return UserDialogs.createResetGuestDialog(getActivity(),
+ (dialog, which) -> switchUser());
+ } else {
+ return UserDialogs.createRemoveGuestDialog(getActivity(),
+ (dialog, which) -> switchUser());
+ }
}
throw new IllegalArgumentException("Unsupported dialogId " + dialogId);
}
@@ -361,6 +376,16 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
if (mUserInfo.isGuest()) {
mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_SWITCH_TO_GUEST);
}
+ if (mUserCaps.mIsGuest && mUserCaps.mIsEphemeral) {
+ int guestUserId = UserHandle.myUserId();
+ // Using markGuestForDeletion allows us to create a new guest before this one is
+ // fully removed.
+ boolean marked = mUserManager.markGuestForDeletion(guestUserId);
+ if (!marked) {
+ Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
+ return;
+ }
+ }
ActivityManager.getService().switchUser(mUserInfo.id);
} catch (RemoteException re) {
Log.e(TAG, "Error while switching to other user.");
diff --git a/src/com/android/settings/users/UserDialogs.java b/src/com/android/settings/users/UserDialogs.java
index 8549ffef407..faaff4c118e 100644
--- a/src/com/android/settings/users/UserDialogs.java
+++ b/src/com/android/settings/users/UserDialogs.java
@@ -189,7 +189,7 @@ public final class UserDialogs {
DialogInterface.OnClickListener onConfirmListener) {
return new AlertDialog.Builder(context)
.setTitle(com.android.settingslib.R.string.guest_reset_guest_dialog_title)
- .setMessage(R.string.user_exit_guest_confirm_message)
+ .setMessage(com.android.settingslib.R.string.guest_exit_dialog_message)
.setPositiveButton(
com.android.settingslib.R.string.guest_reset_guest_confirm_button,
onConfirmListener)
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 358b87b9abf..24ba12f94ca 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -47,13 +47,16 @@ import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract;
+import android.provider.Settings;
import android.text.TextUtils;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
+import android.view.WindowManagerGlobal;
import android.widget.SimpleAdapter;
import android.widget.Toast;
@@ -95,6 +98,7 @@ import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
/**
* Screen that manages the list of users on the device.
@@ -123,6 +127,14 @@ public class UserSettings extends SettingsPreferenceFragment
private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked";
private static final String KEY_MULTIUSER_TOP_INTRO = "multiuser_top_intro";
private static final String KEY_TIMEOUT_TO_USER_ZERO = "timeout_to_user_zero_preference";
+ private static final String KEY_GUEST_CATEGORY = "guest_category";
+ private static final String KEY_GUEST_RESET = "guest_reset";
+ private static final String KEY_GUEST_EXIT = "guest_exit";
+ private static final String KEY_GUEST_INFO = "guest_info";
+ private static final String KEY_REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit";
+ private static final String KEY_GUEST_USER_CATEGORY = "guest_user_category";
+
+ private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
private static final int MENU_REMOVE_USER = Menu.FIRST;
@@ -134,14 +146,18 @@ public class UserSettings extends SettingsPreferenceFragment
private static final int DIALOG_USER_CANNOT_MANAGE = 5;
private static final int DIALOG_CHOOSE_USER_TYPE = 6;
private static final int DIALOG_NEED_LOCKSCREEN = 7;
- private static final int DIALOG_CONFIRM_EXIT_GUEST = 8;
+ private static final int DIALOG_CONFIRM_REMOVE_GUEST = 8;
private static final int DIALOG_USER_PROFILE_EDITOR = 9;
private static final int DIALOG_USER_PROFILE_EDITOR_ADD_USER = 10;
private static final int DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE = 11;
- private static final int DIALOG_CONFIRM_RESET_GUEST = 12;
+ private static final int DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE = 12;
+ private static final int DIALOG_CONFIRM_RESET_AND_RESTART_GUEST = 13;
+ private static final int DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL = 14;
+ private static final int DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL = 15;
private static final int MESSAGE_UPDATE_LIST = 1;
private static final int MESSAGE_USER_CREATED = 2;
+ static final int MESSAGE_REMOVE_GUEST_ON_EXIT_CONTROLLER_GUEST_REMOVED = 3;
private static final int USER_TYPE_USER = 1;
private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
@@ -165,6 +181,16 @@ public class UserSettings extends SettingsPreferenceFragment
@VisibleForTesting
PreferenceGroup mUserListCategory;
@VisibleForTesting
+ PreferenceGroup mGuestUserCategory;
+ @VisibleForTesting
+ PreferenceGroup mGuestCategory;
+ @VisibleForTesting
+ Preference mGuestResetPreference;
+ @VisibleForTesting
+ Preference mGuestExitPreference;
+ @VisibleForTesting
+ Preference mGuestInfoPreference;
+ @VisibleForTesting
UserPreference mMePreference;
@VisibleForTesting
RestrictedPreference mAddGuest;
@@ -189,6 +215,7 @@ public class UserSettings extends SettingsPreferenceFragment
private EditUserInfoController mEditUserInfoController =
new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY);
private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
+ private RemoveGuestOnExitPreferenceController mRemoveGuestOnExitPreferenceController;
private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController;
private TimeoutToUserZeroPreferenceController mTimeoutToUserZeroPreferenceController;
private UserCreatingDialog mUserCreatingDialog;
@@ -213,6 +240,12 @@ public class UserSettings extends SettingsPreferenceFragment
case MESSAGE_USER_CREATED:
onUserCreated(msg.arg1);
break;
+ case MESSAGE_REMOVE_GUEST_ON_EXIT_CONTROLLER_GUEST_REMOVED:
+ updateUserList();
+ if (mGuestUserAutoCreated) {
+ scheduleGuestCreation();
+ }
+ break;
}
}
};
@@ -245,7 +278,11 @@ public class UserSettings extends SettingsPreferenceFragment
final SettingsActivity activity = (SettingsActivity) getActivity();
final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
switchBar.setTitle(getContext().getString(R.string.multiple_users_main_switch_title));
- switchBar.show();
+ if (mUserCaps.mIsAdmin) {
+ switchBar.show();
+ } else {
+ switchBar.hide();
+ }
mSwitchBarController = new MultiUserSwitchBarController(activity,
new MainSwitchBarController(switchBar), this /* listener */);
getSettingsLifecycle().addObserver(mSwitchBarController);
@@ -267,6 +304,9 @@ public class UserSettings extends SettingsPreferenceFragment
mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
activity, KEY_ADD_USER_WHEN_LOCKED);
+ mRemoveGuestOnExitPreferenceController = new RemoveGuestOnExitPreferenceController(
+ activity, KEY_REMOVE_GUEST_ON_EXIT, this, mHandler);
+
mMultiUserTopIntroPreferenceController = new MultiUserTopIntroPreferenceController(activity,
KEY_MULTIUSER_TOP_INTRO);
@@ -275,12 +315,16 @@ public class UserSettings extends SettingsPreferenceFragment
final PreferenceScreen screen = getPreferenceScreen();
mAddUserWhenLockedPreferenceController.displayPreference(screen);
+ mRemoveGuestOnExitPreferenceController.displayPreference(screen);
mMultiUserTopIntroPreferenceController.displayPreference(screen);
mTimeoutToUserZeroPreferenceController.displayPreference(screen);
screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
.setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
+ screen.findPreference(mRemoveGuestOnExitPreferenceController.getPreferenceKey())
+ .setOnPreferenceChangeListener(mRemoveGuestOnExitPreferenceController);
+
if (icicle != null) {
if (icicle.containsKey(SAVE_REMOVING_USER)) {
mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER);
@@ -304,6 +348,18 @@ public class UserSettings extends SettingsPreferenceFragment
mMePreference.setSummary(R.string.user_admin);
}
+ mGuestCategory = findPreference(KEY_GUEST_CATEGORY);
+
+ mGuestResetPreference = findPreference(KEY_GUEST_RESET);
+ mGuestResetPreference.setOnPreferenceClickListener(this);
+
+ mGuestExitPreference = findPreference(KEY_GUEST_EXIT);
+ mGuestExitPreference.setOnPreferenceClickListener(this);
+
+ mGuestInfoPreference = findPreference(KEY_GUEST_INFO);
+
+ mGuestUserCategory = findPreference(KEY_GUEST_USER_CATEGORY);
+
mAddGuest = findPreference(KEY_ADD_GUEST);
mAddGuest.setOnPreferenceClickListener(this);
@@ -339,7 +395,8 @@ public class UserSettings extends SettingsPreferenceFragment
mAddUserWhenLockedPreferenceController.getPreferenceKey()));
mTimeoutToUserZeroPreferenceController.updateState(screen.findPreference(
mTimeoutToUserZeroPreferenceController.getPreferenceKey()));
-
+ mRemoveGuestOnExitPreferenceController.updateState(screen.findPreference(
+ mRemoveGuestOnExitPreferenceController.getPreferenceKey()));
if (mShouldUpdateUserList) {
updateUI();
}
@@ -418,6 +475,11 @@ public class UserSettings extends SettingsPreferenceFragment
updateUserList();
}
+ private boolean isEnableGuestModeUxChanges() {
+ return FeatureFlagUtils.isEnabled(getContext(),
+ FeatureFlagUtils.SETTINGS_GUEST_MODE_UX_CHANGES);
+ }
+
/**
* Loads profile information for the current user.
*/
@@ -702,7 +764,7 @@ public class UserSettings extends SettingsPreferenceFragment
.create();
return dlg;
}
- case DIALOG_CONFIRM_EXIT_GUEST: {
+ case DIALOG_CONFIRM_REMOVE_GUEST: {
Dialog dlg = new AlertDialog.Builder(context)
.setTitle(com.android.settingslib.R.string.guest_remove_guest_dialog_title)
.setMessage(R.string.user_exit_guest_confirm_message)
@@ -710,13 +772,56 @@ public class UserSettings extends SettingsPreferenceFragment
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- exitGuest();
+ clearAndExitGuest();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
return dlg;
}
+ case DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL: {
+ Dialog dlg = new AlertDialog.Builder(context)
+ .setTitle(com.android.settingslib.R.string.guest_exit_dialog_title)
+ .setMessage(com.android.settingslib.R.string.guest_exit_dialog_message)
+ .setPositiveButton(
+ com.android.settingslib.R.string.guest_exit_dialog_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ clearAndExitGuest();
+ }
+ })
+ .setNeutralButton(android.R.string.cancel, null)
+ .create();
+ return dlg;
+ }
+ case DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL: {
+ Dialog dlg = new AlertDialog.Builder(context)
+ .setTitle(
+ com.android.settingslib.R.string.guest_exit_dialog_title_non_ephemeral)
+ .setMessage(
+ com.android.settingslib
+ .R.string.guest_exit_dialog_message_non_ephemeral)
+ .setPositiveButton(
+ com.android.settingslib.R.string.guest_exit_save_data_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ exitGuest();
+ }
+ })
+ .setNegativeButton(
+ com.android.settingslib.R.string.guest_exit_clear_data_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ clearAndExitGuest();
+ }
+ })
+ .setNeutralButton(android.R.string.cancel, null)
+ .create();
+ return dlg;
+ }
case DIALOG_USER_PROFILE_EDITOR: {
return buildEditCurrentUserDialog();
}
@@ -736,14 +841,27 @@ public class UserSettings extends SettingsPreferenceFragment
}
return buildAddUserDialog(USER_TYPE_RESTRICTED_PROFILE);
}
- case DIALOG_CONFIRM_RESET_GUEST: {
- if (mGuestUserAutoCreated) {
- return UserDialogs.createResetGuestDialog(getActivity(),
- (dialog, which) -> resetGuest());
- } else {
- return UserDialogs.createRemoveGuestDialog(getActivity(),
- (dialog, which) -> resetGuest());
- }
+ case DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE: {
+ return UserDialogs.createResetGuestDialog(getActivity(),
+ (dialog, which) -> clearAndExitGuest());
+ }
+ case DIALOG_CONFIRM_RESET_AND_RESTART_GUEST: {
+ Dialog dlg = new AlertDialog.Builder(context)
+ .setTitle(
+ com.android.settingslib.R.string.guest_reset_and_restart_dialog_title)
+ .setMessage(
+ com.android.settingslib.R.string.guest_reset_and_restart_dialog_message)
+ .setPositiveButton(
+ com.android.settingslib.R.string.guest_reset_guest_confirm_button,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ resetAndRestartGuest();
+ }
+ })
+ .setNeutralButton(android.R.string.cancel, null)
+ .create();
+ return dlg;
}
default:
return null;
@@ -821,8 +939,11 @@ public class UserSettings extends SettingsPreferenceFragment
return SettingsEnums.DIALOG_USER_CHOOSE_TYPE;
case DIALOG_NEED_LOCKSCREEN:
return SettingsEnums.DIALOG_USER_NEED_LOCKSCREEN;
- case DIALOG_CONFIRM_EXIT_GUEST:
- case DIALOG_CONFIRM_RESET_GUEST:
+ case DIALOG_CONFIRM_REMOVE_GUEST:
+ case DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE:
+ case DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL:
+ case DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL:
+ case DIALOG_CONFIRM_RESET_AND_RESTART_GUEST:
return SettingsEnums.DIALOG_USER_CONFIRM_EXIT_GUEST;
case DIALOG_USER_PROFILE_EDITOR:
case DIALOG_USER_PROFILE_EDITOR_ADD_USER:
@@ -864,6 +985,18 @@ public class UserSettings extends SettingsPreferenceFragment
}
}
+ private void switchToUserId(int userId) {
+ if (!canSwitchUserNow()) {
+ Log.w(TAG, "Cannot switch current user when switching is disabled");
+ return;
+ }
+ try {
+ ActivityManager.getService().switchUser(userId);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Unable to switch user");
+ }
+ }
+
private void addUserNow(final int userType) {
Trace.beginAsyncSection("UserSettings.addUserNow", 0);
synchronized (mUserLock) {
@@ -945,36 +1078,91 @@ public class UserSettings extends SettingsPreferenceFragment
* Erase the current user (guest) and switch to another user.
*/
@VisibleForTesting
- void exitGuest() {
+ void clearAndExitGuest() {
// Just to be safe
if (!isCurrentUserGuest()) {
return;
}
mMetricsFeatureProvider.action(getActivity(),
SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
- removeThisUser();
- }
- /**
- * Erase the current user (assuming it is a guest user), and create a new one in the background
- */
- @VisibleForTesting
- void resetGuest() {
- // Just to be safe
- if (!isCurrentUserGuest()) {
- return;
- }
int guestUserId = UserHandle.myUserId();
// Using markGuestForDeletion allows us to create a new guest before this one is
- // fully removed. This could happen if someone calls scheduleGuestCreation()
- // immediately after calling this method.
+ // fully removed.
boolean marked = mUserManager.markGuestForDeletion(guestUserId);
if (!marked) {
Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
return;
}
- exitGuest();
- scheduleGuestCreation();
+
+ removeThisUser();
+ if (mGuestUserAutoCreated) {
+ scheduleGuestCreation();
+ }
+ }
+
+ /**
+ * Switch to another user.
+ */
+ private void exitGuest() {
+ // Just to be safe
+ if (!isCurrentUserGuest()) {
+ return;
+ }
+ mMetricsFeatureProvider.action(getActivity(),
+ SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
+ switchToUserId(UserHandle.USER_SYSTEM);
+ }
+
+ private int createGuest() {
+ UserInfo guest;
+ Context context = getPrefContext();
+ try {
+ guest = mUserManager.createGuest(context);
+ } catch (UserManager.UserOperationException e) {
+ Log.e(TAG, "Couldn't create guest user", e);
+ return UserHandle.USER_NULL;
+ }
+ if (guest == null) {
+ Log.e(TAG, "Couldn't create guest, most likely because there already exists one");
+ return UserHandle.USER_NULL;
+ }
+ return guest.id;
+ }
+
+ /**
+ * Remove current guest and start a new guest session
+ */
+ private void resetAndRestartGuest() {
+ // Just to be safe
+ if (!isCurrentUserGuest()) {
+ return;
+ }
+ int oldGuestUserId = UserHandle.myUserId();
+ // Using markGuestForDeletion allows us to create a new guest before this one is
+ // fully removed.
+ boolean marked = mUserManager.markGuestForDeletion(oldGuestUserId);
+ if (!marked) {
+ Log.w(TAG, "Couldn't mark the guest for deletion for user " + oldGuestUserId);
+ return;
+ }
+
+ try {
+ // Create a new guest in the foreground, and then immediately switch to it
+ int newGuestUserId = createGuest();
+ if (newGuestUserId == UserHandle.USER_NULL) {
+ Log.e(TAG, "Could not create new guest, switching back to system user");
+ switchToUserId(UserHandle.USER_SYSTEM);
+ mUserManager.removeUser(oldGuestUserId);
+ WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null);
+ return;
+ }
+ switchToUserId(newGuestUserId);
+ mUserManager.removeUser(oldGuestUserId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't remove guest because ActivityManager or WindowManager is dead");
+ return;
+ }
}
/**
@@ -1009,18 +1197,28 @@ public class UserSettings extends SettingsPreferenceFragment
if (context == null) {
return;
}
- final List users = mUserManager.getAliveUsers();
-
+ final List users = mUserManager.getAliveUsers()
+ // Only users that can be switched to should show up here.
+ // e.g. Managed profiles appear under Accounts Settings instead
+ .stream().filter(UserInfo::supportsSwitchToByUser)
+ .collect(Collectors.toList());
final ArrayList missingIcons = new ArrayList<>();
final ArrayList userPreferences = new ArrayList<>();
- userPreferences.add(mMePreference);
+
+ // mMePreference shows a icon for current user. However when current user is a guest, we
+ // don't show the guest user icon, instead we show two preferences for guest user to
+ // exit and reset itself. Hence we don't add mMePreference, i.e. guest user to the
+ // list of users visible in the UI.
+ if (!mUserCaps.mIsGuest) {
+ 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.
- // e.g. Managed profiles appear under Accounts Settings instead
+ if (user.isGuest()) {
+ // Guest user is added to guest category via updateGuestCategory
+ // and not to user list so skip guest here
continue;
}
UserPreference pref;
@@ -1033,21 +1231,9 @@ public class UserSettings extends SettingsPreferenceFragment
pref.setOnPreferenceClickListener(this);
pref.setEnabled(canOpenUserDetails);
pref.setSelectable(true);
-
- if (user.isGuest()) {
- pref.setIcon(getEncircledDefaultIcon());
- pref.setKey(KEY_USER_GUEST);
- if (mUserCaps.mDisallowSwitchUser) {
- pref.setDisabledByAdmin(
- RestrictedLockUtilsInternal.getDeviceOwner(context));
- } else {
- pref.setDisabledByAdmin(null);
- }
- } else {
- pref.setKey("id=" + user.id);
- if (user.isAdmin()) {
- pref.setSummary(R.string.user_admin);
- }
+ pref.setKey("id=" + user.id);
+ if (user.isAdmin()) {
+ pref.setSummary(R.string.user_admin);
}
}
if (pref == null) {
@@ -1102,12 +1288,13 @@ public class UserSettings extends SettingsPreferenceFragment
loadIconsAsync(missingIcons);
}
- // If profiles are supported, mUserListCategory will have a special title
+ // If restricted profiles are supported, mUserListCategory will have a special title
if (mUserCaps.mCanAddRestrictedProfile) {
mUserListCategory.setTitle(R.string.user_list_title);
+ } else if (isCurrentUserGuest()) {
+ mUserListCategory.setTitle(R.string.other_user_category_title);
} else {
- mUserListCategory.setTitle(null);
- mUserListCategory.setLayoutResource(R.layout.empty_view);
+ mUserListCategory.setTitle(R.string.user_category_title);
}
// Remove everything from mUserListCategory and add new users.
@@ -1122,8 +1309,8 @@ public class UserSettings extends SettingsPreferenceFragment
mMultiUserTopIntroPreferenceController.getPreferenceKey());
mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPrefence);
mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled);
-
- updateAddGuest(context, users.stream().anyMatch(UserInfo::isGuest));
+ updateGuestPreferences();
+ updateGuestCategory(context, users);
updateAddUser(context);
updateAddSupervisedUser(context);
@@ -1152,14 +1339,127 @@ public class UserSettings extends SettingsPreferenceFragment
return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK;
}
- private void updateAddGuest(Context context, boolean isGuestAlreadyCreated) {
+ private void updateGuestPreferences() {
+ // reset guest and exit guest preferences are shown only in guest mode.
+ // For all other users these are not visible.
+ mGuestCategory.setVisible(false);
+ mGuestResetPreference.setVisible(false);
+ mGuestExitPreference.setVisible(false);
+ mGuestInfoPreference.setVisible(false);
+ if (!isCurrentUserGuest()) {
+ return;
+ }
+ mGuestCategory.setVisible(true);
+ mGuestExitPreference.setVisible(true);
+ if (isEnableGuestModeUxChanges()) {
+ mGuestResetPreference.setVisible(true);
+ mGuestInfoPreference.setVisible(true);
+
+ boolean isGuestFirstLogin = Settings.Secure.getIntForUser(
+ getContext().getContentResolver(),
+ SETTING_GUEST_HAS_LOGGED_IN,
+ 0,
+ UserHandle.myUserId()) <= 1;
+ String guestInfoText;
+ if (mUserCaps.mIsEphemeral) {
+ guestInfoText = getContext().getString(
+ R.string.guest_notification_ephemeral);
+ } else if (isGuestFirstLogin) {
+ guestInfoText = getContext().getString(
+ R.string.guest_notification_non_ephemeral);
+ } else {
+ guestInfoText = getContext().getString(
+ R.string.guest_notification_non_ephemeral_non_first_login);
+ }
+ mGuestInfoPreference.setSummary(guestInfoText);
+ } else {
+ mGuestExitPreference.setIcon(getEncircledDefaultIcon());
+ mGuestExitPreference.setTitle(
+ mGuestUserAutoCreated
+ ? com.android.settingslib.R.string.guest_reset_guest
+ : com.android.settingslib.R.string.guest_exit_guest);
+ }
+ }
+
+ private void updateGuestCategory(Context context, List users) {
+ // show guest category title and related guest preferences
+ // - if guest is created, then show guest user preference
+ // - if guest is not created and its allowed to create guest,
+ // then show "add guest" preference
+ // - if allowed, show "reset guest on exit" preference
+ // - if there is nothing to show, then make the guest category as not visible
+ // - guest category is not visible for guest user.
+ UserPreference pref = null;
+ boolean isGuestAlreadyCreated = false;
+ boolean canOpenUserDetails =
+ mUserCaps.mIsAdmin || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser);
+
+ mGuestUserCategory.removeAll();
+ mGuestUserCategory.setVisible(false);
+ for (UserInfo user : users) {
+ if (!user.isGuest() || !user.isEnabled()) {
+ // Only look at enabled, guest users
+ continue;
+ }
+ final Context prefContext = getPrefContext();
+ pref = new UserPreference(prefContext, null, user.id);
+ pref.setTitle(user.name);
+ pref.setOnPreferenceClickListener(this);
+ pref.setEnabled(canOpenUserDetails);
+ pref.setSelectable(true);
+ if (isEnableGuestModeUxChanges()) {
+ pref.setIcon(getContext().getDrawable(R.drawable.ic_account_circle));
+ } else {
+ pref.setIcon(getEncircledDefaultIcon());
+ }
+ pref.setKey(KEY_USER_GUEST);
+ pref.setOrder(Preference.DEFAULT_ORDER);
+ if (mUserCaps.mDisallowSwitchUser) {
+ pref.setDisabledByAdmin(
+ RestrictedLockUtilsInternal.getDeviceOwner(context));
+ } else {
+ pref.setDisabledByAdmin(null);
+ }
+ if (mUserCaps.mUserSwitcherEnabled) {
+ mGuestUserCategory.addPreference(pref);
+ // guest user preference is shown hence also make guest category visible
+ mGuestUserCategory.setVisible(true);
+ }
+ isGuestAlreadyCreated = true;
+ }
+ boolean isVisible = updateAddGuestPreference(context, isGuestAlreadyCreated);
+ if (isVisible) {
+ // "add guest" preference is shown hence also make guest category visible
+ mGuestUserCategory.setVisible(true);
+ }
+ final Preference removeGuestOnExit = getPreferenceScreen().findPreference(
+ mRemoveGuestOnExitPreferenceController.getPreferenceKey());
+ mRemoveGuestOnExitPreferenceController.updateState(removeGuestOnExit);
+ if (mRemoveGuestOnExitPreferenceController.isAvailable()) {
+ // "reset guest on exit" preference is shown hence also make guest category visible
+ mGuestUserCategory.setVisible(true);
+ }
+ if (mUserCaps.mIsGuest) {
+ // guest category is not visible for guest user.
+ mGuestUserCategory.setVisible(false);
+ }
+ }
+
+ private boolean updateAddGuestPreference(Context context, boolean isGuestAlreadyCreated) {
+ boolean isVisible = false;
if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest
&& mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_GUEST)
&& WizardManagerHelper.isDeviceProvisioned(context)
&& mUserCaps.mUserSwitcherEnabled) {
+ isVisible = true;
mAddGuest.setVisible(true);
- Drawable icon = context.getDrawable(R.drawable.ic_account_circle);
- mAddGuest.setIcon(centerAndTint(icon));
+ // when isEnableGuestModeUxChanges() is true, the icon is set via the layout xml
+ // In com.android.settings.users.UserSettingsTest
+ // we disable the check for setIcon being called
+ if (!isEnableGuestModeUxChanges()) {
+ Drawable icon = context.getDrawable(R.drawable.ic_account_circle);
+ mAddGuest.setIcon(centerAndTint(icon));
+ }
mAddGuest.setSelectable(true);
if (mGuestUserAutoCreated && mGuestCreationScheduled.get()) {
mAddGuest.setTitle(com.android.internal.R.string.guest_name);
@@ -1172,19 +1472,26 @@ public class UserSettings extends SettingsPreferenceFragment
} else {
mAddGuest.setVisible(false);
}
+ return isVisible;
}
private void updateAddUser(Context context) {
updateAddUserCommon(context, mAddUser, mUserCaps.mCanAddRestrictedProfile);
- Drawable icon = context.getDrawable(R.drawable.ic_account_circle_filled);
- mAddUser.setIcon(centerAndTint(icon));
+ // when isEnableGuestModeUxChanges() is true, the icon is set via the layout xml
+ if (!isEnableGuestModeUxChanges()) {
+ Drawable icon = context.getDrawable(R.drawable.ic_account_circle_filled);
+ mAddUser.setIcon(centerAndTint(icon));
+ }
}
private void updateAddSupervisedUser(Context context) {
if (!TextUtils.isEmpty(mConfigSupervisedUserCreationPackage)) {
updateAddUserCommon(context, mAddSupervisedUser, false);
- Drawable icon = context.getDrawable(R.drawable.ic_add_supervised_user);
- mAddSupervisedUser.setIcon(centerAndTint(icon));
+ // when isEnableGuestModeUxChanges() is true, the icon is set via the layout xml
+ if (!isEnableGuestModeUxChanges()) {
+ Drawable icon = context.getDrawable(R.drawable.ic_add_supervised_user);
+ mAddSupervisedUser.setIcon(centerAndTint(icon));
+ }
} else {
mAddSupervisedUser.setVisible(false);
}
@@ -1280,17 +1587,36 @@ public class UserSettings extends SettingsPreferenceFragment
@Override
public boolean onPreferenceClick(Preference pref) {
- if (pref == mMePreference) {
- if (isCurrentUserGuest()) {
- if (mGuestUserAutoCreated) {
- showDialog(DIALOG_CONFIRM_RESET_GUEST);
- } else {
- showDialog(DIALOG_CONFIRM_EXIT_GUEST);
+ if (isCurrentUserGuest()) {
+ if (isEnableGuestModeUxChanges()) {
+ if (mGuestResetPreference != null && pref == mGuestResetPreference) {
+ showDialog(DIALOG_CONFIRM_RESET_AND_RESTART_GUEST);
+ return true;
+ }
+ if (mGuestExitPreference != null && pref == mGuestExitPreference) {
+ if (mUserCaps.mIsEphemeral) {
+ showDialog(DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL);
+ } else {
+ showDialog(DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL);
+ }
+ return true;
}
} else {
- showDialog(DIALOG_USER_PROFILE_EDITOR);
+ if (mGuestExitPreference != null && pref == mGuestExitPreference) {
+ if (mGuestUserAutoCreated) {
+ showDialog(DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE);
+ } else {
+ showDialog(DIALOG_CONFIRM_REMOVE_GUEST);
+ }
+ return true;
+ }
+ }
+ }
+ if (pref == mMePreference) {
+ if (!isCurrentUserGuest()) {
+ showDialog(DIALOG_USER_PROFILE_EDITOR);
+ return true;
}
- return true;
} else if (pref instanceof UserPreference) {
UserInfo userInfo = mUserManager.getUserInfo(((UserPreference) pref).getUserId());
openUserDetails(userInfo, false);
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 ea51370c471..b865ea6ad15 100644
--- a/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
+++ b/tests/robotests/src/com/android/settings/testutils/shadow/ShadowUserManager.java
@@ -226,4 +226,32 @@ public class ShadowUserManager extends org.robolectric.shadows.ShadowUserManager
return new UserInfo(PRIMARY_USER_ID, null, null,
UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
}
+
+ protected boolean setUserEphemeral(@UserIdInt int userId, boolean enableEphemeral) {
+ UserInfo userInfo = mUserProfileInfos.stream()
+ .filter(user -> user.id == userId)
+ .findFirst()
+ .orElse(super.getUserInfo(userId));
+
+ boolean isSuccess = false;
+ boolean isEphemeralUser =
+ (userInfo.flags & UserInfo.FLAG_EPHEMERAL) != 0;
+ boolean isEphemeralOnCreateUser =
+ (userInfo.flags & UserInfo.FLAG_EPHEMERAL_ON_CREATE)
+ != 0;
+ // when user is created in ephemeral mode via FLAG_EPHEMERAL
+ // its state cannot be changed.
+ // FLAG_EPHEMERAL_ON_CREATE is used to keep track of this state
+ if (!isEphemeralOnCreateUser) {
+ isSuccess = true;
+ if (isEphemeralUser != enableEphemeral) {
+ if (enableEphemeral) {
+ userInfo.flags |= UserInfo.FLAG_EPHEMERAL;
+ } else {
+ userInfo.flags &= ~UserInfo.FLAG_EPHEMERAL;
+ }
+ }
+ }
+ return isSuccess;
+ }
}
diff --git a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
index bb7dd757ffc..97b8175a78e 100644
--- a/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserSettingsTest.java
@@ -56,6 +56,7 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import androidx.fragment.app.FragmentActivity;
+import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
@@ -152,6 +153,8 @@ public class UserSettingsTest {
ReflectionHelpers.setField(mFragment, "mDefaultIconDrawable", mDefaultIconDrawable);
ReflectionHelpers.setField(mFragment, "mAddingUser", false);
ReflectionHelpers.setField(mFragment, "mMetricsFeatureProvider", mMetricsFeatureProvider);
+ ReflectionHelpers.setField(mFragment, "mRemoveGuestOnExitPreferenceController",
+ mock(RemoveGuestOnExitPreferenceController.class));
doReturn(mUserManager).when(mActivity).getSystemService(UserManager.class);
doReturn(mPackageManager).when(mActivity).getPackageManager();
@@ -178,6 +181,11 @@ public class UserSettingsTest {
mFragment.mAddSupervisedUser = mAddSupervisedUserPreference;
mFragment.mAddGuest = mAddGuestPreference;
mFragment.mUserListCategory = mock(PreferenceCategory.class);
+ mFragment.mGuestUserCategory = mock(PreferenceCategory.class);
+ mFragment.mGuestCategory = mock(PreferenceCategory.class);
+ mFragment.mGuestResetPreference = mock(Preference.class);
+ mFragment.mGuestExitPreference = mock(Preference.class);
+ mFragment.mGuestInfoPreference = mock(Preference.class);
}
@After
@@ -219,7 +227,7 @@ public class UserSettingsTest {
@Test
public void testExitGuest_ShouldLogAction() {
mUserCapabilities.mIsGuest = true;
- mFragment.exitGuest();
+ mFragment.clearAndExitGuest();
verify(mMetricsFeatureProvider).action(any(),
eq(SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED));
}
@@ -227,7 +235,7 @@ public class UserSettingsTest {
@Test
public void testExitGuestWhenNotGuest_ShouldNotLogAction() {
mUserCapabilities.mIsGuest = false;
- mFragment.exitGuest();
+ mFragment.clearAndExitGuest();
verify(mMetricsFeatureProvider, never()).action(any(),
eq(SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED));
}
@@ -323,7 +331,6 @@ public class UserSettingsTest {
verify(mAddGuestPreference).setVisible(true);
verify(mAddGuestPreference).setEnabled(true);
- verify(mAddGuestPreference).setIcon(any(Drawable.class));
verify(mAddGuestPreference).setSelectable(true);
}
@@ -371,7 +378,6 @@ public class UserSettingsTest {
verify(mAddGuestPreference).setVisible(true);
verify(mAddGuestPreference).setEnabled(false);
- verify(mAddGuestPreference).setIcon(any(Drawable.class));
verify(mAddGuestPreference).setSelectable(true);
}
@@ -473,9 +479,9 @@ public class UserSettingsTest {
mFragment.updateUserList();
ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class);
- verify(mFragment.mUserListCategory, times(2))
+ verify(mFragment.mGuestUserCategory, times(1))
.addPreference(captor.capture());
- UserPreference guestPref = captor.getAllValues().get(1);
+ UserPreference guestPref = captor.getAllValues().get(0);
assertThat(guestPref.getUserId()).isEqualTo(INACTIVE_GUEST_USER_ID);
assertThat(guestPref.getTitle()).isEqualTo("Guest");
assertThat(guestPref.getIcon()).isNotNull();
@@ -595,9 +601,9 @@ public class UserSettingsTest {
mFragment.updateUserList();
ArgumentCaptor captor = ArgumentCaptor.forClass(UserPreference.class);
- verify(mFragment.mUserListCategory, times(2))
+ verify(mFragment.mGuestUserCategory, times(1))
.addPreference(captor.capture());
- UserPreference userPref = captor.getAllValues().get(1);
+ UserPreference userPref = captor.getAllValues().get(0);
assertThat(userPref.getUserId()).isEqualTo(INACTIVE_GUEST_USER_ID);
assertThat(userPref.getSummary()).isNull();
}