Multiuser settings revamp for L, first pass

Include Guest settings
Ability to switch users from Settings
Manage user settings like telephony access

Bug: 15761405
Change-Id: I2cfdc7bc2703ed202aa8bf1261c304c51ce48b29
This commit is contained in:
Amith Yamasani
2014-06-30 15:31:37 +05:30
parent 5b3c3c00b0
commit 9e6ac3d25f
8 changed files with 337 additions and 35 deletions

View File

@@ -4942,6 +4942,15 @@
<string name="user_delete_user_description">Delete user</string>
<!-- Delete button text [CHAR LIMIT=25] -->
<string name="user_delete_button">Delete</string>
<!-- Title for Guest user [CHAR LIMIT=35] -->
<string name="user_guest">Guest</string>
<!-- Title of preference to enable calling[CHAR LIMIT=35] -->
<string name="user_enable_calling">Enable phone calls?</string>
<!-- Title of preference to enable calling and SMS [CHAR LIMIT=35] -->
<string name="user_enable_calling_sms">Enable phone calls and SMS?</string>
<!-- Title of preference to remove the user [CHAR LIMIT=35] -->
<string name="user_remove_user">Remove user</string>
<!-- Application Restrictions screen title [CHAR LIMIT=45] -->
<string name="application_restrictions">Allow apps and content</string>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 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.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
<SwitchPreference
android:key="enable_calling"
android:title="@string/user_enable_calling_sms"
android:persistent="false" />
<Preference
android:key="remove_user"
android:title="@string/user_remove_user" />
</PreferenceScreen>

View File

