Adds summary helper for apps subtitle

The subtitle for the apps page says which apps (up to two/three) and how
many (if there are more than three) are allowed to bypass dnd under the
main "Apps" page.

Bug: 308819928
Test: atest ZenModesSummaryHelperTest
Flag: android.app.modes_ui
Change-Id: I15696384c392ba3f054948db50eea614f91d8c48
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6d84965..457e075 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -9211,6 +9211,16 @@
     <string name="zen_mode_apps_none_apps">None</string>
     <!-- [CHAR LIMIT=60] Zen mode settings: all apps will be able to bypass dnd -->
     <string name="zen_mode_apps_all_apps">All</string>
+    <!-- [CHAR LIMIT=NONE] Zen mode settings: Lists apps that can bypass DND. For example, "Nest, Messages, and 2 more can interrupt". -->
+    <string name="zen_mode_apps_subtext">
+        {count, plural, offset:2
+            =0    {No apps can interrupt}
+            =1    {{app_1} can interrupt}
+            =2    {{app_1} and {app_2} can interrupt}
+            =3    {{app_1}, {app_2}, and {app_3} can interrupt}
+            other {{app_1}, {app_2}, and # more can interrupt}
+        }
+    </string>
 
     <!-- [CHAR LIMIT=100] Zen mode settings: Allow apps to bypass DND -->
     <string name="zen_mode_bypassing_apps">Allow apps to override</string>
diff --git a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
index b4075cd..77f364c 100644
--- a/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
+++ b/src/com/android/settings/notification/modes/ZenModeSummaryHelper.java
@@ -43,13 +43,18 @@
 import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenPolicy;
 
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
 import com.android.settings.R;
 
 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 {
@@ -397,12 +402,13 @@
     }
 
     /**
-     * Generates a summary to display under the top level "Apps" preference for a mode.
+     * Generates a summary to display under the top level "Apps" preference for a mode, based
+     * on the given mode and provided set of apps.
      */
-    public String getAppsSummary(ZenMode zenMode) {
-        // TODO: b/308819928 - Set summary using priority app list if Selected Apps Chosen.
+    public @NonNull String getAppsSummary(@NonNull ZenMode zenMode,
+            @Nullable Set<String> appsBypassing) {
         if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) {
-            return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
+            return formatAppsList(appsBypassing);
         } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
             return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
         } else if (zenMode.getPolicy().getAllowedChannels() == ZenMode.CHANNEL_POLICY_ALL) {
@@ -410,4 +416,35 @@
         }
         return "";
     }
+
+    /**
+     * 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.
+     */
+    public @NonNull String formatAppsList(@Nullable Set<String> 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);
+        MessageFormat msgFormat = new MessageFormat(
+                mContext.getString(R.string.zen_mode_apps_subtext),
+                Locale.getDefault());
+        Map<String, Object> 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]);
+                }
+            }
+        }
+        return msgFormat.format(args);
+    }
 }
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 d8c8bf0..ef8290a 100644
--- a/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
+++ b/tests/robotests/src/com/android/settings/notification/modes/ZenModesSummaryHelperTest.java
@@ -38,6 +38,9 @@
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
 
+import java.util.LinkedHashSet;
+import java.util.Set;
+
 @RunWith(RobolectricTestRunner.class)
 public class ZenModesSummaryHelperTest {
     private Context mContext;
@@ -339,7 +342,7 @@
                 .build();
         ZenMode zenMode = new ZenMode("id", rule, true);
 
-        assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("All");
+        assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("All");
     }
 
     @Test
@@ -353,7 +356,56 @@
                 .build();
         ZenMode zenMode = new ZenMode("id", rule, true);
 
-        assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("None");
+        assertThat(mSummaryHelper.getAppsSummary(zenMode, new LinkedHashSet<>())).isEqualTo("None");
+    }
+
+    @Test
+    public void getAppsSummary_priorityAppsNoList() {
+        AutomaticZenRule rule = new AutomaticZenRule.Builder("Bedtime", Uri.parse("bed"))
+                .setType(AutomaticZenRule.TYPE_BEDTIME)
+                .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
+                .setZenPolicy(new ZenPolicy.Builder()
+                        .allowChannels(ZenPolicy.CHANNEL_POLICY_PRIORITY)
+                        .build())
+                .build();
+        ZenMode zenMode = new ZenMode("id", rule, true);
+
+        assertThat(mSummaryHelper.getAppsSummary(zenMode, null)).isEqualTo("Selected apps");
+    }
+
+    @Test
+    public void getAppsSummary_formatAppsListEmpty() {
+        Set<String> apps = new LinkedHashSet<>();
+        assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("No apps can interrupt");
+    }
+
+    @Test
+    public void getAppsSummary_formatAppsListSingle() {
+        Set<String> apps = Set.of("My App");
+        assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App can interrupt");
+    }
+
+    @Test
+    public void getAppsSummary_formatAppsListTwo() {
+        Set<String> apps = Set.of("My App", "SecondApp");
+        assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App and SecondApp "
+                + "can interrupt");
+    }
+
+    @Test
+    public void getAppsSummary_formatAppsListThree() {
+        Set<String> apps = Set.of("My App", "SecondApp", "ThirdApp");
+        assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("My App, SecondApp, "
+                + "and ThirdApp can interrupt");
+    }
+
+    @Test
+    public void getAppsSummary_formatAppsListMany() {
+        Set<String> apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp",
+                "FifthApp", "SixthApp");
+        // Note that apps are selected alphabetically.
+        assertThat(mSummaryHelper.formatAppsList(apps)).isEqualTo("FifthApp, FourthApp, "
+                + "and 4 more can interrupt");
     }
 
     @Test
@@ -366,7 +418,11 @@
                         .build())
                 .build();
         ZenMode zenMode = new ZenMode("id", rule, true);
+        Set<String> apps = Set.of("My App", "SecondApp", "ThirdApp", "FourthApp",
+                "FifthApp", "SixthApp");
 
-        assertThat(mSummaryHelper.getAppsSummary(zenMode)).isEqualTo("Selected apps");
+        assertThat(mSummaryHelper.getAppsSummary(zenMode, apps)).isEqualTo("FifthApp, FourthApp, "
+                + "and 4 more can interrupt");
     }
+
 }