• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.notification.modes;
17 
18 import static android.app.NotificationManager.INTERRUPTION_FILTER_ALL;
19 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_ANYONE;
20 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT;
21 import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE;
22 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE;
23 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS;
24 import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE;
25 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS;
26 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS;
27 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CONVERSATIONS;
28 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS;
29 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA;
30 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES;
31 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS;
32 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS;
33 import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM;
34 import static android.service.notification.ZenPolicy.STATE_ALLOW;
35 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT;
36 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE;
37 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT;
38 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS;
39 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST;
40 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK;
41 import static android.service.notification.ZenPolicy.VISUAL_EFFECT_STATUS_BAR;
42 
43 import android.content.Context;
44 import android.icu.text.MessageFormat;
45 import android.service.notification.ZenDeviceEffects;
46 import android.service.notification.ZenPolicy;
47 import android.service.notification.ZenPolicy.ConversationSenders;
48 import android.service.notification.ZenPolicy.PeopleType;
49 
50 import androidx.annotation.NonNull;
51 import androidx.annotation.Nullable;
52 import androidx.annotation.VisibleForTesting;
53 import androidx.core.text.BidiFormatter;
54 
55 import com.android.settings.R;
56 import com.android.settingslib.applications.ApplicationsState.AppEntry;
57 import com.android.settingslib.notification.modes.ZenMode;
58 
59 import com.google.common.base.Strings;
60 import com.google.common.collect.ImmutableList;
61 
62 import java.util.ArrayList;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.Locale;
66 import java.util.Map;
67 import java.util.function.Predicate;
68 
69 class ZenModeSummaryHelper {
70 
71     private final Context mContext;
72     private final ZenHelperBackend mBackend;
73 
ZenModeSummaryHelper(Context context, ZenHelperBackend backend)74     ZenModeSummaryHelper(Context context, ZenHelperBackend backend) {
75         mContext = context;
76         mBackend = backend;
77     }
78 
79     private static final int[] ALL_PRIORITY_CATEGORIES = {
80             PRIORITY_CATEGORY_ALARMS,
81             PRIORITY_CATEGORY_MEDIA,
82             PRIORITY_CATEGORY_SYSTEM,
83             PRIORITY_CATEGORY_MESSAGES,
84             PRIORITY_CATEGORY_CONVERSATIONS,
85             PRIORITY_CATEGORY_EVENTS,
86             PRIORITY_CATEGORY_REMINDERS,
87             PRIORITY_CATEGORY_CALLS,
88             PRIORITY_CATEGORY_REPEAT_CALLERS,
89     };
90 
91     static final ImmutableList</* @PriorityCategory */ Integer> OTHER_SOUND_CATEGORIES =
92             ImmutableList.of(
93                 PRIORITY_CATEGORY_ALARMS,
94                 PRIORITY_CATEGORY_MEDIA,
95                 PRIORITY_CATEGORY_SYSTEM,
96                 PRIORITY_CATEGORY_REMINDERS,
97                 PRIORITY_CATEGORY_EVENTS);
98 
getOtherSoundCategoriesSummary(ZenMode zenMode)99     String getOtherSoundCategoriesSummary(ZenMode zenMode) {
100         List<String> enabledCategories = getEnabledCategories(
101                 zenMode.getPolicy(),
102                 OTHER_SOUND_CATEGORIES::contains,
103                 true);
104         int numCategories = enabledCategories.size();
105         MessageFormat msgFormat = new MessageFormat(
106                 mContext.getString(R.string.zen_mode_other_sounds_summary),
107                 Locale.getDefault());
108         Map<String, Object> args = new HashMap<>();
109         args.put("count", numCategories);
110         if (numCategories >= 1) {
111             args.put("sound_category_1", enabledCategories.get(0));
112             if (numCategories >= 2) {
113                 args.put("sound_category_2", enabledCategories.get(1));
114                 if (numCategories == 3) {
115                     args.put("sound_category_3", enabledCategories.get(2));
116                 }
117             }
118         }
119         return msgFormat.format(args);
120     }
121 
getCallsSettingSummary(ZenMode zenMode)122     String getCallsSettingSummary(ZenMode zenMode) {
123         List<String> enabledCategories = getEnabledCategories(zenMode.getPolicy(),
124                 category -> PRIORITY_CATEGORY_CALLS == category
125                         || PRIORITY_CATEGORY_REPEAT_CALLERS == category, true);
126         int numCategories = enabledCategories.size();
127         if (numCategories == 0) {
128             return mContext.getString(R.string.zen_mode_none_calls);
129         } else if (numCategories == 1) {
130             return mContext.getString(R.string.zen_mode_calls_summary_one,
131                     enabledCategories.get(0));
132         } else {
133             return mContext.getString(R.string.zen_mode_calls_summary_two,
134                     enabledCategories.get(0),
135                     enabledCategories.get(1));
136         }
137     }
138 
getMessagesSettingSummary(ZenPolicy policy)139     String getMessagesSettingSummary(ZenPolicy policy) {
140         if (policy.getPriorityCategoryMessages() == STATE_ALLOW
141                 && policy.getPriorityMessageSenders() == PEOPLE_TYPE_ANYONE) {
142             // Messages=anyone means anyone. Even if conversation senders is specially configured,
143             // saying "Anyone and priority conversations" 1) makes no sense and 2) is incorrect
144             // because conversations WILL get through by virtue of also being messages.
145             return mContext.getString(R.string.zen_mode_from_anyone);
146         }
147 
148         List<String> enabledCategories = getEnabledCategories(policy,
149                 category -> PRIORITY_CATEGORY_MESSAGES == category
150                         || PRIORITY_CATEGORY_CONVERSATIONS == category, true);
151         int numCategories = enabledCategories.size();
152         if (numCategories == 0) {
153             return mContext.getString(R.string.zen_mode_none_messages);
154         } else if (numCategories == 1) {
155             return enabledCategories.get(0);
156         } else {
157             // While this string name seems like a slight misnomer: it's borrowing the analogous
158             // calls-summary functionality to combine two permissions.
159             return mContext.getString(R.string.zen_mode_calls_summary_two,
160                     enabledCategories.get(0),
161                     enabledCategories.get(1));
162         }
163     }
164 
getBlockedEffectsSummary(ZenMode zenMode)165     String getBlockedEffectsSummary(ZenMode zenMode) {
166         List<Integer> relevantVisualEffects = new ArrayList<>();
167         relevantVisualEffects.add(VISUAL_EFFECT_FULL_SCREEN_INTENT);
168         relevantVisualEffects.add(VISUAL_EFFECT_PEEK);
169         relevantVisualEffects.add(VISUAL_EFFECT_STATUS_BAR);
170         relevantVisualEffects.add(VISUAL_EFFECT_BADGE);
171         relevantVisualEffects.add(VISUAL_EFFECT_AMBIENT);
172         relevantVisualEffects.add(VISUAL_EFFECT_NOTIFICATION_LIST);
173         if (mContext.getResources()
174                 .getBoolean(com.android.internal.R.bool.config_intrusiveNotificationLed)) {
175             relevantVisualEffects.add(VISUAL_EFFECT_LIGHTS);
176         }
177 
178         if (shouldShowAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
179             return mContext.getResources().getString(
180                     R.string.zen_mode_restrict_notifications_summary_muted);
181         } else if (shouldHideAllVisualEffects(zenMode.getPolicy(), relevantVisualEffects)) {
182             return mContext.getResources().getString(
183                     R.string.zen_mode_restrict_notifications_summary_hidden);
184         } else {
185             return mContext.getResources().getString(
186                     R.string.zen_mode_restrict_notifications_summary_custom);
187         }
188     }
189 
shouldShowAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects)190     private boolean shouldShowAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects) {
191         for (int i = 0; i < relevantEffects.size(); i++) {
192             if (!policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
193                 return false;
194             }
195         }
196         return true;
197     }
198 
shouldHideAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects)199     private boolean shouldHideAllVisualEffects(ZenPolicy policy, List<Integer> relevantEffects) {
200         for (int i = 0; i < relevantEffects.size(); i++) {
201             if (policy.isVisualEffectAllowed(relevantEffects.get(i), false)) {
202                 return false;
203             }
204         }
205         return true;
206     }
207 
getDisplayEffectsSummary(ZenMode zenMode)208     String getDisplayEffectsSummary(ZenMode zenMode) {
209         boolean isFirst = true;
210         List<String> enabledEffects = new ArrayList<>();
211         if (!zenMode.getPolicy().shouldShowAllVisualEffects()
212                 && zenMode.getInterruptionFilter() != INTERRUPTION_FILTER_ALL) {
213             enabledEffects.add(getBlockedEffectsSummary(zenMode));
214             isFirst = false;
215         }
216         ZenDeviceEffects currEffects = zenMode.getDeviceEffects();
217         if (currEffects.shouldDisplayGrayscale()) {
218             if (isFirst) {
219                 enabledEffects.add(mContext.getString(R.string.mode_grayscale_title));
220             } else {
221                 enabledEffects.add(mContext.getString(
222                         R.string.mode_grayscale_title_secondary_list));
223             }
224             isFirst = false;
225         }
226         if (currEffects.shouldSuppressAmbientDisplay()) {
227             if (isFirst) {
228                 enabledEffects.add(mContext.getString(R.string.mode_aod_title));
229             } else {
230                 enabledEffects.add(mContext.getString(
231                         R.string.mode_aod_title_secondary_list));
232             }
233             isFirst = false;
234         }
235         if (currEffects.shouldDimWallpaper()) {
236             if (isFirst) {
237                 enabledEffects.add(mContext.getString(R.string.mode_wallpaper_title));
238             } else {
239                 enabledEffects.add(mContext.getString(
240                         R.string.mode_wallpaper_title_secondary_list));
241             }
242             isFirst = false;
243         }
244         if (currEffects.shouldUseNightMode()) {
245             if (isFirst) {
246                 enabledEffects.add(mContext.getString(R.string.mode_dark_theme_title));
247             } else {
248                 enabledEffects.add(mContext.getString(
249                         R.string.mode_dark_theme_title_secondary_list));
250             }
251             isFirst = false;
252         }
253 
254         int numCategories = enabledEffects.size();
255         MessageFormat msgFormat = new MessageFormat(
256                 mContext.getString(R.string.mode_display_settings_summary),
257                 Locale.getDefault());
258         Map<String, Object> args = new HashMap<>();
259         args.put("count", numCategories);
260         if (numCategories >= 1) {
261             args.put("effect_1", enabledEffects.get(0));
262             if (numCategories >= 2) {
263                 args.put("effect_2", enabledEffects.get(1));
264                 if (numCategories == 3) {
265                     args.put("effect_3", enabledEffects.get(2));
266                 }
267             }
268         }
269         return msgFormat.format(args);
270     }
271 
getEnabledCategories(ZenPolicy policy, Predicate<Integer> filteredCategories, boolean capitalizeFirstInList)272     private List<String> getEnabledCategories(ZenPolicy policy,
273             Predicate<Integer> filteredCategories, boolean capitalizeFirstInList) {
274         List<String> enabledCategories = new ArrayList<>();
275         for (int category : ALL_PRIORITY_CATEGORIES) {
276             boolean isFirst = capitalizeFirstInList && enabledCategories.isEmpty();
277             if (filteredCategories.test(category) && policy.isCategoryAllowed(category, false)) {
278                 if (category == PRIORITY_CATEGORY_REPEAT_CALLERS
279                         && policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false)
280                         && policy.getPriorityCallSenders() == PEOPLE_TYPE_ANYONE) {
281                     continue;
282                 }
283 
284                 // For conversations, only the "all/priority conversations" settings are relevant;
285                 // any other setting is subsumed by the messages-specific messaging.
286                 if (category == PRIORITY_CATEGORY_CONVERSATIONS
287                         && policy.isCategoryAllowed(PRIORITY_CATEGORY_CONVERSATIONS, false)
288                         && policy.getPriorityConversationSenders() != CONVERSATION_SENDERS_ANYONE
289                         && policy.getPriorityConversationSenders()
290                         != CONVERSATION_SENDERS_IMPORTANT) {
291                     continue;
292                 }
293 
294                 enabledCategories.add(getCategory(category, policy, isFirst));
295             }
296         }
297         return enabledCategories;
298     }
299 
getCategory(int category, ZenPolicy policy, boolean isFirst)300     private String getCategory(int category, ZenPolicy policy, boolean isFirst) {
301         if (category == PRIORITY_CATEGORY_ALARMS) {
302             if (isFirst) {
303                 return mContext.getString(R.string.zen_mode_alarms_list_first);
304             } else {
305                 return mContext.getString(R.string.zen_mode_alarms_list);
306             }
307         } else if (category == PRIORITY_CATEGORY_MEDIA) {
308             if (isFirst) {
309                 return mContext.getString(R.string.zen_mode_media_list_first);
310             } else {
311                 return mContext.getString(R.string.zen_mode_media_list);
312             }
313         } else if (category == PRIORITY_CATEGORY_SYSTEM) {
314             if (isFirst) {
315                 return mContext.getString(R.string.zen_mode_system_list_first);
316             } else {
317                 return mContext.getString(R.string.zen_mode_system_list);
318             }
319         } else if (category == PRIORITY_CATEGORY_MESSAGES) {
320             if (policy.getPriorityMessageSenders() == PEOPLE_TYPE_ANYONE) {
321                 return mContext.getString(R.string.zen_mode_from_anyone);
322             } else if (policy.getPriorityMessageSenders() == PEOPLE_TYPE_CONTACTS) {
323                 return mContext.getString(R.string.zen_mode_from_contacts);
324             } else {
325                 return mContext.getString(R.string.zen_mode_from_starred);
326             }
327         } else if (category == PRIORITY_CATEGORY_CONVERSATIONS) {
328             if (policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_IMPORTANT) {
329                 if (isFirst) {
330                     return mContext.getString(R.string.zen_mode_from_important_conversations);
331                 } else {
332                     return mContext.getString(
333                             R.string.zen_mode_from_important_conversations_second);
334                 }
335             } else if (policy.getPriorityConversationSenders() == CONVERSATION_SENDERS_ANYONE) {
336                 if (isFirst) {
337                     return mContext.getString(R.string.zen_mode_from_all_conversations);
338                 } else {
339                     return mContext.getString(R.string.zen_mode_from_all_conversations_second);
340                 }
341             }
342         } else if (category == PRIORITY_CATEGORY_EVENTS) {
343             if (isFirst) {
344                 return mContext.getString(R.string.zen_mode_events_list_first);
345             } else {
346                 return mContext.getString(R.string.zen_mode_events_list);
347             }
348         } else if (category == PRIORITY_CATEGORY_REMINDERS) {
349             if (isFirst) {
350                 return mContext.getString(R.string.zen_mode_reminders_list_first);
351             } else {
352                 return mContext.getString(R.string.zen_mode_reminders_list);
353             }
354         } else if (category == PRIORITY_CATEGORY_CALLS) {
355             if (policy.getPriorityCallSenders() == PEOPLE_TYPE_ANYONE) {
356                 if (isFirst) {
357                     return mContext.getString(R.string.zen_mode_from_anyone);
358                 }
359                 return mContext.getString(R.string.zen_mode_all_callers);
360             } else if (policy.getPriorityCallSenders() == PEOPLE_TYPE_CONTACTS) {
361                 if (isFirst) {
362                     return mContext.getString(R.string.zen_mode_from_contacts);
363                 }
364                 return mContext.getString(R.string.zen_mode_contacts_callers);
365             } else {
366                 if (isFirst) {
367                     return mContext.getString(R.string.zen_mode_from_starred);
368                 }
369                 return mContext.getString(R.string.zen_mode_starred_callers);
370             }
371         } else if (category == PRIORITY_CATEGORY_REPEAT_CALLERS) {
372             if (isFirst) {
373                 return mContext.getString(R.string.zen_mode_repeat_callers);
374             } else {
375                 return mContext.getString(R.string.zen_mode_repeat_callers_list);
376             }
377         }
378 
379         return "";
380     }
381 
getStarredContactsSummary()382     public String getStarredContactsSummary() {
383         List<String> starredContacts = mBackend.getStarredContacts().stream()
384                 .map(ZenHelperBackend.Contact::displayName)
385                 .map(name -> Strings.isNullOrEmpty(name)
386                         ? mContext.getString(R.string.zen_mode_starred_contacts_empty_name)
387                         : name)
388                 .toList();
389         int numStarredContacts = starredContacts.size();
390         MessageFormat msgFormat = new MessageFormat(
391                 mContext.getString(R.string.zen_mode_starred_contacts_summary_contacts),
392                 Locale.getDefault());
393         Map<String, Object> args = new HashMap<>();
394         args.put("count", numStarredContacts);
395         if (numStarredContacts >= 1) {
396             args.put("contact_1", starredContacts.get(0));
397             if (numStarredContacts >= 2) {
398                 args.put("contact_2", starredContacts.get(1));
399                 if (numStarredContacts == 3) {
400                     args.put("contact_3", starredContacts.get(2));
401                 }
402             }
403         }
404         return msgFormat.format(args);
405     }
406 
getContactsNumberSummary()407     public String getContactsNumberSummary() {
408         MessageFormat msgFormat = new MessageFormat(
409                 mContext.getString(R.string.zen_mode_contacts_count),
410                 Locale.getDefault());
411         Map<String, Object> args = new HashMap<>();
412         args.put("count", mBackend.getAllContactsCount());
413         return msgFormat.format(args);
414     }
415 
getPeopleSummary(ZenPolicy policy)416     public String getPeopleSummary(ZenPolicy policy) {
417         @PeopleType int callersAllowed = policy.getPriorityCategoryCalls() == STATE_ALLOW
418                 ? policy.getPriorityCallSenders() : PEOPLE_TYPE_NONE;
419         @PeopleType int messagesAllowed = policy.getPriorityCategoryMessages() == STATE_ALLOW
420                 ? policy.getPriorityMessageSenders() : PEOPLE_TYPE_NONE;
421         @ConversationSenders int conversationsAllowed =
422                 policy.getPriorityCategoryConversations() == STATE_ALLOW
423                         ? policy.getPriorityConversationSenders()
424                         : CONVERSATION_SENDERS_NONE;
425         final boolean areRepeatCallersAllowed =
426                 policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false);
427 
428         if (callersAllowed == PEOPLE_TYPE_ANYONE
429                 && messagesAllowed == PEOPLE_TYPE_ANYONE
430                 && conversationsAllowed == CONVERSATION_SENDERS_ANYONE) {
431             return mContext.getString(R.string.zen_mode_people_all);
432         } else if (callersAllowed == PEOPLE_TYPE_NONE
433                 && messagesAllowed == PEOPLE_TYPE_NONE
434                 && conversationsAllowed == CONVERSATION_SENDERS_NONE) {
435             return mContext.getString(
436                     areRepeatCallersAllowed ? R.string.zen_mode_people_repeat_callers
437                             : R.string.zen_mode_people_none);
438         } else {
439             return mContext.getResources().getString(R.string.zen_mode_people_some);
440         }
441     }
442 
443     /**
444      * Generates a summary to display under the top level "Apps" preference for a mode, based
445      * on the given mode and provided set of apps.
446      */
getAppsSummary(@onNull ZenMode zenMode, @Nullable List<AppEntry> appsBypassing)447     public @NonNull String getAppsSummary(@NonNull ZenMode zenMode,
448             @Nullable List<AppEntry> appsBypassing) {
449         if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_PRIORITY) {
450             return formatAppsList(appsBypassing);
451         } else if (zenMode.getPolicy().getAllowedChannels() == ZenPolicy.CHANNEL_POLICY_NONE) {
452             return mContext.getResources().getString(R.string.zen_mode_apps_none_apps);
453         }
454         return "";
455     }
456 
457     /**
458      * Generates a formatted string declaring which apps can interrupt in the style of
459      * "App, App2, and 4 more can interrupt."
460      * Apps selected for explicit mention are picked in order from the provided list.
461      */
462     @VisibleForTesting
formatAppsList(@ullable List<AppEntry> appsBypassingDnd)463     public @NonNull String formatAppsList(@Nullable List<AppEntry> appsBypassingDnd) {
464         if (appsBypassingDnd == null) {
465             return mContext.getResources().getString(R.string.zen_mode_apps_priority_apps);
466         }
467         List<String> appNames = appsBypassingDnd.stream().limit(3)
468                 .map(app -> {
469                     String appName = BidiFormatter.getInstance().unicodeWrap(app.label);
470                     if (app.isManagedProfile()) {
471                         appName = mContext.getString(R.string.zen_mode_apps_work_app, appName);
472                     }
473                     return appName;
474                 })
475                 .toList();
476 
477         MessageFormat msgFormat = new MessageFormat(
478                 mContext.getString(R.string.zen_mode_apps_subtext),
479                 Locale.getDefault());
480         Map<String, Object> args = new HashMap<>();
481         args.put("count", appsBypassingDnd.size());
482         if (appNames.size() >= 1) {
483             args.put("app_1", appNames.get(0));
484             if (appNames.size() >= 2) {
485                 args.put("app_2", appNames.get(1));
486                 if (appNames.size() == 3) {
487                     args.put("app_3", appNames.get(2));
488                 }
489             }
490         }
491         return msgFormat.format(args);
492     }
493 
getModesSummary(List<ZenMode> modes)494     String getModesSummary(List<ZenMode> modes) {
495         List<ZenMode> activeModes = modes.stream().filter(ZenMode::isActive).toList();
496 
497         if (!activeModes.isEmpty()) {
498             MessageFormat msgFormat = new MessageFormat(
499                     mContext.getString(R.string.zen_modes_summary_some_active),
500                     Locale.getDefault());
501             return buildModesSummary(msgFormat, activeModes);
502         } else {
503             List<ZenMode> modesExcludingImplicit = modes.stream()
504                     .filter(m -> m.getKind() != ZenMode.Kind.IMPLICIT)
505                     .toList();
506             MessageFormat msgFormat = new MessageFormat(
507                     mContext.getString(R.string.zen_modes_summary),
508                     Locale.getDefault());
509             return buildModesSummary(msgFormat, modesExcludingImplicit);
510         }
511     }
512 
buildModesSummary(MessageFormat msgFormat, List<ZenMode> modes)513     private static String buildModesSummary(MessageFormat msgFormat, List<ZenMode> modes) {
514         Map<String, Object> args = new HashMap<>();
515         args.put("count", modes.size());
516         if (modes.size() >= 1) {
517             args.put("mode_1", modes.get(0).getName());
518             if (modes.size() >= 2) {
519                 args.put("mode_2", modes.get(1).getName());
520                 if (modes.size() >= 3) {
521                     args.put("mode_3", modes.get(2).getName());
522                 }
523             }
524         }
525         return msgFormat.format(args);
526     }
527 }
528