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