From 64c668e7b587f8253d92e1fdc490e76b2f9df2c1 Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Fri, 9 Jul 2021 15:52:54 -0700 Subject: [PATCH] Easy app-installing for Guest users Allows an admin user to selectively install packages into a Guest user by displaying a list of available packages that are installed in the admin, but not in the Guest, and letting the admin choose to copy those apps to the Guest. Test: atest SettingsLibTests:com.android.settingslib.users.AppCopyingHelperTest Test: Manual: install some apps in user 0, create a guest, uninstall some system apps from it. Now, open this panel. Request some of those apps be installed in the Guest and verify it worked. Bug: 193281439 Change-Id: I4e6874a4ee93cd7bba96e1f6c8d04ed95873c1a2 --- res/drawable/ic_apps.xml | 3 +- res/values/strings.xml | 5 + res/xml/app_copier.xml | 20 ++ res/xml/user_details_settings.xml | 4 + .../settings/users/AppCopyFragment.java | 231 ++++++++++++++++++ .../settings/users/UserDetailsSettings.java | 22 ++ 6 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 res/xml/app_copier.xml create mode 100644 src/com/android/settings/users/AppCopyFragment.java diff --git a/res/drawable/ic_apps.xml b/res/drawable/ic_apps.xml index 20583a16f73..76ba829f5db 100644 --- a/res/drawable/ic_apps.xml +++ b/res/drawable/ic_apps.xml @@ -17,7 +17,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?android:attr/colorControlNormal"> Expand settings for application + + Choose apps to install + + Install available apps + Contactless payments diff --git a/res/xml/app_copier.xml b/res/xml/app_copier.xml new file mode 100644 index 00000000000..d8b4bb8e615 --- /dev/null +++ b/res/xml/app_copier.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/res/xml/user_details_settings.xml b/res/xml/user_details_settings.xml index 9280ff11632..2301bac1d42 100644 --- a/res/xml/user_details_settings.xml +++ b/res/xml/user_details_settings.xml @@ -29,6 +29,10 @@ android:key="app_and_content_access" android:icon="@drawable/ic_lock_closed" android:title="@string/user_restrictions_title" /> + () { + @Override + protected Void doInBackground(Void... params) { + mHelper.installSelectedApps(); + return null; + } + }.execute(); + } + } + + private void onPackageChanged(Intent intent) { + final String action = intent.getAction(); + final String packageName = intent.getData().getSchemeSpecificPart(); + if (DEBUG) Log.d(TAG, "onPackageChanged (" + action + "): " + packageName); + + // Package added/removed, so check if the preference needs to be enabled + final AppSwitchPreference pref = findPreference(getKeyForPackage(packageName)); + if (pref == null) return; + + if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { + pref.setEnabled(false); + pref.setChecked(false); + mHelper.setPackageSelected(packageName, false); + } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { + pref.setEnabled(true); + } + } + + private class AppLoadingTask extends AsyncTask { + + @Override + protected Void doInBackground(Void... params) { + mHelper.fetchAndMergeApps(); + return null; + } + + @Override + protected void onPostExecute(Void result) { + populateApps(); + } + } + + private void populateApps() { + // Check if the user was removed in the meantime. + if (Utils.getExistingUser(mUserManager, mUser) == null) { + return; + } + mHelper.resetSelectedPackages(); + mAppList.removeAll(); + for (AppCopyHelper.SelectableAppInfo app : mHelper.getVisibleApps()) { + if (app.packageName == null) continue; + + final AppSwitchPreference p = new AppSwitchPreference(getPrefContext()); + p.setIcon(app.icon != null ? app.icon.mutate() : null); + p.setChecked(false); + p.setTitle(app.appName); + p.setKey(getKeyForPackage(app.packageName)); + p.setPersistent(false); + p.setOnPreferenceChangeListener((preference, newValue) -> { + if (!preference.isEnabled()) { + // This item isn't available anymore (perhaps it was since uninstalled). + if (DEBUG) Log.d(TAG, "onPreferenceChange but not enabled"); + return false; + } + + final boolean checked = (boolean) newValue; + final String packageName = preference.getKey().substring(PKG_PREFIX.length()); + if (DEBUG) Log.d(TAG, "onPreferenceChange: " + packageName + " check=" + newValue); + mHelper.setPackageSelected(packageName, checked); + mAppListChanged = true; + return true; + }); + + mAppList.addPreference(p); + } + mAppListChanged = true; + } + + private String getKeyForPackage(String packageName) { + return PKG_PREFIX + packageName; + } +} diff --git a/src/com/android/settings/users/UserDetailsSettings.java b/src/com/android/settings/users/UserDetailsSettings.java index ce186f2134a..bd4c3e855fe 100644 --- a/src/com/android/settings/users/UserDetailsSettings.java +++ b/src/com/android/settings/users/UserDetailsSettings.java @@ -62,6 +62,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment 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 String KEY_APP_COPYING = "app_copying"; /** Integer extra containing the userId to manage */ static final String EXTRA_USER_ID = "user_id"; @@ -84,9 +85,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment @VisibleForTesting Preference mAppAndContentAccessPref; @VisibleForTesting + Preference mAppCopyingPref; + @VisibleForTesting Preference mRemoveUserPref; @VisibleForTesting + /** The user being studied (not the user doing the studying). */ UserInfo mUserInfo; private Bundle mDefaultGuestRestrictions; @@ -142,6 +146,9 @@ public class UserDetailsSettings extends SettingsPreferenceFragment } else if (preference == mAppAndContentAccessPref) { openAppAndContentAccessScreen(false); return true; + } else if (preference == mAppCopyingPref) { + openAppCopyingScreen(); + return true; } return false; } @@ -241,6 +248,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment mPhonePref = findPreference(KEY_ENABLE_TELEPHONY); mRemoveUserPref = findPreference(KEY_REMOVE_USER); mAppAndContentAccessPref = findPreference(KEY_APP_AND_CONTENT_ACCESS); + mAppCopyingPref = findPreference(KEY_APP_COPYING); mSwitchUserPref.setTitle( context.getString(com.android.settingslib.R.string.user_switch_to_user, @@ -258,6 +266,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment removePreference(KEY_ENABLE_TELEPHONY); removePreference(KEY_REMOVE_USER); removePreference(KEY_APP_AND_CONTENT_ACCESS); + removePreference(KEY_APP_COPYING); } else { if (!Utils.isVoiceCapable(context)) { // no telephony removePreference(KEY_ENABLE_TELEPHONY); @@ -292,6 +301,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment mPhonePref.setChecked(!mUserManager.hasUserRestriction( UserManager.DISALLOW_OUTGOING_CALLS, new UserHandle(userId))); mRemoveUserPref.setTitle(R.string.user_remove_user); + removePreference(KEY_APP_COPYING); } if (RestrictedLockUtilsInternal.hasBaseUserRestriction(context, UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId())) { @@ -301,6 +311,7 @@ public class UserDetailsSettings extends SettingsPreferenceFragment mRemoveUserPref.setOnPreferenceClickListener(this); mPhonePref.setOnPreferenceChangeListener(this); mAppAndContentAccessPref.setOnPreferenceClickListener(this); + mAppCopyingPref.setOnPreferenceClickListener(this); } } @@ -395,6 +406,17 @@ public class UserDetailsSettings extends SettingsPreferenceFragment .launch(); } + private void openAppCopyingScreen() { + final Bundle extras = new Bundle(); + extras.putInt(AppRestrictionsFragment.EXTRA_USER_ID, mUserInfo.id); + new SubSettingLauncher(getContext()) + .setDestination(AppCopyFragment.class.getName()) + .setArguments(extras) + .setTitleRes(R.string.user_copy_apps_menu_title) + .setSourceMetricsCategory(getMetricsCategory()) + .launch(); + } + private boolean isSecondaryUser(UserInfo user) { return UserManager.USER_TYPE_FULL_SECONDARY.equals(user.userType); }