From 043ba2889205a2d11e572348675b1f44241953cb Mon Sep 17 00:00:00 2001 From: Becca Hughes Date: Thu, 26 Jan 2023 20:09:55 +0000 Subject: [PATCH] Merge credential manager providers by package name **This is a merge to master from udc-dev** As part of the spec'd design we should merge providers by package name this means that if there are multiple providers they will be all turned on/off together. Bug: 266772233 Test: make & atest Change-Id: Id48f27e96cabdd8ab0e8cbafc8eb760b7bbbf928 --- ...CredentialManagerPreferenceController.java | 64 +++++++-- ...entialManagerPreferenceControllerTest.java | 124 ++++++++++++++++-- 2 files changed, 168 insertions(+), 20 deletions(-) diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java index 7916487e81b..a1c4f67a480 100644 --- a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java +++ b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java @@ -24,7 +24,9 @@ import android.app.Dialog; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.content.pm.ServiceInfo; import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.credentials.SetEnabledProvidersException; @@ -32,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.OutcomeReceiver; import android.os.UserHandle; +import android.text.TextUtils; import android.util.IconDrawableFactory; import android.util.Log; @@ -162,22 +165,64 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl PreferenceGroup group = screen.findPreference(getPreferenceKey()); Context context = screen.getContext(); + mPrefs.putAll(buildPreferenceList(context, group)); + } - for (CredentialProviderInfo service : mServices) { - group.addPreference(createPreference(context, service)); + /** Aggregates the list of services and builds a list of UI prefs to show. */ + @VisibleForTesting + public Map buildPreferenceList( + Context context, PreferenceGroup group) { + // Group the services by package name. + Map> groupedInfos = new HashMap<>(); + for (CredentialProviderInfo cpi : mServices) { + String packageName = cpi.getServiceInfo().packageName; + if (!groupedInfos.containsKey(packageName)) { + groupedInfos.put(packageName, new ArrayList<>()); + } + + groupedInfos.get(packageName).add(cpi); } + + // Build the pref list. + Map output = new HashMap<>(); + for (String packageName : groupedInfos.keySet()) { + List infos = groupedInfos.get(packageName); + SwitchPreference pref = createPreference(context, groupedInfos.get(packageName)); + if (pref != null) { + output.put(packageName, pref); + group.addPreference(pref); + } + } + + return output; } /** Creates a preference object based on the provider info. */ @VisibleForTesting - public SwitchPreference createPreference(Context context, CredentialProviderInfo service) { - CharSequence label = service.getLabel(context); + public @Nullable SwitchPreference createPreference( + Context context, List infos) { + final CredentialProviderInfo firstInfo = infos.get(0); + final ServiceInfo firstServiceInfo = firstInfo.getServiceInfo(); + final String packageName = firstServiceInfo.packageName; + CharSequence title = firstInfo.getLabel(context); + Drawable icon = firstInfo.getServiceIcon(context); + + if (infos.size() > 1) { + // If there is more than one then group them under the package. + ApplicationInfo appInfo = firstServiceInfo.applicationInfo; + if (appInfo.nonLocalizedLabel != null) { + title = appInfo.loadLabel(mPm); + } + icon = mIconFactory.getBadgedIcon(appInfo, getUser()); + } + + // If there is no title then don't show anything. + if (TextUtils.isEmpty(title)) { + return null; + } + return addProviderPreference( - context, - label == null ? "" : label, - service.getServiceIcon(mContext), - service.getServiceInfo().packageName, - service.getSettingsSubtitle()); + context, title, icon, packageName, firstInfo.getSettingsSubtitle()); } /** @@ -240,7 +285,6 @@ public class CredentialManagerPreferenceController extends BasePreferenceControl final SwitchPreference pref = new SwitchPreference(prefContext); pref.setTitle(title); pref.setChecked(mEnabledPackageNames.contains(packageName)); - mPrefs.put(packageName, pref); if (icon != null) { pref.setIcon(Utils.getSafeIcon(icon)); diff --git a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java index 88a53112e02..47de08300de 100644 --- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java @@ -44,8 +44,10 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; @RunWith(AndroidJUnit4.class) @@ -55,6 +57,13 @@ public class CredentialManagerPreferenceControllerTest { private PreferenceScreen mScreen; private PreferenceCategory mCredentialsPreferenceCategory; + private static final String TEST_PACKAGE_NAME_A = "com.android.providerA"; + private static final String TEST_PACKAGE_NAME_B = "com.android.providerB"; + private static final String TEST_PACKAGE_NAME_C = "com.android.providerC"; + private static final String TEST_TITLE_APP_A = "test app A"; + private static final String TEST_TITLE_APP_B = "test app B"; + private static final String TEST_TITLE_SERVICE_C = "test service C1"; + @Before public void setUp() { mContext = spy(ApplicationProvider.getApplicationContext()); @@ -114,11 +123,11 @@ public class CredentialManagerPreferenceControllerTest { @Test public void buildSwitchPreference() { CredentialProviderInfo providerInfo1 = - createCredentialProviderInfo( - "com.android.provider1", "ClassA", "Service Title", false, null); + createCredentialProviderInfoWithSettingsSubtitle( + "com.android.provider1", "ClassA", "Service Title", null); CredentialProviderInfo providerInfo2 = - createCredentialProviderInfo( - "com.android.provider2", "ClassA", "Service Title", false, "Summary Text"); + createCredentialProviderInfoWithSettingsSubtitle( + "com.android.provider2", "ClassA", "Service Title", "Summary Text"); CredentialManagerPreferenceController controller = createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2)); assertThat(controller.getAvailabilityStatus()).isEqualTo(AVAILABLE); @@ -136,13 +145,14 @@ public class CredentialManagerPreferenceControllerTest { assertThat(enabledProviders.contains("com.android.provider1")).isTrue(); // Create the pref (checked). - SwitchPreference pref = controller.createPreference(mContext, providerInfo1); + SwitchPreference pref = controller.createPreference(mContext, Arrays.asList(providerInfo1)); assertThat(pref.getTitle().toString()).isEqualTo("Service Title"); assertThat(pref.isChecked()).isTrue(); assertThat(pref.getSummary()).isNull(); // Create the pref (not checked). - SwitchPreference pref2 = controller.createPreference(mContext, providerInfo2); + SwitchPreference pref2 = + controller.createPreference(mContext, Arrays.asList(providerInfo2)); assertThat(pref2.getTitle().toString()).isEqualTo("Service Title"); assertThat(pref2.isChecked()).isFalse(); assertThat(pref2.getSummary().toString()).isEqualTo("Summary Text"); @@ -219,10 +229,10 @@ public class CredentialManagerPreferenceControllerTest { @Test public void handlesCredentialProviderInfoEnabledDisabled() { CredentialProviderInfo providerInfo1 = - createCredentialProviderInfo( + createCredentialProviderInfoWithIsEnabled( "com.android.provider1", "ClassA", "Service Title", false); CredentialProviderInfo providerInfo2 = - createCredentialProviderInfo( + createCredentialProviderInfoWithIsEnabled( "com.android.provider2", "ClassA", "Service Title", true); CredentialManagerPreferenceController controller = createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2)); @@ -246,6 +256,66 @@ public class CredentialManagerPreferenceControllerTest { assertThat(enabledServices.contains("com.android.provider2/ClassA")).isTrue(); } + @Test + public void displayPreference_withServices_preferencesAdded_sameAppShouldBeMerged() { + CredentialProviderInfo serviceA1 = + createCredentialProviderInfoWithAppLabel( + TEST_PACKAGE_NAME_A, + "CredManProviderA1", + TEST_TITLE_APP_A, + "test service A1"); + CredentialProviderInfo serviceB1 = + createCredentialProviderInfoWithAppLabel( + TEST_PACKAGE_NAME_B, + "CredManProviderB1", + TEST_TITLE_APP_B, + "test service B"); + CredentialProviderInfo serviceC1 = + createCredentialProviderInfoWithAppLabel( + TEST_PACKAGE_NAME_C, + "CredManProviderC1", + "test app C1", + TEST_TITLE_SERVICE_C); + CredentialProviderInfo serviceC2 = + createCredentialProviderInfoWithAppLabel( + TEST_PACKAGE_NAME_C, + "CredManProviderC2", + "test app C2", + TEST_TITLE_SERVICE_C); + CredentialProviderInfo serviceC3 = + createCredentialProviderInfoBuilder( + TEST_PACKAGE_NAME_C, + "CredManProviderC3", + "test app C3", + TEST_TITLE_SERVICE_C) + .setEnabled(true) + .build(); + + CredentialManagerPreferenceController controller = + createControllerWithServices( + Lists.newArrayList(serviceA1, serviceB1, serviceC1, serviceC2, serviceC3)); + controller.displayPreference(mScreen); + + assertThat(controller.isConnected()).isFalse(); + assertThat(mCredentialsPreferenceCategory.getPreferenceCount()).isEqualTo(3); + + Map prefs = + controller.buildPreferenceList(mContext, mCredentialsPreferenceCategory); + assertThat(prefs.size()).isEqualTo(3); + SwitchPreference pref1 = prefs.get(TEST_PACKAGE_NAME_C); + assertThat(pref1).isNotNull(); + assertThat(pref1.getTitle()).isEqualTo(TEST_TITLE_APP_A); + assertThat(pref1.isChecked()).isFalse(); + SwitchPreference pref2 = prefs.get(TEST_PACKAGE_NAME_C); + assertThat(pref2).isNotNull(); + assertThat(pref2.getTitle()).isEqualTo(TEST_TITLE_APP_B); + assertThat(pref2.isChecked()).isFalse(); + SwitchPreference pref3 = prefs.get(TEST_PACKAGE_NAME_C); + assertThat(pref3).isNotNull(); + assertThat(pref3.getTitle()).isEqualTo(TEST_TITLE_SERVICE_C); + assertThat(pref3.isChecked()).isTrue(); + } + private CredentialManagerPreferenceController createControllerWithServices( List availableServices) { CredentialManagerPreferenceController controller = @@ -261,7 +331,8 @@ public class CredentialManagerPreferenceControllerTest { private CredentialProviderInfo createCredentialProviderInfo( String packageName, String className) { - return createCredentialProviderInfo(packageName, className, null, false); + return createCredentialProviderInfoBuilder(packageName, className, null, "App Name") + .build(); } private CredentialProviderInfo createCredentialProviderInfo( @@ -286,8 +357,41 @@ public class CredentialManagerPreferenceControllerTest { return new CredentialProviderInfo.Builder(si) .setOverrideLabel(label) - .setEnabled(isEnabled) .setSettingsSubtitle(subtitle) .build(); } + + private CredentialProviderInfo createCredentialProviderInfoWithIsEnabled( + String packageName, String className, CharSequence serviceLabel, boolean isEnabled) { + return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, "App Name") + .setEnabled(isEnabled) + .build(); + } + + private CredentialProviderInfo createCredentialProviderInfoWithSettingsSubtitle( + String packageName, String className, CharSequence serviceLabel, String subtitle) { + return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, "App Name") + .setSettingsSubtitle(subtitle) + .build(); + } + + private CredentialProviderInfo createCredentialProviderInfoWithAppLabel( + String packageName, String className, CharSequence serviceLabel, String appLabel) { + return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, appLabel) + .build(); + } + + private CredentialProviderInfo.Builder createCredentialProviderInfoBuilder( + String packageName, String className, CharSequence serviceLabel, String appLabel) { + ServiceInfo si = new ServiceInfo(); + si.packageName = packageName; + si.name = className; + si.nonLocalizedLabel = serviceLabel; + + si.applicationInfo = new ApplicationInfo(); + si.applicationInfo.packageName = packageName; + si.applicationInfo.nonLocalizedLabel = appLabel; + + return new CredentialProviderInfo.Builder(si).setOverrideLabel(serviceLabel); + } }