Improve multi user functionality for restricted users
- Restricted users can change their name
- Improved App&Content access screen
- Remove "Turn on phone calls" from restricted user detail page
Doc: http://shortn/_Prb3SJ3xJ3
Bug: 142798722
Test: Run robo tests with this command:
make -j64 RunSettingsRoboTests
Change-Id: I2aadf32aef52ba5ad0db7aa0cd83bac9d9941589
(cherry picked from commit f4759e00d53671b273dee3d6c9e62203df6f629b)
diff --git a/res/layout/edit_user_info_dialog_content.xml b/res/layout/edit_user_info_dialog_content.xml
index 895cef6..2bd464b 100644
--- a/res/layout/edit_user_info_dialog_content.xml
+++ b/res/layout/edit_user_info_dialog_content.xml
@@ -25,7 +25,6 @@
android:layout_width="56dip"
android:layout_height="56dip"
android:layout_gravity="bottom"
- android:layout_marginEnd="6dp"
android:contentDescription="@string/user_image_photo_selector"
android:background="@*android:drawable/spinner_background_holo_dark"
android:scaleType="fitCenter"/>
diff --git a/res/layout/user_info_header.xml b/res/layout/user_info_header.xml
deleted file mode 100644
index bfdf3fc..0000000
--- a/res/layout/user_info_header.xml
+++ /dev/null
@@ -1,129 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:minHeight="?android:attr/listPreferredItemHeight"
- android:gravity="center_vertical"
- android:orientation="vertical"
- android:focusable="true"
- android:clickable="true" >
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:id="@+id/app_restrictions_pref"
- android:gravity="center_vertical"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:background="?android:attr/selectableItemBackground" >
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center"
- android:minWidth="@*android:dimen/preference_icon_minWidth"
- android:orientation="horizontal">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center"
- android:minWidth="48dp"
- android:scaleType="centerInside"
- android:layout_marginEnd="@*android:dimen/preference_item_padding_inner"/>
- </LinearLayout>
- <RelativeLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginEnd="6dip"
- android:layout_marginTop="6dip"
- android:layout_marginBottom="6dip"
- android:layout_weight="1">
- <TextView
- android:id="@android:id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- tools:text="Richard"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"/>
- <TextView
- android:id="@android:id/summary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@android:id/title"
- android:layout_alignStart="@android:id/title"
- android:paddingBottom="3dip"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textSize="13sp"
- android:textColor="?android:attr/textColorSecondary"
- android:focusable="false"
- android:text="@string/user_summary_restricted_profile"
- android:maxLines="4" />
- </RelativeLayout>
- <ImageView
- android:id="@+id/delete"
- android:layout_width="wrap_content"
- android:layout_height="fill_parent"
- android:paddingStart="16dip"
- android:paddingEnd="16dip"
- android:src="@drawable/ic_delete"
- android:contentDescription="@string/user_delete_user_description"
- android:layout_gravity="center"
- android:background="?android:attr/selectableItemBackground" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/switch_pref"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:visibility="gone"
- tools:visibility="visible"
- android:paddingStart="?android:attr/listPreferredItemPaddingStart"
- android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
- android:background="?android:attr/selectableItemBackground" >
-
- <ImageView
- android:id="@+id/switchIcon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_gravity="center"
- android:src="@drawable/ic_swap"
- android:minWidth="48dp"
- android:scaleType="centerInside"
- android:layout_marginEnd="@*android:dimen/preference_item_padding_inner"/>
-
- <TextView
- android:id="@+id/switchTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:singleLine="true"
- tools:text="Switch to Richard"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:ellipsize="marquee"
- android:fadingEdge="horizontal"/>
-
- </LinearLayout>
-
- <View android:layout_width="match_parent"
- android:layout_height="2dp"
- android:background="@color/divider_color" />
-</LinearLayout>
diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml
index d336395..9280ff1 100644
--- a/res/xml/user_details_settings.xml
+++ b/res/xml/user_details_settings.xml
@@ -26,6 +26,10 @@
android:icon="@drawable/ic_phone"
android:title="@string/user_enable_calling_sms" />
<com.android.settingslib.RestrictedPreference
+ android:key="app_and_content_access"
+ android:icon="@drawable/ic_lock_closed"
+ android:title="@string/user_restrictions_title" />
+ <com.android.settingslib.RestrictedPreference
android:key="remove_user"
android:icon="@drawable/ic_delete"
settings:userRestriction="no_remove_user"
diff --git a/src/com/android/settings/SettingsLicenseActivity.java b/src/com/android/settings/SettingsLicenseActivity.java
index 6def6fa..57b1f5c 100644
--- a/src/com/android/settings/SettingsLicenseActivity.java
+++ b/src/com/android/settings/SettingsLicenseActivity.java
@@ -30,7 +30,6 @@
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
-import com.android.settings.users.RestrictedProfileSettings;
import com.android.settingslib.license.LicenseHtmlLoaderCompat;
import java.io.File;
@@ -78,7 +77,7 @@
@VisibleForTesting
Uri getUriFromGeneratedHtmlFile(File generatedHtmlFile) {
- return FileProvider.getUriForFile(this, RestrictedProfileSettings.FILE_PROVIDER_AUTHORITY,
+ return FileProvider.getUriForFile(this, Utils.FILE_PROVIDER_AUTHORITY,
generatedHtmlFile);
}
diff --git a/src/com/android/settings/Utils.java b/src/com/android/settings/Utils.java
index d34dbcf..e5fd340 100644
--- a/src/com/android/settings/Utils.java
+++ b/src/com/android/settings/Utils.java
@@ -116,6 +116,8 @@
private static final String TAG = "Settings";
+ public static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files";
+
/**
* Set the preference's title to the matching activity's label.
*/
diff --git a/src/com/android/settings/users/AppRestrictionsFragment.java b/src/com/android/settings/users/AppRestrictionsFragment.java
index 1caf49c..9f6a2b6 100644
--- a/src/com/android/settings/users/AppRestrictionsFragment.java
+++ b/src/com/android/settings/users/AppRestrictionsFragment.java
@@ -97,15 +97,8 @@
/** Key for extra passed in from calling fragment to indicate if this is a newly created user */
public static final String EXTRA_NEW_USER = "new_user";
- /**
- * Key for extra passed in from calling fragment to indicate if
- * switch to user should be shown
- */
- public static final String EXTRA_SHOW_SWITCH_USER = "enable_switch";
-
private boolean mFirstTime = true;
private boolean mNewUser;
- protected boolean mShowSwitchUser;
private boolean mAppListChanged;
protected boolean mRestrictedProfile;
@@ -216,6 +209,12 @@
}
}
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ init(icicle);
+ }
+
protected void init(Bundle icicle) {
if (icicle != null) {
mUser = new UserHandle(icicle.getInt(EXTRA_USER_ID));
@@ -226,7 +225,6 @@
mUser = new UserHandle(args.getInt(EXTRA_USER_ID));
}
mNewUser = args.getBoolean(EXTRA_NEW_USER, false);
- mShowSwitchUser = args.getBoolean(EXTRA_SHOW_SWITCH_USER, false);
}
}
diff --git a/src/com/android/settings/users/EditUserInfoController.java b/src/com/android/settings/users/EditUserInfoController.java
index 373d6a9..d205968 100644
--- a/src/com/android/settings/users/EditUserInfoController.java
+++ b/src/com/android/settings/users/EditUserInfoController.java
@@ -18,8 +18,10 @@
import android.app.Activity;
import android.app.Dialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -77,7 +79,9 @@
}
public void clear() {
- mEditUserPhotoController.removeNewUserPhotoBitmapFile();
+ if (mEditUserPhotoController != null) {
+ mEditUserPhotoController.removeNewUserPhotoBitmapFile();
+ }
mEditUserInfoDialog = null;
mSavedPhoto = null;
}
@@ -116,7 +120,7 @@
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mWaitingForActivityResult = false;
- if (mEditUserInfoDialog != null) {
+ if (mEditUserPhotoController != null && mEditUserInfoDialog != null) {
mEditUserPhotoController.onActivityResult(requestCode, resultCode, data);
}
}
@@ -137,6 +141,14 @@
userNameView.setText(currentUserName);
final ImageView userPhotoView = (ImageView) content.findViewById(R.id.user_photo);
+
+ boolean canChangePhoto = mUserManager != null &&
+ canChangePhoto(activity, mUserManager.getUserInfo(user.getIdentifier()));
+ if (!canChangePhoto) {
+ // some users can't change their photos so we need to remove suggestive
+ // background from the photoView
+ userPhotoView.setBackground(null);
+ }
Drawable drawable = null;
if (mSavedPhoto != null) {
drawable = CircleFramedDrawable.getInstance(activity, mSavedPhoto);
@@ -144,7 +156,10 @@
drawable = currentUserIcon;
}
userPhotoView.setImageDrawable(drawable);
- mEditUserPhotoController = createEditUserPhotoController(fragment, userPhotoView, drawable);
+ if (canChangePhoto) {
+ mEditUserPhotoController =
+ createEditUserPhotoController(fragment, userPhotoView, drawable);
+ }
mEditUserInfoDialog = new AlertDialog.Builder(activity)
.setTitle(title)
.setView(content)
@@ -158,21 +173,22 @@
if (!TextUtils.isEmpty(userName)) {
if (currentUserName == null
|| !userName.toString().equals(
- currentUserName.toString())) {
+ currentUserName.toString())) {
if (callback != null) {
callback.onLabelChanged(mUser, userName.toString());
}
}
}
// Update the photo if changed.
- Drawable drawable = mEditUserPhotoController.getNewUserPhotoDrawable();
- if (drawable != null && !drawable.equals(currentUserIcon)) {
- if (callback != null) {
+ if (mEditUserPhotoController != null) {
+ Drawable drawable =
+ mEditUserPhotoController.getNewUserPhotoDrawable();
+ if (drawable != null && !drawable.equals(currentUserIcon)) {
+ if (callback != null) {
callback.onPhotoChanged(mUser, drawable);
+ }
}
}
- fragment.getActivity().removeDialog(
- RestrictedProfileSettings.DIALOG_ID_EDIT_USER_INFO);
}
clear();
if (completeCallback != null) {
@@ -208,6 +224,13 @@
}
@VisibleForTesting
+ boolean canChangePhoto(Context context, UserInfo user) {
+ return PhotoCapabilityUtils.canCropPhoto(context) &&
+ (PhotoCapabilityUtils.canChoosePhoto(context)
+ || PhotoCapabilityUtils.canTakePhoto(context));
+ }
+
+ @VisibleForTesting
EditUserPhotoController createEditUserPhotoController(Fragment fragment,
ImageView userPhotoView, Drawable drawable) {
return new EditUserPhotoController(fragment, userPhotoView,
diff --git a/src/com/android/settings/users/EditUserPhotoController.java b/src/com/android/settings/users/EditUserPhotoController.java
index 3253f79..a20513a 100644
--- a/src/com/android/settings/users/EditUserPhotoController.java
+++ b/src/com/android/settings/users/EditUserPhotoController.java
@@ -21,7 +21,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
@@ -52,6 +51,7 @@
import androidx.fragment.app.Fragment;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.drawable.CircleFramedDrawable;
@@ -141,14 +141,14 @@
}
private void showUpdatePhotoPopup() {
- final boolean canTakePhoto = canTakePhoto();
- final boolean canChoosePhoto = canChoosePhoto();
+ final Context context = mImageView.getContext();
+ final boolean canTakePhoto = PhotoCapabilityUtils.canTakePhoto(context);
+ final boolean canChoosePhoto = PhotoCapabilityUtils.canChoosePhoto(context);
if (!canTakePhoto && !canChoosePhoto) {
return;
}
- final Context context = mImageView.getContext();
final List<EditUserPhotoController.RestrictedMenuItem> items = new ArrayList<>();
if (canTakePhoto) {
@@ -200,19 +200,6 @@
listPopupWindow.show();
}
- private boolean canTakePhoto() {
- return mImageView.getContext().getPackageManager().queryIntentActivities(
- new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
- PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
- }
-
- private boolean canChoosePhoto() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.setType("image/*");
- return mImageView.getContext().getPackageManager().queryIntentActivities(
- intent, 0).size() > 0;
- }
-
private void takePhoto() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
appendOutputExtra(intent, mTakePictureUri);
@@ -369,8 +356,7 @@
if (purge) {
fullPath.delete();
}
- return FileProvider.getUriForFile(context,
- RestrictedProfileSettings.FILE_PROVIDER_AUTHORITY, fullPath);
+ return FileProvider.getUriForFile(context, Utils.FILE_PROVIDER_AUTHORITY, fullPath);
}
File saveNewUserPhotoBitmap() {
diff --git a/src/com/android/settings/users/PhotoCapabilityUtils.java b/src/com/android/settings/users/PhotoCapabilityUtils.java
new file mode 100644
index 0000000..1e09857
--- /dev/null
+++ b/src/com/android/settings/users/PhotoCapabilityUtils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.users;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.provider.MediaStore;
+
+class PhotoCapabilityUtils {
+
+ /**
+ * Check if the current user can perform any activity for
+ * android.media.action.IMAGE_CAPTURE action.
+ */
+ static boolean canTakePhoto(Context context) {
+ return context.getPackageManager().queryIntentActivities(
+ new Intent(MediaStore.ACTION_IMAGE_CAPTURE),
+ PackageManager.MATCH_DEFAULT_ONLY).size() > 0;
+ }
+
+ /**
+ * Check if the current user can perform any activity for
+ * android.intent.action.GET_CONTENT action for images.
+ */
+ static boolean canChoosePhoto(Context context) {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("image/*");
+ return context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
+ }
+
+ /**
+ * Check if the current user can perform any activity for
+ * com.android.camera.action.CROP action for images.
+ */
+ static boolean canCropPhoto(Context context) {
+ Intent intent = new Intent("com.android.camera.action.CROP");
+ intent.setType("image/*");
+ return context.getPackageManager().queryIntentActivities(intent, 0).size() > 0;
+ }
+
+}
diff --git a/src/com/android/settings/users/RestrictedProfileSettings.java b/src/com/android/settings/users/RestrictedProfileSettings.java
deleted file mode 100644
index 44657cf..0000000
--- a/src/com/android/settings/users/RestrictedProfileSettings.java
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2013 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.ActivityManager;
-import android.app.Dialog;
-import android.app.settings.SettingsEnums;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.util.UserIcons;
-import com.android.settings.R;
-import com.android.settings.Utils;
-import com.android.settingslib.utils.ThreadUtils;
-
-public class RestrictedProfileSettings extends AppRestrictionsFragment
- implements EditUserInfoController.OnContentChangedCallback {
-
- private static final String TAG = RestrictedProfileSettings.class.getSimpleName();
- public static final String FILE_PROVIDER_AUTHORITY = "com.android.settings.files";
- static final int DIALOG_ID_EDIT_USER_INFO = 1;
- private static final int DIALOG_CONFIRM_REMOVE = 2;
-
- private View mHeaderView;
- private ImageView mUserIconView;
- private TextView mUserNameView;
- private ImageView mDeleteButton;
- private View mSwitchUserView;
- private TextView mSwitchTitle;
-
- private EditUserInfoController mEditUserInfoController =
- new EditUserInfoController();
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- if (icicle != null) {
- mEditUserInfoController.onRestoreInstanceState(icicle);
- }
-
- init(icicle);
- }
-
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- mHeaderView = setPinnedHeaderView(R.layout.user_info_header);
- mHeaderView.setOnClickListener(this);
- mUserIconView = (ImageView) mHeaderView.findViewById(android.R.id.icon);
- mUserNameView = (TextView) mHeaderView.findViewById(android.R.id.title);
- mDeleteButton = (ImageView) mHeaderView.findViewById(R.id.delete);
- mDeleteButton.setOnClickListener(this);
-
- mSwitchTitle = mHeaderView.findViewById(R.id.switchTitle);
- mSwitchUserView = mHeaderView.findViewById(R.id.switch_pref);
- mSwitchUserView.setOnClickListener(v -> switchUser());
-
- // This is going to bind the preferences.
- super.onActivityCreated(savedInstanceState);
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mEditUserInfoController.onSaveInstanceState(outState);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- // Check if user still exists
- UserInfo info = Utils.getExistingUser(mUserManager, mUser);
- if (info == null) {
- finishFragment();
- } else {
- ((TextView) mHeaderView.findViewById(android.R.id.title)).setText(info.name);
- ((ImageView) mHeaderView.findViewById(android.R.id.icon)).setImageDrawable(
- com.android.settingslib.Utils.getUserIcon(getActivity(), mUserManager, info));
-
- boolean canSwitchUser =
- mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK;
- if (mShowSwitchUser && canSwitchUser) {
- mSwitchUserView.setVisibility(View.VISIBLE);
- mSwitchTitle.setText(getString(com.android.settingslib.R.string.user_switch_to_user,
- info.name));
- } else {
- mSwitchUserView.setVisibility(View.GONE);
- }
- }
- }
-
- @Override
- public void startActivityForResult(Intent intent, int requestCode) {
- mEditUserInfoController.startingActivityForResult();
- super.startActivityForResult(intent, requestCode);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public void onClick(View view) {
- if (view == mHeaderView) {
- showDialog(DIALOG_ID_EDIT_USER_INFO);
- } else if (view == mDeleteButton) {
- showDialog(DIALOG_CONFIRM_REMOVE);
- } else {
- super.onClick(view); // in AppRestrictionsFragment
- }
- }
-
- @Override
- public Dialog onCreateDialog(int dialogId) {
- if (dialogId == DIALOG_ID_EDIT_USER_INFO) {
- return mEditUserInfoController.createDialog(this, mUserIconView.getDrawable(),
- mUserNameView.getText(), getString(R.string.profile_info_settings_title),
- this, mUser, null);
- } else if (dialogId == DIALOG_CONFIRM_REMOVE) {
- Dialog dlg =
- UserDialogs.createRemoveDialog(getActivity(), mUser.getIdentifier(),
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- removeUser();
- }
- }
- );
- return dlg;
- }
-
- return null;
- }
-
- @Override
- public int getDialogMetricsCategory(int dialogId) {
- switch (dialogId) {
- case DIALOG_ID_EDIT_USER_INFO:
- return SettingsEnums.DIALOG_USER_EDIT;
- case DIALOG_CONFIRM_REMOVE:
- return SettingsEnums.DIALOG_USER_REMOVE;
- default:
- return 0;
- }
- }
-
- private void removeUser() {
- getView().post(new Runnable() {
- public void run() {
- mUserManager.removeUser(mUser.getIdentifier());
- finishFragment();
- }
- });
- }
-
- private void switchUser() {
- try {
- ActivityManager.getService().switchUser(mUser.getIdentifier());
- } catch (RemoteException re) {
- Log.e(TAG, "Error while switching to other user.");
- } finally {
- finishFragment();
- }
- }
-
- @Override
- public void onPhotoChanged(UserHandle user, Drawable photo) {
- mUserIconView.setImageDrawable(photo);
- ThreadUtils.postOnBackgroundThread(new Runnable() {
- @Override
- public void run() {
- mUserManager.setUserIcon(user.getIdentifier(), UserIcons.convertToBitmap(photo));
- }
- });
- }
-
- @Override
- public void onLabelChanged(UserHandle user, CharSequence label) {
- mUserNameView.setText(label);
- mUserManager.setUserName(user.getIdentifier(), label.toString());
- }
-}
diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java
index 2696ddc..c7cf90d 100644
--- a/src/com/android/settings/users/UserDetailsSettings.java
+++ b/src/com/android/settings/users/UserDetailsSettings.java
@@ -36,6 +36,7 @@
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.Utils;
+import com.android.settings.core.SubSettingLauncher;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
@@ -56,6 +57,7 @@
private static final String KEY_SWITCH_USER = "switch_user";
private static final String KEY_ENABLE_TELEPHONY = "enable_calling";
private static final String KEY_REMOVE_USER = "remove_user";
+ private static final String KEY_APP_AND_CONTENT_ACCESS = "app_and_content_access";
/** Integer extra containing the userId to manage */
static final String EXTRA_USER_ID = "user_id";
@@ -69,6 +71,8 @@
Preference mSwitchUserPref;
private SwitchPreference mPhonePref;
@VisibleForTesting
+ Preference mAppAndContentAccessPref;
+ @VisibleForTesting
Preference mRemoveUserPref;
@VisibleForTesting
@@ -109,6 +113,8 @@
switchUser();
}
return true;
+ } else if (preference == mAppAndContentAccessPref) {
+ openAppAndContentAccessScreen(false);
}
return false;
}
@@ -170,11 +176,14 @@
if (userId == USER_NULL) {
throw new IllegalStateException("Arguments to this fragment must contain the user id");
}
+ boolean isNewUser =
+ arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false);
mUserInfo = mUserManager.getUserInfo(userId);
mSwitchUserPref = findPreference(KEY_SWITCH_USER);
mPhonePref = findPreference(KEY_ENABLE_TELEPHONY);
mRemoveUserPref = findPreference(KEY_REMOVE_USER);
+ mAppAndContentAccessPref = findPreference(KEY_APP_AND_CONTENT_ACCESS);
mSwitchUserPref.setTitle(
context.getString(com.android.settingslib.R.string.user_switch_to_user,
@@ -184,16 +193,24 @@
if (!mUserManager.isAdminUser()) { // non admin users can't remove users and allow calls
removePreference(KEY_ENABLE_TELEPHONY);
removePreference(KEY_REMOVE_USER);
+ removePreference(KEY_APP_AND_CONTENT_ACCESS);
} else {
if (!Utils.isVoiceCapable(context)) { // no telephony
removePreference(KEY_ENABLE_TELEPHONY);
}
- if (!mUserInfo.isGuest()) {
- mPhonePref.setChecked(!mUserManager.hasUserRestriction(
- UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId)));
- mRemoveUserPref.setTitle(R.string.user_remove_user);
+ if (mUserInfo.isRestricted()) {
+ removePreference(KEY_ENABLE_TELEPHONY);
+ if (isNewUser) {
+ // for newly created restricted users we should open the apps and content access
+ // screen to initialize the default restrictions
+ openAppAndContentAccessScreen(true);
+ }
} else {
+ removePreference(KEY_APP_AND_CONTENT_ACCESS);
+ }
+
+ if (mUserInfo.isGuest()) {
// These are not for an existing user, just general Guest settings.
// Default title is for calling and SMS. Change to calling-only here
mPhonePref.setTitle(R.string.user_enable_calling);
@@ -201,6 +218,10 @@
mPhonePref.setChecked(
!mDefaultGuestRestrictions.getBoolean(UserManager.DISALLOW_OUTGOING_CALLS));
mRemoveUserPref.setTitle(R.string.user_exit_guest_title);
+ } else {
+ mPhonePref.setChecked(!mUserManager.hasUserRestriction(
+ UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId)));
+ mRemoveUserPref.setTitle(R.string.user_remove_user);
}
if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context,
UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) {
@@ -209,6 +230,7 @@
mRemoveUserPref.setOnPreferenceClickListener(this);
mPhonePref.setOnPreferenceChangeListener(this);
+ mAppAndContentAccessPref.setOnPreferenceClickListener(this);
}
}
@@ -283,4 +305,20 @@
mUserManager.removeUser(mUserInfo.id);
finishFragment();
}
+
+ /**
+ * @param isNewUser indicates if a user was created recently, for new users
+ * AppRestrictionsFragment should set the default restrictions
+ */
+ private void openAppAndContentAccessScreen(boolean isNewUser) {
+ Bundle extras = new Bundle();
+ extras.putInt(AppRestrictionsFragment.EXTRA_USER_ID, mUserInfo.id);
+ extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, isNewUser);
+ new SubSettingLauncher(getContext())
+ .setDestination(AppRestrictionsFragment.class.getName())
+ .setArguments(extras)
+ .setTitleRes(R.string.user_restrictions_title)
+ .setSourceMetricsCategory(getMetricsCategory())
+ .launch();
+ }
}
diff --git a/src/com/android/settings/users/UserSettings.java b/src/com/android/settings/users/UserSettings.java
index 7d4ab5d..719ed4a 100644
--- a/src/com/android/settings/users/UserSettings.java
+++ b/src/com/android/settings/users/UserSettings.java
@@ -485,23 +485,13 @@
private void onManageUserClicked(int userId, boolean newUser) {
mAddingUser = false;
UserInfo userInfo = mUserManager.getUserInfo(userId);
- if (userInfo.isRestricted() && mUserCaps.mIsAdmin) {
- Bundle extras = new Bundle();
- extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId);
- extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser);
- extras.putBoolean(RestrictedProfileSettings.EXTRA_SHOW_SWITCH_USER, canSwitchUserNow());
- new SubSettingLauncher(getContext())
- .setDestination(RestrictedProfileSettings.class.getName())
- .setArguments(extras)
- .setTitleRes(R.string.user_restrictions_title)
- .setSourceMetricsCategory(getMetricsCategory())
- .launch();
- } else if (userId == UserHandle.myUserId()) {
+ if (userId == UserHandle.myUserId()) {
// Jump to owner info panel
OwnerInfoSettings.show(this);
} else {
Bundle extras = new Bundle();
extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId);
+ extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, newUser);
new SubSettingLauncher(getContext())
.setDestination(UserDetailsSettings.class.getName())
.setArguments(extras)
@@ -963,10 +953,10 @@
pref.setSummary(R.string.user_summary_restricted_not_set_up);
} else {
pref.setSummary(R.string.user_summary_not_set_up);
+ // Disallow setting up user which results in user switching when the
+ // restriction is set.
+ pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow());
}
- // Disallow setting up user which results in user switching when the restriction is
- // set.
- pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow());
} else if (user.isRestricted()) {
pref.setSummary(R.string.user_summary_restricted_profile);
}
@@ -1137,17 +1127,14 @@
showDialog(DIALOG_CONFIRM_EXIT_GUEST);
return true;
}
- // If this is a limited user, launch the user info settings instead of profile editor
- if (mUserManager.isRestrictedProfile()) {
- onManageUserClicked(UserHandle.myUserId(), false);
- } else {
- showDialog(DIALOG_USER_PROFILE_EDITOR);
- }
+ showDialog(DIALOG_USER_PROFILE_EDITOR);
} else if (pref instanceof UserPreference) {
int userId = ((UserPreference) pref).getUserId();
// Get the latest status of the user
UserInfo user = mUserManager.getUserInfo(userId);
- if (!user.isInitialized()) {
+ if (!user.isInitialized() && isSecondaryUser(user)) {
+ // for uninitialized secondary users we should show a prompt dialog before
+ // starting the setup
mHandler.sendMessage(mHandler.obtainMessage(
MESSAGE_SETUP_USER, user.id, user.serialNumber));
} else {
@@ -1279,4 +1266,7 @@
}
};
+ private boolean isSecondaryUser(UserInfo user) {
+ return UserManager.USER_TYPE_FULL_SECONDARY.equals(user.userType);
+ }
}
diff --git a/tests/robotests/src/com/android/settings/users/EditUserInfoControllerTest.java b/tests/robotests/src/com/android/settings/users/EditUserInfoControllerTest.java
index 1c191fa..db9872f 100644
--- a/tests/robotests/src/com/android/settings/users/EditUserInfoControllerTest.java
+++ b/tests/robotests/src/com/android/settings/users/EditUserInfoControllerTest.java
@@ -28,7 +28,9 @@
import static org.mockito.Mockito.when;
import android.app.Dialog;
+import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.widget.EditText;
import android.widget.ImageView;
@@ -62,6 +64,8 @@
@Mock
private Drawable mCurrentIcon;
+ private boolean mCanChangePhoto;
+
private FragmentActivity mActivity;
private TestEditUserInfoController mController;
@@ -78,6 +82,11 @@
mPhotoController = mock(EditUserPhotoController.class, Answers.RETURNS_DEEP_STUBS);
return mPhotoController;
}
+
+ @Override
+ boolean canChangePhoto(Context context, UserInfo user) {
+ return mCanChangePhoto;
+ }
}
@Before
@@ -86,6 +95,7 @@
mActivity = spy(ActivityController.of(new FragmentActivity()).get());
when(mFragment.getActivity()).thenReturn(mActivity);
mController = new TestEditUserInfoController();
+ mCanChangePhoto = true;
}
@Test
@@ -256,4 +266,17 @@
verify(dialogCompleteCallback, times(1)).onPositive();
verify(dialogCompleteCallback, times(0)).onNegativeOrCancel();
}
+
+ @Test
+ public void createDialog_canNotChangePhoto_nullPhotoController() {
+ mCanChangePhoto = false;
+
+ mController.createDialog(
+ mFragment, mCurrentIcon, "test",
+ "title", null,
+ android.os.Process.myUserHandle(),
+ null);
+
+ assertThat(mController.mPhotoController).isNull();
+ }
}
diff --git a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
index 6c54782..56e4957 100644
--- a/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
+++ b/tests/robotests/src/com/android/settings/users/UserDetailsSettingsTest.java
@@ -32,9 +32,11 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.robolectric.Shadows.shadowOf;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.UserHandle;
@@ -47,6 +49,8 @@
import androidx.preference.SwitchPreference;
import com.android.settings.R;
+import com.android.settings.SettingsActivity;
+import com.android.settings.SubSettings;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
import com.android.settings.testutils.shadow.ShadowUserManager;
@@ -61,6 +65,7 @@
import org.robolectric.android.controller.ActivityController;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowIntent;
import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
@@ -76,6 +81,7 @@
private static final String KEY_SWITCH_USER = "switch_user";
private static final String KEY_ENABLE_TELEPHONY = "enable_calling";
private static final String KEY_REMOVE_USER = "remove_user";
+ private static final String KEY_APP_AND_CONTENT_ACCESS = "app_and_content_access";
private static final int DIALOG_CONFIRM_REMOVE = 1;
@@ -90,6 +96,8 @@
private SwitchPreference mPhonePref;
@Mock
private Preference mRemoveUserPref;
+ @Mock
+ private Preference mAppAndContentAccessPref;
private FragmentActivity mActivity;
private Context mContext;
@@ -114,14 +122,15 @@
ReflectionHelpers.setField(mFragment, "mUserManager", userManager);
doReturn(mActivity).when(mFragment).getActivity();
- doReturn(mContext).when(mFragment).getContext();
+ doReturn(mActivity).when(mFragment).getContext();
doReturn(mock(PreferenceScreen.class)).when(mFragment).getPreferenceScreen();
- doReturn("").when(mActivity).getString(anyInt(), anyString());
doReturn(mSwitchUserPref).when(mFragment).findPreference(KEY_SWITCH_USER);
doReturn(mPhonePref).when(mFragment).findPreference(KEY_ENABLE_TELEPHONY);
doReturn(mRemoveUserPref).when(mFragment).findPreference(KEY_REMOVE_USER);
+ doReturn(mAppAndContentAccessPref)
+ .when(mFragment).findPreference(KEY_APP_AND_CONTENT_ACCESS);
}
@After
@@ -170,6 +179,24 @@
}
@Test
+ public void initialize_userSelected_shouldNotShowAppAndContentPref() {
+ setupSelectedUser();
+
+ mFragment.initialize(mActivity, mArguments);
+
+ verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS);
+ }
+
+ @Test
+ public void initialize_guestSelected_shouldNotShowAppAndContentPref() {
+ setupSelectedGuest();
+
+ mFragment.initialize(mActivity, mArguments);
+
+ verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS);
+ }
+
+ @Test
public void onResume_canSwitch_shouldEnableSwitchPref() {
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK);
mFragment.mSwitchUserPref = mSwitchUserPref;
@@ -249,6 +276,16 @@
}
@Test
+ public void initialize_nonAdmin_shouldNotShowAppAndContentPref() {
+ setupSelectedUser();
+ mUserManager.setIsAdminUser(false);
+
+ mFragment.initialize(mActivity, mArguments);
+
+ verify(mFragment).removePreference(KEY_APP_AND_CONTENT_ACCESS);
+ }
+
+ @Test
public void initialize_adminSelectsSecondaryUser_shouldShowRemovePreference() {
setupSelectedUser();
mUserManager.setIsAdminUser(true);
@@ -261,6 +298,57 @@
}
@Test
+ public void initialize_adminSelectsNewRestrictedUser_shouldOpenAppContentScreen() {
+ setupSelectedRestrictedUser();
+ mUserManager.setIsAdminUser(true);
+ mArguments.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, true);
+
+ mFragment.initialize(mActivity, mArguments);
+
+ Intent startedIntent = shadowOf(mActivity).getNextStartedActivity();
+ ShadowIntent shadowIntent = shadowOf(startedIntent);
+ assertThat(shadowIntent.getIntentClass()).isEqualTo(SubSettings.class);
+ assertThat(startedIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(AppRestrictionsFragment.class.getName());
+ Bundle arguments = startedIntent.getBundleExtra(
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(arguments).isNotNull();
+ assertThat(arguments.getInt(AppRestrictionsFragment.EXTRA_USER_ID, 0))
+ .isEqualTo(mUserInfo.id);
+ assertThat(arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false))
+ .isEqualTo(true);
+ }
+
+ @Test
+ public void initialize_adminSelectsRestrictedUser_shouldSetupPreferences() {
+ setupSelectedRestrictedUser();
+ mUserManager.setIsAdminUser(true);
+ doReturn(true).when(mTelephonyManager).isVoiceCapable();
+
+ mFragment.initialize(mActivity, mArguments);
+
+ verify(mFragment, never()).removePreference(KEY_REMOVE_USER);
+ verify(mFragment, never()).removePreference(KEY_SWITCH_USER);
+ verify(mFragment, never()).removePreference(KEY_APP_AND_CONTENT_ACCESS);
+ verify(mFragment).removePreference(KEY_ENABLE_TELEPHONY);
+ verify(mSwitchUserPref).setTitle("Switch to " + mUserInfo.name);
+ verify(mAppAndContentAccessPref).setOnPreferenceClickListener(mFragment);
+ verify(mSwitchUserPref).setOnPreferenceClickListener(mFragment);
+ verify(mRemoveUserPref).setOnPreferenceClickListener(mFragment);
+ }
+
+ @Test
+ public void initialize_adminSelectsExistingRestrictedUser_shouldNotStartAppAndContentAccess() {
+ setupSelectedRestrictedUser();
+ mUserManager.setIsAdminUser(true);
+ mArguments.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, false);
+
+ mFragment.initialize(mActivity, mArguments);
+
+ verify(mActivity, never()).startActivity(any(Intent.class));
+ }
+
+ @Test
public void initialize_adminSelectsGuest_shouldShowRemovePreference() {
setupSelectedGuest();
mUserManager.setIsAdminUser(true);
@@ -344,6 +432,7 @@
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_OK);
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
mFragment.mUserInfo = mUserInfo;
mFragment.onPreferenceClick(mSwitchUserPref);
@@ -357,6 +446,7 @@
mUserManager.setSwitchabilityStatus(SWITCHABILITY_STATUS_USER_SWITCH_DISALLOWED);
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
mFragment.mUserInfo = mUserInfo;
mFragment.onPreferenceClick(mSwitchUserPref);
@@ -371,6 +461,7 @@
mUserManager.setIsAdminUser(true);
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
doNothing().when(mFragment).showDialog(anyInt());
mFragment.onPreferenceClick(mRemoveUserPref);
@@ -386,6 +477,7 @@
mUserManager.setIsAdminUser(false);
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
doNothing().when(mFragment).showDialog(anyInt());
mFragment.onPreferenceClick(mRemoveUserPref);
@@ -395,11 +487,36 @@
}
@Test
+ public void onPreferenceClick_selectRestrictedUser_appAndContentAccessClicked_startActivity() {
+ setupSelectedRestrictedUser();
+ mFragment.mUserInfo = mUserInfo;
+ mUserManager.setIsAdminUser(true);
+ mFragment.mSwitchUserPref = mSwitchUserPref;
+ mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
+
+ mFragment.onPreferenceClick(mAppAndContentAccessPref);
+
+ Intent startedIntent = shadowOf(mActivity).getNextStartedActivity();
+ ShadowIntent shadowIntent = shadowOf(startedIntent);
+ assertThat(shadowIntent.getIntentClass()).isEqualTo(SubSettings.class);
+ assertThat(startedIntent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
+ .isEqualTo(AppRestrictionsFragment.class.getName());
+ Bundle arguments = startedIntent.getBundleExtra(
+ SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(arguments.getInt(AppRestrictionsFragment.EXTRA_USER_ID, 0))
+ .isEqualTo(mUserInfo.id);
+ assertThat(arguments.getBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, true))
+ .isEqualTo(false);
+ }
+
+ @Test
public void onPreferenceClick_unknownPreferenceClicked_doNothing() {
setupSelectedUser();
mFragment.mUserInfo = mUserInfo;
mFragment.mSwitchUserPref = mSwitchUserPref;
mFragment.mRemoveUserPref = mRemoveUserPref;
+ mFragment.mAppAndContentAccessPref = mAppAndContentAccessPref;
mFragment.onPreferenceClick(mock(UserPreference.class));
@@ -464,4 +581,13 @@
mUserManager.addProfile(mUserInfo);
}
+
+ private void setupSelectedRestrictedUser() {
+ mArguments.putInt("user_id", 21);
+ mUserInfo = new UserInfo(21, "Bob", null,
+ UserInfo.FLAG_FULL | UserInfo.FLAG_INITIALIZED | UserInfo.FLAG_RESTRICTED,
+ UserManager.USER_TYPE_FULL_RESTRICTED);
+
+ mUserManager.addProfile(mUserInfo);
+ }
}