From da3ae255e3acbd53837721bc5b787042ff7740a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C3=ADas=20Hern=C3=A1ndez?= Date: Mon, 15 Jul 2024 20:38:51 +0200 Subject: [PATCH] Distinguish the profile of DND-bypassing apps Bug: 353273192 Test: atest ZenModeAppsLinkPreferenceControllerTest ZenModesSummaryHelperTest Flag: android.app.modes_ui Change-Id: I31d8836dbba38d04c9ab3b3180025cb8ddcfbe6d --- res/values/strings.xml | 2 + .../ZenModeAppsLinkPreferenceController.java | 73 +++++++++------- .../notification/modes/ZenModeFragment.java | 6 +- .../modes/ZenModeSummaryHelper.java | 41 +++++---- ...nModeAppsLinkPreferenceControllerTest.java | 73 +++++++++++++--- .../modes/ZenModesSummaryHelperTest.java | 85 ++++++++++++++----- 6 files changed, 194 insertions(+), 86 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 3063f777449..f1989ec7198 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9357,6 +9357,8 @@ other {{app_1}, {app_2}, and # more can interrupt} } + + %s (Work) Calculating\u2026 diff --git a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java index 810b1b5efcb..9c3f267e188 100644 --- a/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java +++ b/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceController.java @@ -19,28 +19,32 @@ package com.android.settings.notification.modes; import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL; import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; +import android.app.Application; import android.content.Context; import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; import android.text.TextUtils; -import android.util.ArraySet; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; -import androidx.core.text.BidiFormatter; import androidx.fragment.app.Fragment; import androidx.preference.Preference; import com.android.settings.R; import com.android.settings.core.SubSettingLauncher; import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.notification.modes.ZenMode; import com.android.settingslib.notification.modes.ZenModesBackend; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Multimap; + import java.util.ArrayList; -import java.util.HashMap; +import java.util.Comparator; import java.util.List; -import java.util.Map; -import java.util.Set; /** * Preference with a link and summary about what apps can break through the mode @@ -51,12 +55,21 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr private final ZenModeSummaryHelper mSummaryHelper; private final ApplicationsState mApplicationsState; + private final UserManager mUserManager; private ApplicationsState.Session mAppSession; private final ZenHelperBackend mHelperBackend; private ZenMode mZenMode; private Preference mPreference; private final Fragment mHost; + ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, + ZenModesBackend backend, ZenHelperBackend helperBackend) { + this(context, key, host, + ApplicationsState.getInstance((Application) context.getApplicationContext()), + backend, helperBackend); + } + + @VisibleForTesting ZenModeAppsLinkPreferenceController(Context context, String key, Fragment host, ApplicationsState applicationsState, ZenModesBackend backend, ZenHelperBackend helperBackend) { @@ -64,6 +77,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr mSummaryHelper = new ZenModeSummaryHelper(mContext, helperBackend); mHelperBackend = helperBackend; mApplicationsState = applicationsState; + mUserManager = context.getSystemService(UserManager.class); mHost = host; } @@ -90,10 +104,10 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr if (mApplicationsState != null && mHost != null) { mAppSession = mApplicationsState.newSession(mAppSessionCallbacks, mHost.getLifecycle()); } - triggerUpdateAppsBypassingDndSummaryText(); + triggerUpdateAppsBypassingDnd(); } - private void triggerUpdateAppsBypassingDndSummaryText() { + private void triggerUpdateAppsBypassingDnd() { if (mAppSession == null) { return; } @@ -108,31 +122,28 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr mAppSession.rebuild(filter, ApplicationsState.ALPHA_COMPARATOR, false); } - private void updateAppsBypassingDndSummaryText(List apps) { - Set appNames = getAppsBypassingDnd(apps); - mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, appNames)); + private void displayAppsBypassingDnd(List allApps) { + ImmutableList apps = getAppsBypassingDndSortedByName(allApps); + + mPreference.setSummary(mSummaryHelper.getAppsSummary(mZenMode, apps)); } @VisibleForTesting - ArraySet getAppsBypassingDnd(@NonNull List apps) { - ArraySet appsBypassingDnd = new ArraySet<>(); + ImmutableList getAppsBypassingDndSortedByName(@NonNull List allApps) { + Multimap packagesBypassingDnd = HashMultimap.create(); + for (UserHandle userHandle : mUserManager.getUserProfiles()) { + packagesBypassingDnd.putAll(userHandle.getIdentifier(), + mHelperBackend.getPackagesBypassingDnd(userHandle.getIdentifier(), + /* includeConversationChannels= */ false)); + } - Map pkgLabelMap = new HashMap(); - for (ApplicationsState.AppEntry entry : apps) { - if (entry.info != null) { - pkgLabelMap.put(entry.info.packageName, entry.label); - } - } - for (String pkg : mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), - /* includeConversationChannels= */ false)) { - // Settings may hide some packages from the user, so if they're not present here - // we skip displaying them, even if they bypass dnd. - if (pkgLabelMap.get(pkg) == null) { - continue; - } - appsBypassingDnd.add(BidiFormatter.getInstance().unicodeWrap(pkgLabelMap.get(pkg))); - } - return appsBypassingDnd; + return ImmutableList.copyOf( + allApps.stream() + .filter(app -> packagesBypassingDnd.containsEntry( + UserHandle.getUserId(app.info.uid), app.info.packageName)) + .sorted(Comparator.comparing((AppEntry app) -> app.label) + .thenComparing(app -> UserHandle.getUserId(app.info.uid))) + .toList()); } @VisibleForTesting @@ -145,12 +156,12 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr @Override public void onPackageListChanged() { - triggerUpdateAppsBypassingDndSummaryText(); + triggerUpdateAppsBypassingDnd(); } @Override public void onRebuildComplete(ArrayList apps) { - updateAppsBypassingDndSummaryText(apps); + displayAppsBypassingDnd(apps); } @Override @@ -171,7 +182,7 @@ class ZenModeAppsLinkPreferenceController extends AbstractZenModePreferenceContr @Override public void onLoadEntriesCompleted() { - triggerUpdateAppsBypassingDndSummaryText(); + triggerUpdateAppsBypassingDnd(); } }; } diff --git a/src/com/android/settings/notification/modes/ZenModeFragment.java b/src/com/android/settings/notification/modes/ZenModeFragment.java index c5b52a9627d..7d7631bef02 100644 --- a/src/com/android/settings/notification/modes/ZenModeFragment.java +++ b/src/com/android/settings/notification/modes/ZenModeFragment.java @@ -17,7 +17,6 @@ package com.android.settings.notification.modes; import android.app.AlertDialog; -import android.app.Application; import android.app.settings.SettingsEnums; import android.content.Context; import android.view.Menu; @@ -29,7 +28,6 @@ import androidx.annotation.NonNull; import androidx.core.view.MenuProvider; import com.android.settings.R; -import com.android.settingslib.applications.ApplicationsState; import com.android.settingslib.core.AbstractPreferenceController; import com.android.settingslib.notification.modes.ZenMode; @@ -58,9 +56,7 @@ public class ZenModeFragment extends ZenModeFragmentBase { prefControllers.add(new ZenModePeopleLinkPreferenceController( context, "zen_mode_people", mHelperBackend)); prefControllers.add(new ZenModeAppsLinkPreferenceController( - context, "zen_mode_apps", this, - ApplicationsState.getInstance((Application) context.getApplicationContext()), - mBackend, mHelperBackend)); + context, "zen_mode_apps", this, mBackend, mHelperBackend)); prefControllers.add(new ZenModeOtherLinkPreferenceController( context, "zen_other_settings", mHelperBackend)); prefControllers.add(new ZenModeDisplayLinkPreferenceController( diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java index 00d0eeff63a..26de9eedf33 100644 --- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java +++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java @@ -49,17 +49,18 @@ import android.util.ArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.core.text.BidiFormatter; import com.android.settings.R; +import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.notification.modes.ZenMode; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Set; import java.util.function.Predicate; class ZenModeSummaryHelper { @@ -412,7 +413,7 @@ class ZenModeSummaryHelper { * on the given mode and provided set of apps. */ public @NonNull String getAppsSummary(@NonNull ZenMode zenMode, - @Nullable Set appsBypassing) { + @Nullable List appsBypassing) { if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) { return formatAppsList(appsBypassing); } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) { @@ -424,28 +425,34 @@ class ZenModeSummaryHelper { /** * Generates a formatted string declaring which apps can interrupt in the style of * "App, App2, and 4 more can interrupt." - * Apps selected for explicit mention are selected in order from the provided set sorted - * alphabetically. + * Apps selected for explicit mention are picked in order from the provided list. */ - public @NonNull String formatAppsList(@Nullable Set appsBypassingDnd) { + @VisibleForTesting + public @NonNull String formatAppsList(@Nullable List appsBypassingDnd) { if (appsBypassingDnd == null) { return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps); } - final int numAppsBypassingDnd = appsBypassingDnd.size(); - String[] appsBypassingDndArr = appsBypassingDnd.toArray(new String[numAppsBypassingDnd]); - // Sorts the provided apps alphabetically. - Arrays.sort(appsBypassingDndArr); + List appNames = appsBypassingDnd.stream().limit(3) + .map(app -> { + String appName = BidiFormatter.getInstance().unicodeWrap(app.label); + if (app.isManagedProfile()) { + appName = mContext.getString(R.string.zen_mode_apps_work_app, appName); + } + return appName; + }) + .toList(); + MessageFormat msgFormat = new MessageFormat( mContext.getString(R.string.zen_mode_apps_subtext), Locale.getDefault()); Map args = new HashMap<>(); - args.put("count", numAppsBypassingDnd); - if (numAppsBypassingDnd >= 1) { - args.put("app_1", appsBypassingDndArr[0]); - if (numAppsBypassingDnd >= 2) { - args.put("app_2", appsBypassingDndArr[1]); - if (numAppsBypassingDnd == 3) { - args.put("app_3", appsBypassingDndArr[2]); + args.put("count", appsBypassingDnd.size()); + if (appNames.size() >= 1) { + args.put("app_1", appNames.get(0)); + if (appNames.size() >= 2) { + args.put("app_2", appNames.get(1)); + if (appNames.size() == 3) { + args.put("app_3", appNames.get(2)); } } } diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java index 1faba309e7e..6d12594d1c1 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModeAppsLinkPreferenceControllerTest.java @@ -22,17 +22,22 @@ import static android.provider.Settings.EXTRA_AUTOMATIC_ZEN_RULE_ID; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.robolectric.Shadows.shadowOf; import android.app.Flags; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; +import android.content.pm.UserInfo; import android.os.Bundle; +import android.os.UserHandle; +import android.os.UserManager; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.ZenPolicy; @@ -41,6 +46,7 @@ import androidx.fragment.app.Fragment; import com.android.settings.SettingsActivity; import com.android.settingslib.applications.ApplicationsState; +import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; import com.android.settingslib.notification.modes.TestModeBuilder; import com.android.settingslib.notification.modes.ZenMode; @@ -58,6 +64,7 @@ import org.robolectric.RuntimeEnvironment; import java.util.ArrayList; import java.util.List; +import java.util.Random; @RunWith(RobolectricTestRunner.class) @EnableFlags(Flags.FLAG_MODES_UI) @@ -90,13 +97,13 @@ public final class ZenModeAppsLinkPreferenceControllerTest { mZenModesBackend, mHelperBackend); } - private ApplicationsState.AppEntry createAppEntry(String packageName, String label) { - ApplicationsState.AppEntry entry = mock(ApplicationsState.AppEntry.class); - entry.info = new ApplicationInfo(); - entry.info.packageName = packageName; - entry.label = label; - entry.info.uid = 0; - return entry; + private AppEntry createAppEntry(String packageName, int userId) { + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.packageName = packageName; + applicationInfo.uid = UserHandle.getUid(userId, new Random().nextInt(100)); + AppEntry appEntry = new AppEntry(mContext, applicationInfo, 1); + appEntry.label = packageName; + return appEntry; } private ZenMode createPriorityChannelsZenMode() { @@ -137,14 +144,52 @@ public final class ZenModeAppsLinkPreferenceControllerTest { @Test public void testGetAppsBypassingDnd() { - ApplicationsState.AppEntry entry = createAppEntry("test", "testLabel"); - ApplicationsState.AppEntry entryConv = createAppEntry("test_conv", "test_convLabel"); - List appEntries = List.of(entry, entryConv); + ApplicationsState.AppEntry app1 = createAppEntry("app1", mContext.getUserId()); + ApplicationsState.AppEntry app2 = createAppEntry("app2", mContext.getUserId()); + List allApps = List.of(app1, app2); when(mHelperBackend.getPackagesBypassingDnd(mContext.getUserId(), - false)).thenReturn(List.of("test")); + false)).thenReturn(List.of("app1")); - assertThat(mController.getAppsBypassingDnd(appEntries)).containsExactly("testLabel"); + assertThat(mController.getAppsBypassingDndSortedByName(allApps)).containsExactly(app1); + } + + @Test + public void testGetAppsBypassingDnd_sortsByName() { + ApplicationsState.AppEntry appC = createAppEntry("C", mContext.getUserId()); + ApplicationsState.AppEntry appA = createAppEntry("A", mContext.getUserId()); + ApplicationsState.AppEntry appB = createAppEntry("B", mContext.getUserId()); + List allApps = List.of(appC, appA, appB); + + when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) + .thenReturn(List.of("B", "C", "A")); + + assertThat(mController.getAppsBypassingDndSortedByName(allApps)) + .containsExactly(appA, appB, appC).inOrder(); + } + + @Test + public void testGetAppsBypassingDnd_withWorkProfile_includesProfileAndSorts() { + UserInfo workProfile = new UserInfo(10, "Work Profile", 0); + workProfile.userType = UserManager.USER_TYPE_PROFILE_MANAGED; + UserManager userManager = mContext.getSystemService(UserManager.class); + shadowOf(userManager).addProfile(mContext.getUserId(), 10, workProfile); + + ApplicationsState.AppEntry personalCopy = createAppEntry("app", mContext.getUserId()); + ApplicationsState.AppEntry workCopy = createAppEntry("app", 10); + ApplicationsState.AppEntry otherPersonal = createAppEntry("p2", mContext.getUserId()); + ApplicationsState.AppEntry otherWork = createAppEntry("w2", 10); + List allApps = List.of(workCopy, personalCopy, otherPersonal, + otherWork); + + when(mHelperBackend.getPackagesBypassingDnd(eq(mContext.getUserId()), anyBoolean())) + .thenReturn(List.of("app", "p2")); + when(mHelperBackend.getPackagesBypassingDnd(eq(10), anyBoolean())) + .thenReturn(List.of("app")); + + // Personal copy before work copy (names match). + assertThat(mController.getAppsBypassingDndSortedByName(allApps)) + .containsExactly(personalCopy, workCopy, otherPersonal).inOrder(); } @Test @@ -156,7 +201,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest { // Create some applications. ArrayList appEntries = new ArrayList<>(); - appEntries.add(createAppEntry("test", "pkgLabel")); + appEntries.add(createAppEntry("test", mContext.getUserId())); when(mHelperBackend.getPackagesBypassingDnd( mContext.getUserId(), false)) @@ -170,7 +215,7 @@ public final class ZenModeAppsLinkPreferenceControllerTest { // Manually triggers the callback that will happen on rebuild. mController.mAppSessionCallbacks.onRebuildComplete(appEntries); - assertThat(String.valueOf(preference.getSummary())).isEqualTo("pkgLabel can interrupt"); + assertThat(String.valueOf(preference.getSummary())).isEqualTo("test can interrupt"); } @Test diff --git a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java index e847fb75233..672a0d74355 100644 --- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java +++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java @@ -28,10 +28,16 @@ import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; import static com.google.common.truth.Truth.assertThat; +import static org.robolectric.Shadows.shadowOf; + import android.app.Flags; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.UserInfo; import android.net.Uri; +import android.os.UserHandle; +import android.os.UserManager; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.service.notification.Condition; @@ -39,9 +45,12 @@ import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; +import com.android.settingslib.applications.ApplicationsState.AppEntry; import com.android.settingslib.notification.modes.TestModeBuilder; import com.android.settingslib.notification.modes.ZenMode; +import com.google.common.collect.ImmutableList; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -50,10 +59,12 @@ import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.Random; + @RunWith(RobolectricTestRunner.class) public class ZenModesSummaryHelperTest { + private static final int WORK_PROFILE_ID = 3; + private Context mContext; private ZenHelperBackend mBackend; @@ -69,6 +80,11 @@ public class ZenModesSummaryHelperTest { mContext = RuntimeEnvironment.application; mBackend = new ZenHelperBackend(mContext); mSummaryHelper = new ZenModeSummaryHelper(mContext, mBackend); + + UserInfo workProfile = new UserInfo(WORK_PROFILE_ID, "Work Profile", 0); + workProfile.userType = UserManager.USER_TYPE_PROFILE_MANAGED; + UserManager userManager = mContext.getSystemService(UserManager.class); + shadowOf(userManager).addProfile(mContext.getUserId(), WORK_PROFILE_ID, workProfile); } @Test @@ -306,7 +322,7 @@ public class ZenModesSummaryHelperTest { .build()) .build(); - assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("None"); + assertThat(mSummaryHelper.getAppsSummary(zenMode, ImmutableList.of())).isEqualTo("None"); } @Test @@ -321,40 +337,58 @@ public class ZenModesSummaryHelperTest { } @Test - public void getAppsSummary_formatAppsListEmpty() { - Set apps = new LinkedHashSet<>(); + public void formatAppsList_listEmpty() { + ImmutableList apps = ImmutableList.of(); assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("No apps can interrupt"); } @Test - public void getAppsSummary_formatAppsListSingle() { - Set apps = Set.of("My App"); + public void formatAppsList_single() { + ImmutableList apps = ImmutableList.of(newAppEntry("My App")); assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App can interrupt"); } @Test - public void getAppsSummary_formatAppsListTwo() { - Set apps = Set.of("My App", "SecondApp"); + public void formatAppsList_two() { + ImmutableList apps = ImmutableList.of(newAppEntry("My App"), + newAppEntry("SecondApp")); assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App and SecondApp " + "can interrupt"); } @Test - public void getAppsSummary_formatAppsListThree() { - Set apps = Set.of("My App", "SecondApp", "ThirdApp"); + public void formatAppsList_three() { + ImmutableList apps = ImmutableList.of(newAppEntry("My App"), + newAppEntry("SecondApp"), newAppEntry("ThirdApp")); assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App, SecondApp, " + "and ThirdApp can interrupt"); } @Test - public void getAppsSummary_formatAppsListMany() { - Set apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp", - "FifthApp", "SixthApp"); - // Note that apps are selected alphabetically. - assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("FifthApp, FourthApp, " + public void formatAppsList_many() { + ImmutableList apps = ImmutableList.of(newAppEntry("My App"), + newAppEntry("SecondApp"), newAppEntry("ThirdApp"), newAppEntry("FourthApp"), + newAppEntry("FifthApp"), newAppEntry("SixthApp")); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App, SecondApp, " + "and 4 more can interrupt"); } + @Test + public void formatAppsList_singleWorkProfile() { + ImmutableList apps = ImmutableList.of(newAppEntry("My App", WORK_PROFILE_ID)); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App (Work) can interrupt"); + } + + @Test + public void formatAppsList_mixOfProfiles() { + ImmutableList apps = ImmutableList.of( + newAppEntry("My App", mContext.getUserId()), + newAppEntry("My App", WORK_PROFILE_ID), + newAppEntry("SecondApp", mContext.getUserId())); + assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App, My App (Work), " + + "and SecondApp can interrupt"); + } + @Test public void getAppsSummary_priorityApps() { ZenMode zenMode = new TestModeBuilder() @@ -362,13 +396,26 @@ public class ZenModesSummaryHelperTest { .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY) .build()) .build(); - Set apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp", - "FifthApp", "SixthApp"); + ImmutableList apps = ImmutableList.of(newAppEntry("My App"), + newAppEntry("SecondApp"), newAppEntry("ThirdApp"), newAppEntry("FourthApp"), + newAppEntry("FifthApp"), newAppEntry("SixthApp")); - assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("FifthApp, FourthApp, " + assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("My App, SecondApp, " + "and 4 more can interrupt"); } + private AppEntry newAppEntry(String name) { + return newAppEntry(name, mContext.getUserId()); + } + + private AppEntry newAppEntry(String name, int userId) { + ApplicationInfo applicationInfo = new ApplicationInfo(); + applicationInfo.uid = UserHandle.getUid(userId, new Random().nextInt(100)); + AppEntry appEntry = new AppEntry(mContext, applicationInfo, 1); + appEntry.label = name; + return appEntry; + } + @Test @EnableFlags(Flags.FLAG_MODES_UI) public void getSoundSummary_off_noRules() {