summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2025-02-04 10:43:25 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-02-04 10:43:25 -0800
commit2ed7c4c3e74acd854ff955afb684cfcb9b40963e (patch)
treea6498c58f0a50d7ba6cd65b5758b7dbabdd80090
parent694f01b016f137012fe39740bb7804d5d9740e4f (diff)
parentb25f034ab782e6bae168b1f8ee276206ef9fbdf8 (diff)
Merge "Refactor default app implementation" into main
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/model/Role.java69
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java20
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/SignedPackage.java155
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/util/SignedPackageUtils.java84
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java109
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java27
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java6
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataFilterFunction.java (renamed from PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java)32
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataSortFunction.java49
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java17
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java92
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationItem.java53
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java25
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java12
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java34
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java37
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java13
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt22
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt11
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt20
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt37
-rw-r--r--PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt11
23 files changed, 616 insertions, 339 deletions
diff --git a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
index 5109e505b..64682ba79 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/model/Role.java
@@ -50,6 +50,7 @@ import com.android.role.controller.util.CollectionUtils;
import com.android.role.controller.util.PackageUtils;
import com.android.role.controller.util.RoleFlags;
import com.android.role.controller.util.RoleManagerCompat;
+import com.android.role.controller.util.SignedPackageUtils;
import com.android.role.controller.util.UserUtils;
import java.lang.annotation.Retention;
@@ -84,10 +85,6 @@ public class Role {
private static final String PACKAGE_NAME_ANDROID_SYSTEM = "android";
- private static final String DEFAULT_HOLDER_SEPARATOR = ";";
-
- private static final String CERTIFICATE_SEPARATOR = ":";
-
@Retention(RetentionPolicy.SOURCE)
@IntDef({
EXCLUSIVITY_NONE,
@@ -560,69 +557,11 @@ public class Role {
}
if (isExclusive()) {
- String packageName = getQualifiedDefaultHolderPackageNameAsUser(defaultHolders, user,
- context);
- if (packageName == null) {
- return Collections.emptyList();
- }
- return Collections.singletonList(packageName);
- } else {
- List<String> packageNames = new ArrayList<>();
- for (String defaultHolder : defaultHolders.split(DEFAULT_HOLDER_SEPARATOR)) {
- String packageName = getQualifiedDefaultHolderPackageNameAsUser(defaultHolder,
- user, context);
- if (packageName != null) {
- packageNames.add(packageName);
- }
- }
- return packageNames;
- }
- }
-
- @Nullable
- private String getQualifiedDefaultHolderPackageNameAsUser(@NonNull String defaultHolder,
- @NonNull UserHandle user, @NonNull Context context) {
- String packageName;
- byte[] certificate;
- int certificateSeparatorIndex = defaultHolder.indexOf(CERTIFICATE_SEPARATOR);
- if (certificateSeparatorIndex != -1) {
- packageName = defaultHolder.substring(0, certificateSeparatorIndex);
- String certificateString = defaultHolder.substring(certificateSeparatorIndex + 1);
- try {
- certificate = new Signature(certificateString).toByteArray();
- } catch (IllegalArgumentException e) {
- Log.w(LOG_TAG, "Cannot parse signing certificate: " + defaultHolder, e);
- return null;
- }
- } else {
- packageName = defaultHolder;
- certificate = null;
- }
-
- if (certificate != null) {
- Context userContext = UserUtils.getUserContext(context, user);
- PackageManager userPackageManager = userContext.getPackageManager();
- if (!userPackageManager.hasSigningCertificate(packageName, certificate,
- PackageManager.CERT_INPUT_SHA256)) {
- Log.w(LOG_TAG, "Default holder doesn't have required signing certificate: "
- + defaultHolder);
- return null;
- }
+ return CollectionUtils.singletonOrEmpty(
+ SignedPackageUtils.getPackageNameAsUser(defaultHolders, user, context));
} else {
- ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
- user, context);
- if (applicationInfo == null) {
- Log.w(LOG_TAG, "Cannot get ApplicationInfo for default holder: " + packageName);
- return null;
- }
- if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
- Log.w(LOG_TAG, "Default holder didn't specify a signing certificate and isn't a"
- + " system app: " + packageName);
- return null;
- }
+ return SignedPackageUtils.getPackageNamesAsUser(defaultHolders, user, context);
}
-
- return packageName;
}
/**
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
index cbffd451a..512015972 100644
--- a/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/PackageUtils.java
@@ -57,7 +57,7 @@ public final class PackageUtils {
}
/**
- * Retrieve if a package is a system package.
+ * Check whether a package is a system package.
*
* @param packageName the name of the package
* @param user the user of the package
@@ -93,4 +93,22 @@ public final class PackageUtils {
return null;
}
}
+
+ /**
+ * Check whether a package has a signing certificate.
+ *
+ * @param packageName the name of the package
+ * @param certificate the signing certificate
+ * @param user the user of the package
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return whether the package has the signing certificate.
+ */
+ public static boolean hasSigningCertificateAsUser(@NonNull String packageName,
+ @NonNull byte[] certificate, @NonNull UserHandle user, @NonNull Context context) {
+ Context userContext = UserUtils.getUserContext(context, user);
+ PackageManager userPackageManager = userContext.getPackageManager();
+ return userPackageManager.hasSigningCertificate(packageName, certificate,
+ PackageManager.CERT_INPUT_SHA256);
+ }
}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackage.java b/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackage.java
new file mode 100644
index 000000000..a3869b349
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackage.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2025 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.role.controller.util;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.os.UserHandle;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A package name with an optional signing certificate.
+ */
+public class SignedPackage {
+
+ private static final String LOG_TAG = SignedPackage.class.getSimpleName();
+
+ private static final String SIGNED_PACKAGE_SEPARATOR = ";";
+ private static final String CERTIFICATE_SEPARATOR = ":";
+
+ /**
+ * The name of the package.
+ */
+ @NonNull
+ private final String mPackageName;
+
+ /**
+ * The signing certificate of the package.
+ */
+ @Nullable
+ private final byte[] mCertificate;
+
+ public SignedPackage(@NonNull String packageName, @Nullable byte[] certificate) {
+ mPackageName = packageName;
+ mCertificate = certificate;
+ }
+
+ @NonNull
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @Nullable
+ public byte[] getCertificate() {
+ return mCertificate;
+ }
+
+ /**
+ * Parse a {@link SignedPackage} from a string input.
+ *
+ * @param input the package name, optionally followed by a colon and a signing certificate
+ * digest if it's not a system app
+ *
+ * @return the parsed {@link SignedPackage}, or {@code null} if the input is invalid
+ */
+ @Nullable
+ public static SignedPackage parse(@NonNull String input) {
+ String packageName;
+ byte[] certificate;
+ int certificateSeparatorIndex = input.indexOf(CERTIFICATE_SEPARATOR);
+ if (certificateSeparatorIndex != -1) {
+ packageName = input.substring(0, certificateSeparatorIndex);
+ String certificateString = input.substring(certificateSeparatorIndex + 1);
+ try {
+ certificate = new Signature(certificateString).toByteArray();
+ } catch (IllegalArgumentException e) {
+ Log.w(LOG_TAG, "Cannot parse signing certificate: " + input, e);
+ return null;
+ }
+ } else {
+ packageName = input;
+ certificate = null;
+ }
+ return new SignedPackage(packageName, certificate);
+ }
+
+ /**
+ * Parse a list of {@link SignedPackage}s from a string input.
+ *
+ * @param input the package names, each optionally followed by a colon and a signing certificate
+ * digest if it's not a system app
+ *
+ * @return the parsed list of valid {@link SignedPackage}s
+ */
+ @NonNull
+ public static List<SignedPackage> parseList(@NonNull String input) {
+ if (TextUtils.isEmpty(input)) {
+ return Collections.emptyList();
+ }
+ List<SignedPackage> signedPackages = new ArrayList<>();
+ for (String signedPackageInput : input.split(SIGNED_PACKAGE_SEPARATOR)) {
+ SignedPackage signedPackage = parse(signedPackageInput);
+ if (signedPackage != null) {
+ signedPackages.add(signedPackage);
+ }
+ }
+ return signedPackages;
+ }
+
+ /*
+ * Checks whether this signed package is available, i.e. it is installed, and either has the
+ * specified signing certificate or is a system app if no signing certificate is specified.
+ *
+ * @param user the user of the package
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return whether this signed package is available
+ */
+ public boolean isAvailableAsUser(@NonNull UserHandle user, @NonNull Context context) {
+ if (mCertificate != null) {
+ if (!PackageUtils.hasSigningCertificateAsUser(mPackageName, mCertificate, user,
+ context)) {
+ Log.w(LOG_TAG, "Package doesn't have required signing certificate: "
+ + mPackageName);
+ return false;
+ }
+ } else {
+ ApplicationInfo applicationInfo =
+ PackageUtils.getApplicationInfoAsUser(mPackageName, user, context);
+ if (applicationInfo == null) {
+ Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + mPackageName);
+ return false;
+ }
+ if ((applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Log.w(LOG_TAG, "Package didn't specify a signing certificate and isn't a" +
+ " system app: " + mPackageName);
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackageUtils.java b/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackageUtils.java
new file mode 100644
index 000000000..817c31c5f
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/util/SignedPackageUtils.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2025 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.role.controller.util;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.UserHandle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Utility methods about {@link SignedPackage}.
+ */
+public final class SignedPackageUtils {
+
+ private SignedPackageUtils() {}
+
+ /**
+ * Get a list of package names from a string input for {@link SignedPackage}s.
+ * <p>
+ * This method only returns packages that are
+ * {@link SignedPackage#isAvailableAsUser(UserHandle, Context) available}.
+ *
+ * @param input the string input
+ * @param user the user of the packages
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the package names
+ */
+ @NonNull
+ public static List<String> getPackageNamesAsUser(@NonNull String input,
+ @NonNull UserHandle user, @NonNull Context context) {
+ List<SignedPackage> signedPackages = SignedPackage.parseList(input);
+ List<String> packageNames = new ArrayList<>();
+ int signedPackagesSize = signedPackages.size();
+ for (int i = 0; i < signedPackagesSize; i++) {
+ SignedPackage signedPackage = signedPackages.get(i);
+ if (signedPackage.isAvailableAsUser(user, context)) {
+ packageNames.add(signedPackage.getPackageName());
+ }
+ }
+ return packageNames;
+ }
+
+ /**
+ * Get a package name from a string input for a {@link SignedPackage}.
+ * <p>
+ * This method only returns a package if it is
+ * {@link SignedPackage#isAvailableAsUser(UserHandle, Context) available}.
+ *
+ * @param input the string input
+ * @param user the user of the package
+ * @param context the {@code Context} to retrieve system services
+ *
+ * @return the package name, or {@code null} otherwise
+ */
+ @Nullable
+ public static String getPackageNameAsUser(@NonNull String input, @NonNull UserHandle user,
+ @NonNull Context context) {
+ SignedPackage signedPackage = SignedPackage.parse(input);
+ if (signedPackage == null || !signedPackage.isAvailableAsUser(user, context)) {
+ return null;
+ }
+ return signedPackage.getPackageName();
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
index 61e33d10b..5f870b1e3 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppChildFragment.java
@@ -25,7 +25,6 @@ import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -34,6 +33,7 @@ import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
@@ -125,13 +125,17 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
ViewModelProvider.Factory viewModelFactory = new DefaultAppViewModel.Factory(mRole, mUser,
activity.getApplication());
mViewModel = new ViewModelProvider(this, viewModelFactory).get(DefaultAppViewModel.class);
- mViewModel.getRoleLiveData().observe(this, this::onRoleChanged);
+ mViewModel.getLiveData().observe(this, applicationItems -> onApplicationListChanged());
mViewModel.getManageRoleHolderStateLiveData().observe(this,
this::onManageRoleHolderStateChanged);
}
- private void onRoleChanged(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
+ private void onApplicationListChanged() {
+ List<RoleApplicationItem> applicationItems = mViewModel.getLiveData().getValue();
+ if (applicationItems == null) {
+ return;
+ }
+
PF preferenceFragment = requirePreferenceFragment();
PreferenceManager preferenceManager = preferenceFragment.getPreferenceManager();
Context context = preferenceManager.getContext();
@@ -142,37 +146,12 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
preferenceScreen = preferenceManager.createPreferenceScreen(context);
preferenceFragment.setPreferenceScreen(preferenceScreen);
} else {
- for (int i = preferenceScreen.getPreferenceCount() - 1; i >= 0; --i) {
- Preference preference = preferenceScreen.getPreference(i);
-
- preferenceScreen.removePreference(preference);
- preference.setOrder(Preference.DEFAULT_ORDER);
- oldPreferences.put(preference.getKey(), preference);
- }
+ clearPreferences(preferenceScreen, oldPreferences);
}
- if (mRole.shouldShowNone()) {
- Drawable icon = AppCompatResources.getDrawable(context, R.drawable.ic_remove_circle);
- String title = getString(R.string.default_app_none);
- boolean noHolderApplication = !hasHolderApplication(qualifyingApplications);
- addPreference(PREFERENCE_KEY_NONE, icon, title, noHolderApplication, null,
- oldPreferences, preferenceScreen, context);
- }
-
- int qualifyingApplicationsSize = qualifyingApplications.size();
- for (int i = 0; i < qualifyingApplicationsSize; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(i);
- ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first;
- boolean isHolderApplication = qualifyingApplication.second;
-
- int userId =
- UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid).getIdentifier();
- String key = qualifyingApplicationInfo.packageName + "@" + userId;
- Drawable icon = Utils.getBadgedIcon(context, qualifyingApplicationInfo);
- String title = Utils.getFullAppLabel(qualifyingApplicationInfo, context);
- addPreference(key, icon, title, isHolderApplication, qualifyingApplicationInfo,
- oldPreferences, preferenceScreen, context);
- }
+ boolean noneChecked = !hasHolderApplication(applicationItems);
+ addNonePreferenceIfNeeded(preferenceScreen, noneChecked, oldPreferences, context);
+ addApplicationPreferences(preferenceScreen, applicationItems, oldPreferences, context);
addNonPaymentNfcServicesPreference(preferenceScreen, oldPreferences, context);
addDescriptionPreference(preferenceScreen, oldPreferences);
@@ -180,24 +159,64 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
preferenceFragment.onPreferenceScreenChanged();
}
- private static boolean hasHolderApplication(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
- int qualifyingApplicationsSize = qualifyingApplications.size();
- for (int i = 0; i < qualifyingApplicationsSize; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(i);
- boolean isHolderApplication = qualifyingApplication.second;
+ private static void clearPreferences(@NonNull PreferenceGroup preferenceGroup,
+ @NonNull ArrayMap<String, Preference> oldPreferences) {
+ for (int i = preferenceGroup.getPreferenceCount() - 1; i >= 0; --i) {
+ Preference preference = preferenceGroup.getPreference(i);
+
+ preferenceGroup.removePreference(preference);
+ preference.setOrder(Preference.DEFAULT_ORDER);
+ oldPreferences.put(preference.getKey(), preference);
+ }
+ }
- if (isHolderApplication) {
+ private static boolean hasHolderApplication(
+ @NonNull List<RoleApplicationItem> applicationItems) {
+ int applicationItemsSize = applicationItems.size();
+ for (int i = 0; i < applicationItemsSize; i++) {
+ RoleApplicationItem applicationItem = applicationItems.get(i);
+ if (applicationItem.isHolderApplication()) {
return true;
}
}
return false;
}
- private void addPreference(@NonNull String key, @NonNull Drawable icon,
- @NonNull CharSequence title, boolean checked, @Nullable ApplicationInfo applicationInfo,
- @NonNull ArrayMap<String, Preference> oldPreferences,
- @NonNull PreferenceScreen preferenceScreen, @NonNull Context context) {
+ private void addNonePreferenceIfNeeded(@NonNull PreferenceGroup preferenceGroup,
+ boolean checked, @NonNull ArrayMap<String, Preference> oldPreferences,
+ @NonNull Context context) {
+ if (!mRole.shouldShowNone()) {
+ return;
+ }
+
+ Drawable icon = AppCompatResources.getDrawable(context, R.drawable.ic_remove_circle);
+ String title = getString(R.string.default_app_none);
+ addApplicationPreference(preferenceGroup, PREFERENCE_KEY_NONE, icon, title, checked, null,
+ oldPreferences, context);
+ }
+
+ private void addApplicationPreferences(@NonNull PreferenceGroup preferenceGroup,
+ @NonNull List<RoleApplicationItem> applicationItems,
+ @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) {
+ int applicationItemsSize = applicationItems.size();
+ for (int i = 0; i < applicationItemsSize; i++) {
+ RoleApplicationItem applicationItem = applicationItems.get(i);
+ ApplicationInfo applicationInfo = applicationItem.getApplicationInfo();
+ int userId = UserHandle.getUserHandleForUid(applicationInfo.uid).getIdentifier();
+ String key = applicationInfo.packageName + "@" + userId;
+ Drawable icon = Utils.getBadgedIcon(context, applicationInfo);
+ String title = Utils.getFullAppLabel(applicationInfo, context);
+ boolean isHolderApplication = applicationItem.isHolderApplication();
+
+ addApplicationPreference(preferenceGroup, key, icon, title, isHolderApplication,
+ applicationInfo, oldPreferences, context);
+ }
+ }
+
+ private void addApplicationPreference(@NonNull PreferenceGroup preferenceGroup,
+ @NonNull String key, @NonNull Drawable icon, @NonNull CharSequence title,
+ boolean checked, @Nullable ApplicationInfo applicationInfo,
+ @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) {
RoleApplicationPreference roleApplicationPreference =
(RoleApplicationPreference) oldPreferences.get(key);
TwoStatePreference preference;
@@ -233,7 +252,7 @@ public class DefaultAppChildFragment<PF extends PreferenceFragmentCompat
applicationInfo, user, context);
}
- preferenceScreen.addPreference(preference);
+ preferenceGroup.addPreference(preference);
}
private void onManageRoleHolderStateChanged(int state) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
index 48472bc5e..2a987167e 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListChildFragment.java
@@ -131,18 +131,16 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
} else {
oldWorkPreferenceCategory =
preferenceScreen.findPreference(PREFERENCE_KEY_WORK_CATEGORY);
- clearPreferenceCategory(
- oldWorkPreferenceCategory, preferenceScreen, oldWorkPreferences);
+ clearPreferenceCategory(oldWorkPreferenceCategory, oldWorkPreferences);
oldPrivatePreferenceCategory =
preferenceScreen.findPreference(PREFERENCE_KEY_PRIVATE_CATEGORY);
- clearPreferenceCategory(
- oldPrivatePreferenceCategory, preferenceScreen, oldPrivatePreferences);
+ clearPreferenceCategory(oldPrivatePreferenceCategory, oldPrivatePreferences);
clearPreferences(preferenceScreen, oldPreferences);
}
- addPreferences(preferenceScreen, roleItems, oldPreferences, this, mViewModel.getUser(),
+ addRolePreferences(preferenceScreen, roleItems, oldPreferences, this, mViewModel.getUser(),
context);
addMoreDefaultAppsPreference(preferenceScreen, oldPreferences, context);
addManageDomainUrlsPreference(preferenceScreen, oldPreferences, context);
@@ -155,7 +153,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
}
String workTitle = Utils.getEnterpriseString(context,
DefaultAppSettings.WORK_PROFILE_DEFAULT_APPS_TITLE, defaultWorkTitle);
- addPreferenceCategory(oldWorkPreferenceCategory, PREFERENCE_KEY_WORK_CATEGORY,
+ addRolePreferenceCategory(oldWorkPreferenceCategory, PREFERENCE_KEY_WORK_CATEGORY,
workTitle, preferenceScreen, workRoleItems, oldWorkPreferences, this,
mViewModel.getWorkProfile(), context);
}
@@ -166,22 +164,22 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
} else {
privateTitle = context.getString(R.string.default_apps_for_private_profile);
}
- addPreferenceCategory(oldPrivatePreferenceCategory, PREFERENCE_KEY_PRIVATE_CATEGORY,
- privateTitle, preferenceScreen, privateRoleItems, oldPrivatePreferences, this,
- mViewModel.getPrivateProfile(), context);
+ addRolePreferenceCategory(oldPrivatePreferenceCategory,
+ PREFERENCE_KEY_PRIVATE_CATEGORY, privateTitle, preferenceScreen,
+ privateRoleItems, oldPrivatePreferences, this, mViewModel.getPrivateProfile(),
+ context);
}
preferenceFragment.onPreferenceScreenChanged();
}
private static void clearPreferenceCategory(@Nullable PreferenceCategory preferenceCategory,
- @NonNull PreferenceScreen preferenceScreen,
@NonNull ArrayMap<String, Preference> oldPreferences) {
if (preferenceCategory == null) {
return;
}
clearPreferences(preferenceCategory, oldPreferences);
- preferenceScreen.removePreference(preferenceCategory);
+ preferenceCategory.getParent().removePreference(preferenceCategory);
preferenceCategory.setOrder(Preference.DEFAULT_ORDER);
}
@@ -197,7 +195,7 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
}
@NonNull
- private void addPreferenceCategory(
+ private void addRolePreferenceCategory(
@Nullable PreferenceCategory oldPreferenceCategory, @NonNull String key,
@Nullable String title, @NonNull PreferenceScreen preferenceScreen,
@NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences,
@@ -210,11 +208,10 @@ public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
preferenceCategory.setTitle(title);
}
preferenceScreen.addPreference(preferenceCategory);
- addPreferences(preferenceCategory, roleItems, oldPreferences, listener,
- user, context);
+ addRolePreferences(preferenceCategory, roleItems, oldPreferences, listener, user, context);
}
- private void addPreferences(@NonNull PreferenceGroup preferenceGroup,
+ private void addRolePreferences(@NonNull PreferenceGroup preferenceGroup,
@NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences,
@NonNull Preference.OnPreferenceClickListener listener, @NonNull UserHandle user,
@NonNull Context context) {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
index 718af090e..4a280bd58 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppListViewModel.java
@@ -78,11 +78,11 @@ public class DefaultAppListViewModel extends AndroidViewModel {
mLiveData = Transformations.map(
new MergeRoleListLiveData(liveData,
Transformations.map(workLiveData,
- new RoleListFilterFunction(exclusivityPredicate))),
+ new ListLiveDataFilterFunction<>(exclusivityPredicate))),
sortFunction);
mWorkLiveData = Transformations.map(
Transformations.map(workLiveData,
- new RoleListFilterFunction(exclusivityPredicate.negate())),
+ new ListLiveDataFilterFunction<>(exclusivityPredicate.negate())),
sortFunction);
} else if (Flags.crossUserRoleUxBugfixEnabled() && isWorkProfile) {
// Show profile group exclusive roles from the profile parent (full user) in primary
@@ -92,7 +92,7 @@ public class DefaultAppListViewModel extends AndroidViewModel {
mLiveData = Transformations.map(
new MergeRoleListLiveData(liveData,
Transformations.map(profileParentLiveData,
- new RoleListFilterFunction(exclusivityPredicate))),
+ new ListLiveDataFilterFunction<>(exclusivityPredicate))),
sortFunction);
mWorkLiveData = null;
} else {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
index cdee94b13..790c55d84 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/DefaultAppViewModel.java
@@ -18,10 +18,8 @@ package com.android.permissioncontroller.role.ui;
import android.app.Application;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
@@ -48,7 +46,7 @@ public class DefaultAppViewModel extends AndroidViewModel {
private final UserHandle mUser;
@NonNull
- private final LiveData<List<Pair<ApplicationInfo, Boolean>>> mRoleLiveData;
+ private final LiveData<List<RoleApplicationItem>> mLiveData;
@NonNull
private final ManageRoleHolderStateLiveData mManageRoleHolderStateLiveData =
@@ -60,30 +58,30 @@ public class DefaultAppViewModel extends AndroidViewModel {
mRole = role;
// If EXCLUSIVITY_PROFILE_GROUP this user should be profile parent
- mUser = mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP
+ mUser = role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP
? UserUtils.getProfileParentOrSelf(user, application)
: user;
- RoleLiveData liveData = new RoleLiveData(mRole, mUser, application);
+ RoleLiveData liveData = new RoleLiveData(role, mUser, application);
RoleSortFunction sortFunction = new RoleSortFunction(application);
- if (mRole.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
+ if (role.getExclusivity() == Role.EXCLUSIVITY_PROFILE_GROUP) {
// Context user might be work profile, ensure we get a non-null UserHandle if work
// profile exists. getWorkProfile returns null if context user is work profile.
UserHandle workProfile = UserUtils.getWorkProfileOrSelf(application);
if (workProfile != null) {
RoleLiveData workLiveData = new RoleLiveData(role, workProfile, application);
- mRoleLiveData = Transformations.map(new MergeRoleLiveData(liveData, workLiveData),
+ mLiveData = Transformations.map(new MergeRoleLiveData(liveData, workLiveData),
sortFunction);
} else {
- mRoleLiveData = Transformations.map(liveData, sortFunction);
+ mLiveData = Transformations.map(liveData, sortFunction);
}
} else {
- mRoleLiveData = Transformations.map(liveData, sortFunction);
+ mLiveData = Transformations.map(liveData, sortFunction);
}
}
@NonNull
- public LiveData<List<Pair<ApplicationInfo, Boolean>>> getRoleLiveData() {
- return mRoleLiveData;
+ public LiveData<List<RoleApplicationItem>> getLiveData() {
+ return mLiveData;
}
@NonNull
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataFilterFunction.java
index b84fa80b9..8657db0ab 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListFilterFunction.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataFilterFunction.java
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.permissioncontroller.role.ui;
import androidx.annotation.NonNull;
@@ -25,29 +26,32 @@ import java.util.List;
import java.util.function.Predicate;
/**
- * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
- * that filters a live data for role list.
+ * A function for
+ * {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData, Function1)}
+ * that filters a live data for a list.
+ *
+ * @param <T> the type of the list elements
*/
-public class RoleListFilterFunction implements Function1<List<RoleItem>, List<RoleItem>> {
- private final Predicate<RoleItem> mPredicate;
+public class ListLiveDataFilterFunction<T> implements Function1<List<T>, List<T>> {
+ private final Predicate<T> mPredicate;
- public RoleListFilterFunction(@NonNull Predicate<RoleItem> predicate) {
+ public ListLiveDataFilterFunction(@NonNull Predicate<T> predicate) {
mPredicate = predicate;
}
@NonNull
@Override
- public List<RoleItem> invoke(@Nullable List<RoleItem> roleItems) {
- List<RoleItem> filteredRoleItems = new ArrayList<>();
- if (roleItems != null) {
- int roleItemsSize = roleItems.size();
- for (int i = 0; i < roleItemsSize; i++) {
- RoleItem roleItem = roleItems.get(i);
- if (mPredicate.test(roleItem)) {
- filteredRoleItems.add(roleItem);
+ public List<T> invoke(@Nullable List<T> items) {
+ List<T> filteredItems = new ArrayList<>();
+ if (items != null) {
+ int itemsSize = items.size();
+ for (int i = 0; i < itemsSize; i++) {
+ T item = items.get(i);
+ if (mPredicate.test(item)) {
+ filteredItems.add(item);
}
}
}
- return filteredRoleItems;
+ return filteredItems;
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataSortFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataSortFunction.java
new file mode 100644
index 000000000..36e7afcc8
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/ListLiveDataSortFunction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2025 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.permissioncontroller.role.ui;
+
+import androidx.annotation.NonNull;
+
+import kotlin.jvm.functions.Function1;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * A function for
+ * {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData, Function1)}
+ * that sorts a live data for a list.
+ *
+ * @param <T> the type of the list elements
+ */
+public class ListLiveDataSortFunction<T> implements Function1<List<T>, List<T>> {
+
+ @NonNull
+ private final Comparator<T> mComparator;
+
+ public ListLiveDataSortFunction(@NonNull Comparator<T> comparator) {
+ mComparator = comparator;
+ }
+
+ @Override
+ public List<T> invoke(List<T> items) {
+ List<T> sortedItems = new ArrayList<>(items);
+ sortedItems.sort(mComparator);
+ return sortedItems;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java
index 31a02729a..72c2264ce 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/MergeRoleLiveData.java
@@ -16,9 +16,6 @@
package com.android.permissioncontroller.role.ui;
-import android.content.pm.ApplicationInfo;
-import android.util.Pair;
-
import androidx.annotation.NonNull;
import androidx.lifecycle.MediatorLiveData;
@@ -28,7 +25,7 @@ import java.util.List;
/**
* {@link MediatorLiveData} that merges multiple {@link RoleLiveData} instances.
*/
-public class MergeRoleLiveData extends MediatorLiveData<List<Pair<ApplicationInfo, Boolean>>> {
+public class MergeRoleLiveData extends MediatorLiveData<List<RoleApplicationItem>> {
@NonNull
private final RoleLiveData[] mLiveDatas;
@@ -40,23 +37,23 @@ public class MergeRoleLiveData extends MediatorLiveData<List<Pair<ApplicationInf
for (int i = 0; i < liveDatasLength; i++) {
RoleLiveData liveData = mLiveDatas[i];
- addSource(liveData, roleItems -> onRoleChanged());
+ addSource(liveData, items -> onRoleChanged());
}
}
private void onRoleChanged() {
- List<Pair<ApplicationInfo, Boolean>> mergedQualifyingApplications = new ArrayList<>();
+ List<RoleApplicationItem> mergedItems = new ArrayList<>();
int liveDatasLength = mLiveDatas.length;
for (int i = 0; i < liveDatasLength; i++) {
RoleLiveData liveData = mLiveDatas[i];
- List<Pair<ApplicationInfo, Boolean>> qualifyingApplications = liveData.getValue();
- if (qualifyingApplications == null) {
+ List<RoleApplicationItem> items = liveData.getValue();
+ if (items == null) {
return;
}
- mergedQualifyingApplications.addAll(qualifyingApplications);
+ mergedItems.addAll(items);
}
- setValue(mergedQualifyingApplications);
+ setValue(mergedItems);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
index 60139f0c8..f411b0cd0 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RequestRoleFragment.java
@@ -30,7 +30,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -236,7 +235,7 @@ public class RequestRoleFragment extends DialogFragment {
ViewModelProvider.Factory viewModelFactory = new RequestRoleViewModel.Factory(mRole,
requireActivity().getApplication());
mViewModel = new ViewModelProvider(this, viewModelFactory).get(RequestRoleViewModel.class);
- mViewModel.getRoleLiveData().observe(this, this::onRoleDataChanged);
+ mViewModel.getLiveData().observe(this, this::onApplicationListChanged);
mViewModel.getManageRoleHolderStateLiveData().observe(this,
this::onManageRoleHolderStateChanged);
}
@@ -272,9 +271,9 @@ public class RequestRoleFragment extends DialogFragment {
setDeniedOnceAndFinish();
}
- private void onRoleDataChanged(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
- mAdapter.replace(qualifyingApplications);
+ private void onApplicationListChanged(
+ @NonNull List<RoleApplicationItem> applicationItems) {
+ mAdapter.replace(applicationItems);
updateUi();
}
@@ -364,8 +363,8 @@ public class RequestRoleFragment extends DialogFragment {
boolean dontAskAgain = mDontAskAgainCheck != null && mDontAskAgainCheck.isChecked();
mAdapter.setDontAskAgain(dontAskAgain);
AlertDialog dialog = getDialog();
- boolean hasRoleData = mViewModel.getRoleLiveData().getValue() != null;
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled && hasRoleData
+ boolean hasApplicationList = mViewModel.getLiveData().getValue() != null;
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(enabled && hasApplicationList
&& (dontAskAgain || !mAdapter.isHolderApplicationChecked()));
dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setEnabled(enabled);
}
@@ -420,12 +419,12 @@ public class RequestRoleFragment extends DialogFragment {
}
int count = mAdapter.getCount();
for (int i = 0; i < count; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = mAdapter.getItem(i);
- if (qualifyingApplication == null) {
+ RoleApplicationItem applicationItem = mAdapter.getItem(i);
+ if (applicationItem == null) {
// Skip the "None" item.
continue;
}
- ApplicationInfo applicationInfo = qualifyingApplication.first;
+ ApplicationInfo applicationInfo = applicationItem.getApplicationInfo();
if (Objects.equals(UserPackage.from(applicationInfo), userPackage)) {
return applicationInfo.uid;
}
@@ -493,8 +492,7 @@ public class RequestRoleFragment extends DialogFragment {
// We'll use a null to represent the "None" item.
@NonNull
- private final List<Pair<ApplicationInfo, Boolean>> mQualifyingApplications =
- new ArrayList<>();
+ private final List<RoleApplicationItem> mApplicationItems = new ArrayList<>();
@Nullable
private UserPackage mHolderUserPackage;
@@ -546,12 +544,12 @@ public class RequestRoleFragment extends DialogFragment {
}
public void onItemClicked(int position) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
- if (qualifyingApplication == null) {
+ RoleApplicationItem applicationItem = getItem(position);
+ if (applicationItem == null) {
mUserChecked = true;
mCheckedUserPackage = null;
} else {
- ApplicationInfo applicationInfo = qualifyingApplication.first;
+ ApplicationInfo applicationInfo = applicationItem.getApplicationInfo();
UserHandle user = UserHandle.getUserHandleForUid(applicationInfo.uid);
Intent restrictionIntent = mRole.getApplicationRestrictionIntentAsUser(
applicationInfo, user, mListView.getContext());
@@ -566,24 +564,24 @@ public class RequestRoleFragment extends DialogFragment {
notifyDataSetChanged();
}
- public void replace(@NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
- mQualifyingApplications.clear();
+ public void replace(@NonNull List<RoleApplicationItem> applicationItems) {
+ mApplicationItems.clear();
if (mRole.shouldShowNone()) {
- mQualifyingApplications.add(0, null);
+ mApplicationItems.add(0, null);
}
- mQualifyingApplications.addAll(qualifyingApplications);
- mHolderUserPackage = getHolderUserPackage(qualifyingApplications);
+ mApplicationItems.addAll(applicationItems);
+ mHolderUserPackage = getHolderUserPackage(applicationItems);
if (mUserChecked && mCheckedUserPackage != null) {
boolean isCheckedPackageNameFound = false;
int count = getCount();
for (int i = 0; i < count; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(i);
- if (qualifyingApplication == null) {
+ RoleApplicationItem applicationItem = getItem(i);
+ if (applicationItem == null) {
continue;
}
- ApplicationInfo applicationInfo = qualifyingApplication.first;
- UserPackage userPackage = UserPackage.from(applicationInfo);
+ UserPackage userPackage =
+ UserPackage.from(applicationItem.getApplicationInfo());
if (Objects.equals(userPackage, mCheckedUserPackage)) {
mUserChecked = true;
@@ -606,19 +604,15 @@ public class RequestRoleFragment extends DialogFragment {
@Nullable
private static UserPackage getHolderUserPackage(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
- int qualifyingApplicationSize = qualifyingApplications.size();
- for (int i = 0; i < qualifyingApplicationSize; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(
- i);
- if (qualifyingApplication == null) {
+ @NonNull List<RoleApplicationItem> applicationItems) {
+ int applicationItemSize = applicationItems.size();
+ for (int i = 0; i < applicationItemSize; i++) {
+ RoleApplicationItem applicationItem = applicationItems.get(i);
+ if (applicationItem == null) {
continue;
}
- ApplicationInfo applicationInfo = qualifyingApplication.first;
- boolean isHolderApplication = qualifyingApplication.second;
-
- if (isHolderApplication) {
- return UserPackage.from(applicationInfo);
+ if (applicationItem.isHolderApplication()) {
+ return UserPackage.from(applicationItem.getApplicationInfo());
}
}
return null;
@@ -645,13 +639,13 @@ public class RequestRoleFragment extends DialogFragment {
@Override
public int getCount() {
- return mQualifyingApplications.size();
+ return mApplicationItems.size();
}
@Nullable
@Override
- public Pair<ApplicationInfo, Boolean> getItem(int position) {
- return mQualifyingApplications.get(position);
+ public RoleApplicationItem getItem(int position) {
+ return mApplicationItems.get(position);
}
@Override
@@ -660,9 +654,9 @@ public class RequestRoleFragment extends DialogFragment {
// Work around AbsListView.confirmCheckedPositionsById() not respecting our count.
return ListView.INVALID_ROW_ID;
}
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
- return qualifyingApplication == null ? 0
- : qualifyingApplication.first.packageName.hashCode();
+ RoleApplicationItem applicationItem = getItem(position);
+ return applicationItem == null ? 0
+ : applicationItem.getApplicationInfo().packageName.hashCode();
}
@Override
@@ -670,12 +664,11 @@ public class RequestRoleFragment extends DialogFragment {
if (!mDontAskAgain) {
return true;
}
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
- if (qualifyingApplication == null) {
+ RoleApplicationItem applicationItem = getItem(position);
+ if (applicationItem == null) {
return mHolderUserPackage == null;
} else {
- boolean isHolderApplication = qualifyingApplication.second;
- return isHolderApplication;
+ return applicationItem.isHolderApplication();
}
}
@@ -697,14 +690,14 @@ public class RequestRoleFragment extends DialogFragment {
LAYOUT_TRANSITION_DURATION_MILLIS);
}
- Pair<ApplicationInfo, Boolean> qualifyingApplication = getItem(position);
+ RoleApplicationItem applicationItem = getItem(position);
ApplicationInfo applicationInfo;
boolean restricted;
boolean checked;
Drawable icon;
String title;
String subtitle;
- if (qualifyingApplication == null) {
+ if (applicationItem == null) {
applicationInfo = null;
restricted = false;
checked = mCheckedUserPackage == null;
@@ -713,15 +706,14 @@ public class RequestRoleFragment extends DialogFragment {
subtitle = mHolderUserPackage == null ? context.getString(
R.string.request_role_current_default) : null;
} else {
- applicationInfo = qualifyingApplication.first;
+ applicationInfo = applicationItem.getApplicationInfo();
UserPackage userPackage = UserPackage.from(applicationInfo);
restricted = mRole.getApplicationRestrictionIntentAsUser(applicationInfo,
userPackage.user, context) != null;
checked = Objects.equals(userPackage, mCheckedUserPackage);
icon = Utils.getBadgedIcon(context, applicationInfo);
title = Utils.getAppLabel(applicationInfo, context);
- boolean isHolderApplication = qualifyingApplication.second;
- subtitle = isHolderApplication
+ subtitle = applicationItem.isHolderApplication()
? context.getString(R.string.request_role_current_default)
: checked ? context.getString(mRole.getRequestDescriptionResource()) : null;
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationItem.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationItem.java
new file mode 100644
index 000000000..b6c489ea1
--- /dev/null
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleApplicationItem.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2025 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.permissioncontroller.role.ui;
+
+import android.content.pm.ApplicationInfo;
+
+import androidx.annotation.NonNull;
+
+/**
+ * Information about an application to be displayed in a list of applications qualifying for a role.
+ */
+public class RoleApplicationItem {
+
+ /**
+ * The {@link ApplicationInfo} for this application.
+ */
+ @NonNull
+ private final ApplicationInfo mApplicationInfo;
+
+ /**
+ * Whether this application is holding the role.
+ */
+ private final boolean mIsHolderApplication;
+
+ public RoleApplicationItem(@NonNull ApplicationInfo applicationInfo,
+ boolean isHolderApplication) {
+ mApplicationInfo = applicationInfo;
+ mIsHolderApplication = isHolderApplication;
+ }
+
+ @NonNull
+ public ApplicationInfo getApplicationInfo() {
+ return mApplicationInfo;
+ }
+
+ public boolean isHolderApplication() {
+ return mIsHolderApplication;
+ }
+}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
index ca059aa32..07b4db1f4 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleListSortFunction.java
@@ -23,30 +23,23 @@ import androidx.annotation.NonNull;
import kotlin.jvm.functions.Function1;
-import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
/**
- * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
+ * A function for
+ * {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData, Function1)}
* that sorts a live data for role list.
*/
-public class RoleListSortFunction implements Function1<List<RoleItem>, List<RoleItem>> {
-
- @NonNull
- private final Comparator<RoleItem> mComparator;
+public class RoleListSortFunction extends ListLiveDataSortFunction<RoleItem> {
public RoleListSortFunction(@NonNull Context context) {
- Collator collator = Collator.getInstance(context.getResources().getConfiguration()
- .getLocales().get(0));
- mComparator = Comparator.comparing(roleItem -> context.getString(
- roleItem.getRole().getShortLabelResource()), collator);
+ super(createComparator(context));
}
- @Override
- public List<RoleItem> invoke(List<RoleItem> p1) {
- List<RoleItem> sorted = new ArrayList<>(p1);
- sorted.sort(mComparator);
- return sorted;
+ private static Comparator<RoleItem> createComparator(@NonNull Context context) {
+ Collator collator = Collator.getInstance(context.getResources().getConfiguration()
+ .getLocales().get(0));
+ return Comparator.comparing(item -> context.getString(
+ item.getRole().getShortLabelResource()), collator);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
index bb492f76d..1b6d42934 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleLiveData.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
@@ -38,7 +37,7 @@ import java.util.List;
/**
* {@link LiveData} for a role.
*/
-public class RoleLiveData extends AsyncTaskLiveData<List<Pair<ApplicationInfo, Boolean>>>
+public class RoleLiveData extends AsyncTaskLiveData<List<RoleApplicationItem>>
implements OnRoleHoldersChangedListener {
private static final String LOG_TAG = RoleLiveData.class.getSimpleName();
@@ -77,12 +76,12 @@ public class RoleLiveData extends AsyncTaskLiveData<List<Pair<ApplicationInfo, B
@Override
@WorkerThread
- protected List<Pair<ApplicationInfo, Boolean>> loadValueInBackground() {
+ protected List<RoleApplicationItem> loadValueInBackground() {
RoleManager roleManager = mContext.getSystemService(RoleManager.class);
List<String> holderPackageNames = roleManager.getRoleHoldersAsUser(mRole.getName(), mUser);
List<String> qualifyingPackageNames = mRole.getQualifyingPackagesAsUser(mUser, mContext);
- List<Pair<ApplicationInfo, Boolean>> qualifyingApplications = new ArrayList<>();
+ List<RoleApplicationItem> applicationItems = new ArrayList<>();
int qualifyingPackageNamesSize = qualifyingPackageNames.size();
for (int i = 0; i < qualifyingPackageNamesSize; i++) {
String qualifyingPackageName = qualifyingPackageNames.get(i);
@@ -98,9 +97,10 @@ public class RoleLiveData extends AsyncTaskLiveData<List<Pair<ApplicationInfo, B
continue;
}
boolean isHolderApplication = holderPackageNames.contains(qualifyingPackageName);
- qualifyingApplications.add(new Pair<>(qualifyingApplicationInfo, isHolderApplication));
+ applicationItems.add(
+ new RoleApplicationItem(qualifyingApplicationInfo, isHolderApplication));
}
- return qualifyingApplications;
+ return applicationItems;
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
index 10db9dbcd..9a06a6b01 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/RoleSortFunction.java
@@ -17,10 +17,8 @@
package com.android.permissioncontroller.role.ui;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.icu.text.Collator;
import android.os.UserHandle;
-import android.util.Pair;
import androidx.annotation.NonNull;
@@ -28,34 +26,26 @@ import com.android.permissioncontroller.permission.utils.Utils;
import kotlin.jvm.functions.Function1;
-import java.util.ArrayList;
import java.util.Comparator;
-import java.util.List;
/**
- * A function for {@link androidx.lifecycle#map(androidx.lifecycle.LiveData, Function1)}
+ * A function for
+ * {@link androidx.lifecycle.Transformations#map(androidx.lifecycle.LiveData, Function1)}
* that sorts a live data for role.
*/
-public class RoleSortFunction implements Function1<List<Pair<ApplicationInfo, Boolean>>,
- List<Pair<ApplicationInfo, Boolean>>> {
-
- @NonNull
- private final Comparator<Pair<ApplicationInfo, Boolean>> mComparator;
+public class RoleSortFunction extends ListLiveDataSortFunction<RoleApplicationItem> {
public RoleSortFunction(@NonNull Context context) {
- Collator collator = Collator.getInstance(context.getResources().getConfiguration()
- .getLocales().get(0));
- Comparator<Pair<ApplicationInfo, Boolean>> labelComparator = Comparator.comparing(role ->
- Utils.getAppLabel(role.first, context), collator);
- Comparator<Pair<ApplicationInfo, Boolean>> userIdComparator = Comparator.comparingInt(role
- -> UserHandle.getUserHandleForUid(role.first.uid).getIdentifier());
- mComparator = labelComparator.thenComparing(userIdComparator);
+ super(createComparator(context));
}
- @Override
- public List<Pair<ApplicationInfo, Boolean>> invoke(List<Pair<ApplicationInfo, Boolean>> p1) {
- List<Pair<ApplicationInfo, Boolean>> sorted = new ArrayList<>(p1);
- sorted.sort(mComparator);
- return sorted;
+ private static Comparator<RoleApplicationItem> createComparator(@NonNull Context context) {
+ Collator collator = Collator.getInstance(context.getResources().getConfiguration()
+ .getLocales().get(0));
+ Comparator<RoleApplicationItem> labelComparator = Comparator.comparing(item ->
+ Utils.getAppLabel(item.getApplicationInfo(), context), collator);
+ Comparator<RoleApplicationItem> userIdComparator = Comparator.comparingInt(item
+ -> UserHandle.getUserHandleForUid(item.getApplicationInfo().uid).getIdentifier());
+ return labelComparator.thenComparing(userIdComparator);
}
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
index 0963635e7..7a13eb2b5 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessChildFragment.java
@@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -37,6 +36,7 @@ import androidx.preference.TwoStatePreference;
import com.android.permissioncontroller.permission.utils.Utils;
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
+import com.android.permissioncontroller.role.ui.RoleApplicationItem;
import com.android.permissioncontroller.role.ui.RoleApplicationPreference;
import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils;
import com.android.role.controller.model.Role;
@@ -107,12 +107,12 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
activity.getApplication());
mViewModel = new ViewModelProvider(this, viewModelFactory)
.get(SpecialAppAccessViewModel.class);
- mViewModel.getRoleLiveData().observe(this, this::onRoleChanged);
+ mViewModel.getLiveData().observe(this, this::onApplicationListChanged);
mViewModel.observeManageRoleHolderState(this, this::onManageRoleHolderStateChanged);
}
- private void onRoleChanged(
- @NonNull List<Pair<ApplicationInfo, Boolean>> qualifyingApplications) {
+ private void onApplicationListChanged(
+ @NonNull List<RoleApplicationItem> applicationItems) {
PF preferenceFragment = requirePreferenceFragment();
PreferenceManager preferenceManager = preferenceFragment.getPreferenceManager();
Context context = preferenceManager.getContext();
@@ -138,14 +138,12 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
}
}
- int qualifyingApplicationsSize = qualifyingApplications.size();
- for (int i = 0; i < qualifyingApplicationsSize; i++) {
- Pair<ApplicationInfo, Boolean> qualifyingApplication = qualifyingApplications.get(i);
- ApplicationInfo qualifyingApplicationInfo = qualifyingApplication.first;
- boolean isHolderPackage = qualifyingApplication.second;
-
- String key = qualifyingApplicationInfo.packageName + '_'
- + qualifyingApplicationInfo.uid;
+ int applicationItemsSize = applicationItems.size();
+ for (int i = 0; i < applicationItemsSize; i++) {
+ RoleApplicationItem applicationItem = applicationItems.get(i);
+ ApplicationInfo applicationInfo = applicationItem.getApplicationInfo();
+ String key = applicationInfo.packageName + '_'
+ + applicationInfo.uid;
RoleApplicationPreference roleApplicationPreference =
(RoleApplicationPreference) oldPreferences.get(key);
TwoStatePreference preference;
@@ -153,24 +151,23 @@ public class SpecialAppAccessChildFragment<PF extends PreferenceFragmentCompat
roleApplicationPreference = preferenceFragment.createApplicationPreference();
preference = roleApplicationPreference.asTwoStatePreference();
preference.setKey(key);
- preference.setIcon(Utils.getBadgedIcon(context, qualifyingApplicationInfo));
- preference.setTitle(Utils.getFullAppLabel(qualifyingApplicationInfo, context));
+ preference.setIcon(Utils.getBadgedIcon(context, applicationInfo));
+ preference.setTitle(Utils.getFullAppLabel(applicationInfo, context));
preference.setPersistent(false);
preference.setOnPreferenceChangeListener((preference2, newValue) -> false);
preference.setOnPreferenceClickListener(this);
preference.getExtras().putParcelable(PREFERENCE_EXTRA_APPLICATION_INFO,
- qualifyingApplicationInfo);
+ applicationInfo);
} else {
preference = roleApplicationPreference.asTwoStatePreference();
}
- preference.setChecked(isHolderPackage);
- UserHandle user = UserHandle.getUserHandleForUid(qualifyingApplicationInfo.uid);
+ preference.setChecked(applicationItem.isHolderApplication());
+ UserHandle user = UserHandle.getUserHandleForUid(applicationInfo.uid);
roleApplicationPreference.setRestrictionIntent(
- mRole.getApplicationRestrictionIntentAsUser(qualifyingApplicationInfo, user,
- context));
+ mRole.getApplicationRestrictionIntentAsUser(applicationInfo, user, context));
RoleUiBehaviorUtils.prepareApplicationPreferenceAsUser(mRole, roleApplicationPreference,
- qualifyingApplicationInfo, user, context);
+ applicationInfo, user, context);
preferenceScreen.addPreference(preference);
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
index c12265d43..f4abc0db1 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/specialappaccess/SpecialAppAccessViewModel.java
@@ -17,12 +17,10 @@
package com.android.permissioncontroller.role.ui.specialappaccess;
import android.app.Application;
-import android.content.pm.ApplicationInfo;
import android.os.Process;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
-import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
@@ -34,6 +32,7 @@ import androidx.lifecycle.ViewModelProvider;
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData;
import com.android.permissioncontroller.role.ui.MergeRoleLiveData;
+import com.android.permissioncontroller.role.ui.RoleApplicationItem;
import com.android.permissioncontroller.role.ui.RoleLiveData;
import com.android.permissioncontroller.role.ui.RoleSortFunction;
import com.android.permissioncontroller.role.utils.UserUtils;
@@ -52,7 +51,7 @@ public class SpecialAppAccessViewModel extends AndroidViewModel {
private final Role mRole;
@NonNull
- private final LiveData<List<Pair<ApplicationInfo, Boolean>>> mRoleLiveData;
+ private final LiveData<List<RoleApplicationItem>> mLiveData;
@NonNull
private final ArrayMap<String, ManageRoleHolderStateLiveData> mManageRoleHolderStateLiveDatas =
@@ -68,17 +67,17 @@ public class SpecialAppAccessViewModel extends AndroidViewModel {
UserHandle workProfile = UserUtils.getWorkProfile(application);
RoleSortFunction sortFunction = new RoleSortFunction(application);
if (workProfile == null) {
- mRoleLiveData = Transformations.map(roleLiveData, sortFunction);
+ mLiveData = Transformations.map(roleLiveData, sortFunction);
} else {
RoleLiveData workRoleLiveData = new RoleLiveData(role, workProfile, application);
- mRoleLiveData = Transformations.map(new MergeRoleLiveData(roleLiveData,
+ mLiveData = Transformations.map(new MergeRoleLiveData(roleLiveData,
workRoleLiveData), sortFunction);
}
}
@NonNull
- public LiveData<List<Pair<ApplicationInfo, Boolean>>> getRoleLiveData() {
- return mRoleLiveData;
+ public LiveData<List<RoleApplicationItem>> getLiveData() {
+ return mLiveData;
}
/**
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
index 9e93f33c7..65548d9a1 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppHelper.kt
@@ -17,12 +17,11 @@
package com.android.permissioncontroller.role.ui.wear
import android.content.Context
-import android.content.pm.ApplicationInfo
import android.os.UserHandle
-import android.util.Pair
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.role.ui.DefaultAppViewModel
+import com.android.permissioncontroller.role.ui.RoleApplicationItem
import com.android.permissioncontroller.role.ui.wear.model.ConfirmDialogArgs
import com.android.permissioncontroller.role.ui.wear.model.DefaultAppConfirmDialogViewModel
import com.android.permissioncontroller.role.utils.RoleUiBehaviorUtils
@@ -39,13 +38,13 @@ class WearDefaultAppHelper(
fun getTitle() = context.getString(role.labelResource)
fun getNonePreference(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ applicationItems: List<RoleApplicationItem>
): WearRoleApplicationPreference? =
if (role.shouldShowNone()) {
WearRoleApplicationPreference(
context = context,
defaultLabel = context.getString(R.string.default_app_none),
- checked = !hasHolderApplication(qualifyingApplications),
+ checked = !hasHolderApplication(applicationItems),
onDefaultCheckChanged = { _ -> viewModel.setNoneDefaultApp() },
)
.apply { icon = context.getDrawable(R.drawable.ic_remove_circle) }
@@ -54,12 +53,12 @@ class WearDefaultAppHelper(
}
fun getPreferences(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
+ applicationItems: List<RoleApplicationItem>
): List<WearRoleApplicationPreference> {
- return qualifyingApplications
- .map { pair ->
- val appInfo = pair.first
- val selected = pair.second
+ return applicationItems
+ .map { applicationItem ->
+ val appInfo = applicationItem.applicationInfo
+ val selected = applicationItem.isHolderApplication
val user = UserHandle.getUserHandleForUid(appInfo.uid)
WearRoleApplicationPreference(
context = context,
@@ -127,7 +126,6 @@ class WearDefaultAppHelper(
fun getDescription() = context.getString(role.descriptionResource)
- private fun hasHolderApplication(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
- ): Boolean = qualifyingApplications.map { it.second }.contains(true)
+ private fun hasHolderApplication(applicationItems: List<RoleApplicationItem>): Boolean =
+ applicationItems.map { it.isHolderApplication }.contains(true)
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt
index 067c5b1be..c3cbe4670 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearDefaultAppScreen.kt
@@ -16,8 +16,6 @@
package com.android.permissioncontroller.role.ui.wear
-import android.content.pm.ApplicationInfo
-import android.util.Pair
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -25,6 +23,7 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
+import com.android.permissioncontroller.role.ui.RoleApplicationItem
import com.android.permissioncontroller.role.ui.wear.model.ConfirmDialogArgs
import com.android.permissioncontroller.wear.permission.components.ScrollableScreen
import com.android.permissioncontroller.wear.permission.components.material3.DialogButtonContent
@@ -39,7 +38,7 @@ import com.android.permissioncontroller.wear.permission.components.theme.WearPer
@Composable
fun WearDefaultAppScreen(helper: WearDefaultAppHelper) {
- val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
+ val roleLiveData = helper.viewModel.liveData.observeAsState(emptyList())
val showConfirmDialog =
helper.confirmDialogViewModel.showConfirmDialogLiveData.observeAsState(false)
var isLoading by remember { mutableStateOf(true) }
@@ -60,11 +59,11 @@ fun WearDefaultAppScreen(helper: WearDefaultAppHelper) {
@Composable
private fun WearDefaultAppContent(
isLoading: Boolean,
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ applicationItems: List<RoleApplicationItem>,
helper: WearDefaultAppHelper,
) {
ScrollableScreen(title = helper.getTitle(), isLoading = isLoading) {
- helper.getNonePreference(qualifyingApplications)?.let {
+ helper.getNonePreference(applicationItems)?.let {
item {
WearPermissionToggleControl(
label = it.title.toString(),
@@ -76,7 +75,7 @@ private fun WearDefaultAppContent(
)
}
}
- for (pref in helper.getPreferences(qualifyingApplications)) {
+ for (pref in helper.getPreferences(applicationItems)) {
item {
WearPermissionToggleControl(
label = pref.title.toString(),
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
index af8dc5e92..99f2fe36f 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleFragment.kt
@@ -231,11 +231,11 @@ class WearRequestRoleFragment : Fragment() {
if (userPackage == null) {
return -1
}
- viewModel.roleLiveData.value?.let { qualifyingApplications ->
- for (qualifyingApplication in qualifyingApplications) {
- val qualifyingApplicationInfo = qualifyingApplication.first
- val qualifyingAppUserPackage = UserPackage.from(qualifyingApplicationInfo)
- if (Objects.equals(qualifyingAppUserPackage, userPackage)) {
+ viewModel.liveData.value?.let { applicationItems ->
+ for (applicationItem in applicationItems) {
+ val qualifyingApplicationInfo = applicationItem.applicationInfo
+ val qualifyingUserPackage = UserPackage.from(qualifyingApplicationInfo)
+ if (Objects.equals(qualifyingUserPackage, userPackage)) {
return qualifyingApplicationInfo.uid
}
}
@@ -244,12 +244,10 @@ class WearRequestRoleFragment : Fragment() {
}
private fun getHolderUserPackage(): UserPackage? {
- viewModel.roleLiveData.value?.let { qualifyingApplications ->
+ viewModel.liveData.value?.let { qualifyingApplications ->
for (qualifyingApplication in qualifyingApplications) {
- val isHolderApplication = qualifyingApplication.second
- if (isHolderApplication) {
- val applicationInfo = qualifyingApplication.first
- return UserPackage.from(applicationInfo)
+ if (qualifyingApplication.isHolderApplication) {
+ return UserPackage.from(qualifyingApplication.applicationInfo)
}
}
}
@@ -257,7 +255,7 @@ class WearRequestRoleFragment : Fragment() {
}
private fun getQualifyingApplicationCount(): Int {
- return viewModel.roleLiveData.value?.size ?: -1
+ return viewModel.liveData.value?.size ?: -1
}
private fun setDeniedAlwaysAndFinish() {
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
index f8a1b1ee8..f95f1ee90 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleHelper.kt
@@ -20,12 +20,12 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import android.graphics.drawable.Drawable
import android.os.Process
-import android.util.Pair
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.utils.Utils
import com.android.permissioncontroller.role.UserPackage
import com.android.permissioncontroller.role.model.UserDeniedManager
import com.android.permissioncontroller.role.ui.RequestRoleViewModel
+import com.android.permissioncontroller.role.ui.RoleApplicationItem
import com.android.permissioncontroller.role.ui.wear.model.WearRequestRoleViewModel
import com.android.role.controller.model.Role
import java.util.Objects
@@ -50,11 +50,11 @@ class WearRequestRoleHelper(
UserDeniedManager.getInstance(context).isDeniedOnce(roleName, packageName)
fun getNonePreference(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ applicationItems: List<RoleApplicationItem>,
selectedPackage: UserPackage?,
): RequestRolePreference? =
if (role.shouldShowNone()) {
- val hasHolderApplication = hasHolderApplication(qualifyingApplications)
+ val hasHolderApplication = hasHolderApplication(applicationItems)
RequestRolePreference(
userPackage = null,
label = context.getString(R.string.default_app_none),
@@ -79,48 +79,47 @@ class WearRequestRoleHelper(
}
fun getPreferences(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ applicationItems: List<RoleApplicationItem>,
selectedPackage: UserPackage?,
): List<RequestRolePreference> {
- return qualifyingApplications
- .map { qualifyingApplication ->
- val userPackage = UserPackage.from(qualifyingApplication.first)
+ return applicationItems
+ .map { applicationItem ->
+ val userPackage = UserPackage.from(applicationItem.applicationInfo)
RequestRolePreference(
userPackage = userPackage,
- label = Utils.getAppLabel(qualifyingApplication.first, context),
+ label = Utils.getAppLabel(applicationItem.applicationInfo, context),
subTitle =
- if (qualifyingApplication.second) {
+ if (applicationItem.isHolderApplication) {
context.getString(R.string.request_role_current_default)
} else {
context.getString(role.requestDescriptionResource)
},
- icon = Utils.getBadgedIcon(context, qualifyingApplication.first),
+ icon = Utils.getBadgedIcon(context, applicationItem.applicationInfo),
checked = Objects.equals(userPackage, selectedPackage),
enabled =
if (!wearViewModel.dontAskAgain()) {
true
} else {
- qualifyingApplication.second
+ applicationItem.isHolderApplication
},
- isHolder = qualifyingApplication.second,
+ isHolder = applicationItem.isHolderApplication,
)
}
.toList()
}
- private fun hasHolderApplication(
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>
- ): Boolean = qualifyingApplications.map { it.second }.contains(true)
+ private fun hasHolderApplication(applicationItems: List<RoleApplicationItem>): Boolean =
+ applicationItems.map { it.isHolderApplication }.contains(true)
fun shouldSetAsDefaultEnabled(enabled: Boolean): Boolean {
return enabled && (wearViewModel.dontAskAgain() || !wearViewModel.isHolderChecked)
}
- fun initializeHolderPackage(qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>) {
+ fun initializeHolderPackage(applicationItems: List<RoleApplicationItem>) {
wearViewModel.holderPackage =
- qualifyingApplications
- .find { it.second }
- ?.first
+ applicationItems
+ .find { it.isHolderApplication }
+ ?.applicationInfo
?.let { appInfo -> UserPackage.from(appInfo) }
}
diff --git a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
index 5244e1a63..1367f4c3c 100644
--- a/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
+++ b/PermissionController/src/com/android/permissioncontroller/role/ui/wear/WearRequestRoleScreen.kt
@@ -16,8 +16,6 @@
package com.android.permissioncontroller.role.ui.wear
-import android.content.pm.ApplicationInfo
-import android.util.Pair
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
@@ -33,6 +31,7 @@ import androidx.compose.ui.unit.dp
import com.android.permissioncontroller.R
import com.android.permissioncontroller.role.UserPackage
import com.android.permissioncontroller.role.ui.ManageRoleHolderStateLiveData
+import com.android.permissioncontroller.role.ui.RoleApplicationItem
import com.android.permissioncontroller.wear.permission.components.ScrollableScreen
import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionButton
import com.android.permissioncontroller.wear.permission.components.material3.WearPermissionButtonStyle
@@ -50,7 +49,7 @@ fun WearRequestRoleScreen(
onSetAsDefault: (Boolean, UserPackage?) -> Unit,
onCanceled: () -> Unit,
) {
- val roleLiveData = helper.viewModel.roleLiveData.observeAsState(emptyList())
+ val roleLiveData = helper.viewModel.liveData.observeAsState(emptyList())
val manageRoleHolderState =
helper.viewModel.manageRoleHolderStateLiveData.observeAsState(
ManageRoleHolderStateLiveData.STATE_WORKING
@@ -102,7 +101,7 @@ internal fun WearRequestRoleContent(
materialUIVersion: WearPermissionMaterialUIVersion,
isLoading: Boolean,
helper: WearRequestRoleHelper,
- qualifyingApplications: List<Pair<ApplicationInfo, Boolean>>,
+ applicationItems: List<RoleApplicationItem>,
enabled: Boolean,
dontAskAgain: Boolean,
selectedPackage: UserPackage?,
@@ -118,7 +117,7 @@ internal fun WearRequestRoleContent(
showTimeText = false,
isLoading = isLoading,
) {
- helper.getNonePreference(qualifyingApplications, selectedPackage)?.let { pref ->
+ helper.getNonePreference(applicationItems, selectedPackage)?.let { pref ->
item {
WearPermissionToggleControl(
materialUIVersion = materialUIVersion,
@@ -143,7 +142,7 @@ internal fun WearRequestRoleContent(
}
}
- for (pref in helper.getPreferences(qualifyingApplications, selectedPackage)) {
+ for (pref in helper.getPreferences(applicationItems, selectedPackage)) {
item {
WearPermissionToggleControl(
materialUIVersion = materialUIVersion,