diff --git a/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java b/src/com/android/settings/applications/credentials/CredentialManagerPreferenceController.java index f8c1f643a0c..8621e6f5f95 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,10 +165,54 @@ 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); + CredentialProviderInfo firstInfo = infos.get(0); + ServiceInfo firstServiceInfo = firstInfo.getServiceInfo(); + 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)) { + continue; + } + + // Build the pref and add it to the output & group. + SwitchPreference pref = addProviderPreference(context, title, icon, packageName); + output.put(packageName, pref); + group.addPreference(pref); + } + + return output; } /** Creates a preference object based on the provider info. */ @@ -238,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 2633ea7ad40..5f7f45bd068 100644 --- a/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java +++ b/tests/unit/src/com/android/settings/applications/credentials/CredentialManagerPreferenceControllerTest.java @@ -46,6 +46,7 @@ import org.junit.runner.RunWith; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; @RunWith(AndroidJUnit4.class) @@ -55,6 +56,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,10 +122,10 @@ public class CredentialManagerPreferenceControllerTest { @Test public void buildSwitchPreference() { CredentialProviderInfo providerInfo1 = - createCredentialProviderInfo( + createCredentialProviderInfoWithIsEnabled( "com.android.provider1", "ClassA", "Service Title", false); CredentialProviderInfo providerInfo2 = - createCredentialProviderInfo( + createCredentialProviderInfoWithIsEnabled( "com.android.provider2", "ClassA", "Service Title", false); CredentialManagerPreferenceController controller = createControllerWithServices(Lists.newArrayList(providerInfo1, providerInfo2)); @@ -217,10 +225,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)); @@ -244,6 +252,63 @@ 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); + assertThat(prefs.containsKey(TEST_PACKAGE_NAME_A)); + assertThat(prefs.get(TEST_PACKAGE_NAME_A).getTitle()).isEqualTo(TEST_TITLE_APP_A); + assertThat(prefs.get(TEST_PACKAGE_NAME_A).isChecked()).isFalse(); + assertThat(prefs.containsKey(TEST_PACKAGE_NAME_B)); + assertThat(prefs.get(TEST_PACKAGE_NAME_B).getTitle()).isEqualTo(TEST_TITLE_APP_B); + assertThat(prefs.get(TEST_PACKAGE_NAME_B).isChecked()).isFalse(); + assertThat(prefs.containsKey(TEST_PACKAGE_NAME_C)); + assertThat(prefs.get(TEST_PACKAGE_NAME_C).getTitle()).isEqualTo(TEST_TITLE_SERVICE_C); + assertThat(prefs.get(TEST_PACKAGE_NAME_C).isChecked()).isTrue(); + } + private CredentialManagerPreferenceController createControllerWithServices( List availableServices) { CredentialManagerPreferenceController controller = @@ -259,23 +324,34 @@ 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( - String packageName, String className, CharSequence label, boolean isEnabled) { - ServiceInfo si = new ServiceInfo(); - si.packageName = packageName; - si.name = className; - si.nonLocalizedLabel = "test"; - - si.applicationInfo = new ApplicationInfo(); - si.applicationInfo.packageName = packageName; - si.applicationInfo.nonLocalizedLabel = "test"; - - return new CredentialProviderInfo.Builder(si) - .setOverrideLabel(label) + private CredentialProviderInfo createCredentialProviderInfoWithIsEnabled( + String packageName, String className, CharSequence serviceLabel, boolean isEnabled) { + return createCredentialProviderInfoBuilder(packageName, className, serviceLabel, "App Name") .setEnabled(isEnabled) .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); + } }