@@ -683,7 +683,7 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
app.masterEntry.activityName));
}
p.setKey(getKeyForPackage(packageName));
p.setSettingsEnabled(hasSettings || isSettingsApp);
p.setSettingsEnabled((hasSettings || isSettingsApp) && app.masterEntry == null);
p.setPersistent(false);
p.setOnPreferenceChangeListener(this);
p.setOnPreferenceClickListener(this);
@@ -702,7 +702,8 @@ public class AppRestrictionsFragment extends SettingsPreferenceFragment implemen
// Get and populate the defaults, since the user is not going to be
// able to toggle this app ON (it's ON by default and immutable).
// Only do this for restricted profiles, not single-user restrictions
if (hasSettings) {
// Also don't do this for slave icons
if (hasSettings && app.masterEntry == null) {
requestRestrictionsForApp(packageName, p, false);
}
} else if (!mNewUser && isAppEnabledForUser(pi)) {

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2014 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.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.settings.R;
public class RemoveUserUtil {
static Dialog createConfirmationDialog(Context context, int removingUserId,
DialogInterface.OnClickListener onConfirmListener) {
final UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
final UserInfo userInfo = um.getUserInfo(removingUserId);
Dialog dlg = new AlertDialog.Builder(context)
.setTitle(UserHandle.myUserId() == removingUserId
? R.string.user_confirm_remove_self_title
: (userInfo.isRestricted()
? R.string.user_profile_confirm_remove_title
: R.string.user_confirm_remove_title))
.setMessage(UserHandle.myUserId() == removingUserId
? R.string.user_confirm_remove_self_message
: (userInfo.isRestricted()
? R.string.user_profile_confirm_remove_message
: R.string.user_confirm_remove_message))
.setPositiveButton(R.string.user_delete_button,
onConfirmListener)
.setNegativeButton(android.R.string.cancel, null)
.create();
return dlg;
}
}

View File

@@ -101,7 +101,7 @@ public class RestrictedProfileSettings extends AppRestrictionsFragment {
mHeaderView.setOnClickListener(this);
mUserIconView = (ImageView) mHeaderView.findViewById(android.R.id.icon);
mUserNameView = (TextView) mHeaderView.findViewById(android.R.id.title);
getListView().setFastScrollEnabled(true);
//getListView().setFastScrollEnabled(true);
}
// This is going to bind the preferences.
super.onActivityCreated(savedInstanceState);

View File

@@ -0,0 +1,152 @@
/*
* Copyright (C) 2014 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.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.Preference;
import android.preference.SwitchPreference;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
/**
* 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, 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_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_SMS = 3;
private UserManager mUserManager;
private SwitchPreference mPhonePref;
private Preference mRemoveUserPref;
private UserInfo mUserInfo;
private boolean mGuestUser;
private Bundle mDefaultGuestRestrictions;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
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);
mGuestUser = getArguments().getBoolean(EXTRA_USER_GUEST, false);
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_TELEPHONY,
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_TELEPHONY));
}
mPhonePref.setOnPreferenceChangeListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference) {
if (preference == mRemoveUserPref) {
if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
throw new RuntimeException("Only the owner can remove a user");
}
showDialog(DIALOG_CONFIRM_REMOVE);
return true;
}
return false;
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (mGuestUser) {
// TODO: Show confirmation dialog: b/15761405
mDefaultGuestRestrictions.putBoolean(UserManager.DISALLOW_TELEPHONY,
!((Boolean) newValue));
mUserManager.setDefaultGuestRestrictions(mDefaultGuestRestrictions);
} else {
// TODO: Show confirmation dialog: b/15761405
mUserManager.setUserRestriction(UserManager.DISALLOW_TELEPHONY, !((Boolean) newValue),
new UserHandle(mUserInfo.id));
}
return true;
}
@Override
public Dialog onCreateDialog(int dialogId) {
Context context = getActivity();
if (context == null) return null;
switch (dialogId) {
case DIALOG_CONFIRM_REMOVE: {
Dialog dlg = RemoveUserUtil.createConfirmationDialog(getActivity(), mUserInfo.id,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
removeUser();
}
});
return dlg;
}
case DIALOG_CONFIRM_ENABLE_CALLING:
case DIALOG_CONFIRM_ENABLE_CALLING_SMS:
// TODO: b/15761405
}
return null;
}
void removeUser() {
mUserManager.removeUser(mUserInfo.id);
finishFragment();
}
}

View File

@@ -30,6 +30,7 @@ import android.view.View.OnClickListener;
public class UserPreference extends Preference {
public static final int USERID_UNKNOWN = -10;
public static final int USERID_GUEST_DEFAULTS = -11;
private OnClickListener mDeleteClickListener;
private OnClickListener mSettingsClickListener;
@@ -92,7 +93,11 @@ public class UserPreference extends Preference {
if (mUserId == UserHandle.myUserId()) return Integer.MIN_VALUE;
if (mSerialNumber < 0) {
// If the userId is unknown
if (mUserId == USERID_UNKNOWN) return Integer.MAX_VALUE;
if (mUserId == USERID_UNKNOWN) {
return Integer.MAX_VALUE;
} else if (mUserId == USERID_GUEST_DEFAULTS) {
return Integer.MAX_VALUE - 1;
}
mSerialNumber = ((UserManager) getContext().getSystemService(Context.USER_SERVICE))
.getUserSerialNumber(mUserId);
if (mSerialNumber < 0) return mUserId;

View File

@@ -135,6 +135,7 @@ public class UserSettings extends SettingsPreferenceFragment
private UserManager mUserManager;
private SparseArray<Bitmap> mUserIcons = new SparseArray<Bitmap>();
private boolean mIsOwner = UserHandle.myUserId() == UserHandle.USER_OWNER;
private boolean mIsGuest;
private Handler mHandler = new Handler() {
@Override
@@ -189,9 +190,12 @@ public class UserSettings extends SettingsPreferenceFragment
return;
}
final int myUserId = UserHandle.myUserId();
mIsGuest = mUserManager.getUserInfo(myUserId).isGuest();
addPreferencesFromResource(R.xml.user_settings);
mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
mMePreference = new UserPreference(context, null, UserHandle.myUserId(),
mMePreference = new UserPreference(context, null, myUserId,
mUserManager.isLinkedUser() ? null : this, null);
mMePreference.setKey(KEY_USER_ME);
mMePreference.setOnPreferenceClickListener(this);
@@ -207,7 +211,10 @@ public class UserSettings extends SettingsPreferenceFragment
mAddUser.setOnPreferenceClickListener(this);
DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm.getDeviceOwner() != null) {
// No restricted profiles for tablets with a device owner, or
// phones.
if (dpm.getDeviceOwner() != null
|| Utils.isVoiceCapable(context)) {
mCanAddRestrictedProfile = false;
mAddUser.setTitle(R.string.user_add_user_menu);
}
@@ -216,7 +223,7 @@ public class UserSettings extends SettingsPreferenceFragment
setHasOptionsMenu(true);
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
getActivity().registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null,
context.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null,
mHandler);
}
@@ -390,6 +397,14 @@ public class UserSettings extends SettingsPreferenceFragment
}
private void onManageUserClicked(int userId, boolean newUser) {
if (userId == UserPreference.USERID_GUEST_DEFAULTS) {
Bundle extras = new Bundle();
extras.putBoolean(UserDetailsSettings.EXTRA_USER_GUEST, true);
((SettingsActivity) getActivity()).startPreferencePanel(
UserDetailsSettings.class.getName(),
extras, R.string.user_guest, null, null, 0);
return;
}
UserInfo info = mUserManager.getUserInfo(userId);
if (info.isRestricted() && mIsOwner) {
Bundle extras = new Bundle();
@@ -411,6 +426,12 @@ public class UserSettings extends SettingsPreferenceFragment
((SettingsActivity) getActivity()).startPreferencePanel(
OwnerInfoSettings.class.getName(),
extras, titleResId, null, null, 0);
} else if (mIsOwner) {
Bundle extras = new Bundle();
extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId);
((SettingsActivity) getActivity()).startPreferencePanel(
UserDetailsSettings.class.getName(),
extras, -1, info.name, null, 0);
}
}
@@ -436,25 +457,14 @@ public class UserSettings extends SettingsPreferenceFragment
if (context == null) return null;
switch (dialogId) {
case DIALOG_CONFIRM_REMOVE: {
Dialog dlg = new AlertDialog.Builder(getActivity())
.setTitle(UserHandle.myUserId() == mRemovingUserId
? R.string.user_confirm_remove_self_title
: (mUserManager.getUserInfo(mRemovingUserId).isRestricted()
? R.string.user_profile_confirm_remove_title
: R.string.user_confirm_remove_title))
.setMessage(UserHandle.myUserId() == mRemovingUserId
? R.string.user_confirm_remove_self_message
: (mUserManager.getUserInfo(mRemovingUserId).isRestricted()
? R.string.user_profile_confirm_remove_message
: R.string.user_confirm_remove_message))
.setPositiveButton(R.string.user_delete_button,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
removeUserNow();
}
})
.setNegativeButton(android.R.string.cancel, null)
.create();
Dialog dlg =
RemoveUserUtil.createConfirmationDialog(getActivity(), mRemovingUserId,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
removeUserNow();
}
}
);
return dlg;
}
case DIALOG_USER_CANNOT_MANAGE:
@@ -625,20 +635,38 @@ public class UserSettings extends SettingsPreferenceFragment
private void updateUserList() {
if (getActivity() == null) return;
List<UserInfo> users = mUserManager.getUsers(true);
final Context context = getActivity();
mUserListCategory.removeAll();
mUserListCategory.setOrderingAsAdded(false);
mUserListCategory.addPreference(mMePreference);
final boolean voiceCapable = Utils.isVoiceCapable(context);
final ArrayList<Integer> missingIcons = new ArrayList<Integer>();
for (UserInfo user : users) {
Preference pref;
if (user.id == UserHandle.myUserId()) {
pref = mMePreference;
} else if (user.isGuest()) {
// Skip over Guest. We add generic Guest settings after this loop
continue;
} else {
pref = new UserPreference(getActivity(), null, user.id,
mIsOwner && user.isRestricted() ? this : null,
mIsOwner ? this : null);
// 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 = mIsOwner && (voiceCapable || user.isRestricted());
final boolean showDelete = mIsOwner
&& (!voiceCapable && !user.isRestricted() && !user.isGuest());
pref = new UserPreference(context, null, user.id,
showSettings ? this : null,
showDelete ? this : null);
//mIsOwner && user.isRestricted() ? this : null,
//mIsOwner ? this : null);
pref.setOnPreferenceClickListener(this);
pref.setKey("id=" + user.id);
mUserListCategory.addPreference(pref);
@@ -682,6 +710,17 @@ public class UserSettings extends SettingsPreferenceFragment
pref.setIcon(encircle(R.drawable.avatar_default_1));
mUserListCategory.addPreference(pref);
}
if (!mIsGuest) {
// Add a virtual Guest user for guest defaults
Preference pref = new UserPreference(getActivity(), null,
UserPreference.USERID_GUEST_DEFAULTS, mIsOwner ? this : null, null);
pref.setTitle(R.string.user_guest);
pref.setIcon(encircle(R.drawable.ic_settings_accounts));
pref.setOnPreferenceClickListener(this);
mUserListCategory.addPreference(pref);
}
getActivity().invalidateOptionsMenu();
// Load the icons
@@ -767,16 +806,16 @@ public class UserSettings extends SettingsPreferenceFragment
}
} else if (pref instanceof UserPreference) {
int userId = ((UserPreference) pref).getUserId();
// Get the latest status of the user
UserInfo user = mUserManager.getUserInfo(userId);
if (UserHandle.myUserId() != UserHandle.USER_OWNER) {
showDialog(DIALOG_USER_CANNOT_MANAGE);
if (userId == UserPreference.USERID_GUEST_DEFAULTS) {
createAndSwitchToGuestUser();
} else {
// Get the latest status of the user
UserInfo user = mUserManager.getUserInfo(userId);
if (!isInitialized(user)) {
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_SETUP_USER, user.id, user.serialNumber));
} else if (user.isRestricted()) {
onManageUserClicked(user.id, false);
} else if (!user.isManagedProfile()) {
switchUserNow(userId);
}
}
} else if (pref == mAddUser) {
@@ -791,6 +830,22 @@ public class UserSettings extends SettingsPreferenceFragment
return false;
}
private void createAndSwitchToGuestUser() {
List<UserInfo> users = mUserManager.getUsers();
for (UserInfo user : users) {
if (user.isGuest()) {
switchUserNow(user.id);
return;
}
}
// No guest user. Create one.
UserInfo guestUser = mUserManager.createGuest(getActivity(),
getResources().getString(R.string.user_guest));
if (guestUser != null) {
switchUserNow(guestUser.id);
}
}
private boolean isInitialized(UserInfo user) {
return (user.flags & UserInfo.FLAG_INITIALIZED) != 0;
}