diff --git a/res/values/strings.xml b/res/values/strings.xml index ea6ece6657a..cc9a8dfbcc5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4942,6 +4942,15 @@ Delete user Delete + + Guest + + + Enable phone calls? + + Enable phone calls and SMS? + + Remove user Allow apps and content diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml new file mode 100644 index 00000000000..4353657f965 --- /dev/null +++ b/res/xml/user_details_settings.xml @@ -0,0 +1,28 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java index f1022b9a2eb..d1a5de32ce5 100644 --- a/src/com/android/settings/users/AppRestrictionsFragment.java +++ b/src/com/android/settings/users/AppRestrictionsFragment.java @@ -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)) { diff --git a/src/com/android/settings/users/RemoveUserUtil.java b/src/com/android/settings/users/RemoveUserUtil.java new file mode 100644 index 00000000000..ac74bfae3cc --- /dev/null +++ b/src/com/android/settings/users/RemoveUserUtil.java @@ -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; + } +} diff --git a/src/com/android/settings/users/RestrictedProfileSettings.java b/src/com/android/settings/users/RestrictedProfileSettings.java index 535e19672e0..41acda43bdf 100644 --- a/src/com/android/settings/users/RestrictedProfileSettings.java +++ b/src/com/android/settings/users/RestrictedProfileSettings.java @@ -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); diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java new file mode 100644 index 00000000000..56b4598c21c --- /dev/null +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -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(); + } +} diff --git a/src/com/android/settings/users/UserPreference.java b/src/com/android/settings/users/UserPreference.java index 56ca1143933..72aa9a4c583 100644 --- a/src/com/android/settings/users/UserPreference.java +++ b/src/com/android/settings/users/UserPreference.java @@ -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; diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java index 8d27c65e425..42e1a5b7c9a 100644 --- a/src/com/android/settings/users/UserSettings.java +++ b/src/com/android/settings/users/UserSettings.java @@ -135,6 +135,7 @@ public class UserSettings extends SettingsPreferenceFragment private UserManager mUserManager; private SparseArray mUserIcons = new SparseArray(); 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 users = mUserManager.getUsers(true); + final Context context = getActivity(); mUserListCategory.removeAll(); mUserListCategory.setOrderingAsAdded(false); mUserListCategory.addPreference(mMePreference); + final boolean voiceCapable = Utils.isVoiceCapable(context); final ArrayList missingIcons = new ArrayList(); 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 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; }