1 /*
2  * Copyright (C) 2014 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 
17 package com.android.settings.notification.zen;
18 
19 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS;
20 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CALLS;
21 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_CONVERSATIONS;
22 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_EVENTS;
23 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA;
24 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_MESSAGES;
25 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REMINDERS;
26 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
27 import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM;
28 
29 import android.app.Activity;
30 import android.app.Application;
31 import android.app.AutomaticZenRule;
32 import android.app.NotificationManager;
33 import android.app.NotificationManager.Policy;
34 import android.app.settings.SettingsEnums;
35 import android.content.Context;
36 import android.icu.text.MessageFormat;
37 import android.provider.Settings;
38 import android.service.notification.ZenModeConfig;
39 
40 import androidx.annotation.VisibleForTesting;
41 import androidx.fragment.app.Fragment;
42 import androidx.fragment.app.FragmentManager;
43 
44 import com.android.settings.R;
45 import com.android.settings.search.BaseSearchIndexProvider;
46 import com.android.settingslib.core.AbstractPreferenceController;
47 import com.android.settingslib.core.lifecycle.Lifecycle;
48 import com.android.settingslib.search.SearchIndexable;
49 
50 import java.util.ArrayList;
51 import java.util.HashMap;
52 import java.util.List;
53 import java.util.Locale;
54 import java.util.Map;
55 import java.util.Map.Entry;
56 import java.util.function.Predicate;
57 
58 @SearchIndexable
59 public class ZenModeSettings extends ZenModeSettingsBase {
60     @Override
onResume()61     public void onResume() {
62         super.onResume();
63     }
64 
65     @Override
getPreferenceScreenResId()66     protected int getPreferenceScreenResId() {
67         return R.xml.zen_mode_settings;
68     }
69 
70     @Override
getMetricsCategory()71     public int getMetricsCategory() {
72         return SettingsEnums.NOTIFICATION_ZEN_MODE;
73     }
74 
75     @Override
createPreferenceControllers(Context context)76     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
77         final Activity activity = getActivity();
78         return buildPreferenceControllers(context, getSettingsLifecycle(), getFragmentManager(),
79                 activity != null ? activity.getApplication() : null, this);
80     }
81 
82     @Override
getHelpResource()83     public int getHelpResource() {
84         return R.string.help_uri_interruptions;
85     }
86 
buildPreferenceControllers(Context context, Lifecycle lifecycle, FragmentManager fragmentManager, Application app, Fragment fragment)87     private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
88             Lifecycle lifecycle, FragmentManager fragmentManager, Application app,
89             Fragment fragment) {
90         List<AbstractPreferenceController> controllers = new ArrayList<>();
91         controllers.add(new ZenModeButtonPreferenceController(context, lifecycle, fragmentManager));
92         controllers.add(new ZenModePeoplePreferenceController(context, lifecycle,
93                 "zen_mode_behavior_people"));
94         controllers.add(new ZenModeBypassingAppsPreferenceController(context, app,
95                 fragment, lifecycle));
96         controllers.add(new ZenModeSoundVibrationPreferenceController(context, lifecycle,
97                 "zen_sound_vibration_settings"));
98         controllers.add(new ZenModeAutomationPreferenceController(context));
99         controllers.add(new ZenModeDurationPreferenceController(context, lifecycle));
100         controllers.add(new ZenModeBlockedEffectsPreferenceController(context, lifecycle));
101         controllers.add(new ZenModeSettingsFooterPreferenceController(context, lifecycle,
102                 fragmentManager));
103         return controllers;
104     }
105 
106     public static class SummaryBuilder {
107 
108         private Context mContext;
109 
SummaryBuilder(Context context)110         public SummaryBuilder(Context context) {
111             mContext = context;
112         }
113 
114         // these should match NotificationManager.Policy#ALL_PRIORITY_CATEGORIES
115         private static final int[] ALL_PRIORITY_CATEGORIES = {
116                 PRIORITY_CATEGORY_ALARMS,
117                 PRIORITY_CATEGORY_MEDIA,
118                 PRIORITY_CATEGORY_SYSTEM,
119                 PRIORITY_CATEGORY_MESSAGES,
120                 PRIORITY_CATEGORY_CONVERSATIONS,
121                 PRIORITY_CATEGORY_EVENTS,
122                 PRIORITY_CATEGORY_REMINDERS,
123                 PRIORITY_CATEGORY_CALLS,
124                 PRIORITY_CATEGORY_REPEAT_CALLERS,
125         };
126 
getOtherSoundCategoriesSummary(Policy policy)127         String getOtherSoundCategoriesSummary(Policy policy) {
128             List<String> enabledCategories = getEnabledCategories(
129                     policy,
130                     category -> PRIORITY_CATEGORY_ALARMS == category
131                             || PRIORITY_CATEGORY_MEDIA == category
132                             || PRIORITY_CATEGORY_SYSTEM == category
133                             || PRIORITY_CATEGORY_REMINDERS == category
134                             || PRIORITY_CATEGORY_EVENTS == category,
135                     true);
136             int numCategories = enabledCategories.size();
137             MessageFormat msgFormat = new MessageFormat(
138                     mContext.getString(R.string.zen_mode_other_sounds_summary),
139                     Locale.getDefault());
140             Map<String, Object> args = new HashMap<>();
141             args.put("count", numCategories);
142             if (numCategories >= 1) {
143                 args.put("sound_category_1", enabledCategories.get(0));
144                 if (numCategories >= 2) {
145                     args.put("sound_category_2", enabledCategories.get(1));
146                     if (numCategories == 3) {
147                         args.put("sound_category_3", enabledCategories.get(2));
148                     }
149                 }
150             }
151             return msgFormat.format(args);
152         }
153 
getCallsSettingSummary(Policy policy)154         String getCallsSettingSummary(Policy policy) {
155             List<String> enabledCategories = getEnabledCategories(policy,
156                     category -> PRIORITY_CATEGORY_CALLS == category
157                             || PRIORITY_CATEGORY_REPEAT_CALLERS == category, true);
158             int numCategories = enabledCategories.size();
159             if (numCategories == 0) {
160                 return mContext.getString(R.string.zen_mode_none_calls);
161             } else if (numCategories == 1) {
162                 return mContext.getString(R.string.zen_mode_calls_summary_one,
163                         enabledCategories.get(0));
164             } else {
165                 return mContext.getString(R.string.zen_mode_calls_summary_two,
166                         enabledCategories.get(0),
167                         enabledCategories.get(1));
168             }
169         }
170 
getMessagesSettingSummary(Policy policy)171         String getMessagesSettingSummary(Policy policy) {
172             List<String> enabledCategories = getEnabledCategories(policy,
173                     category -> PRIORITY_CATEGORY_MESSAGES == category
174                            || PRIORITY_CATEGORY_CONVERSATIONS == category, true);
175             int numCategories = enabledCategories.size();
176             if (numCategories == 0) {
177                 return mContext.getString(R.string.zen_mode_none_messages);
178             } else if (numCategories == 1) {
179                 return enabledCategories.get(0);
180             } else {
181                 // While this string name seems like a slight misnomer: it's borrowing the analogous
182                 // calls-summary functionality to combine two permissions.
183                 return mContext.getString(R.string.zen_mode_calls_summary_two,
184                         enabledCategories.get(0),
185                         enabledCategories.get(1));
186             }
187         }
188 
getSoundSummary()189         String getSoundSummary() {
190             int zenMode = NotificationManager.from(mContext).getZenMode();
191 
192             if (zenMode != Settings.Global.ZEN_MODE_OFF) {
193                 ZenModeConfig config = NotificationManager.from(mContext).getZenModeConfig();
194                 String description = ZenModeConfig.getDescription(mContext, true, config, false);
195 
196                 if (description == null) {
197                     return mContext.getString(R.string.zen_mode_sound_summary_on);
198                 } else {
199                     return mContext.getString(R.string.zen_mode_sound_summary_on_with_info,
200                             description);
201                 }
202             } else {
203                 MessageFormat msgFormat = new MessageFormat(
204                         mContext.getString(R.string.zen_mode_sound_summary_off),
205                         Locale.getDefault());
206                 Map<String, Object> msgArgs = new HashMap<>();
207                 msgArgs.put("count", getEnabledAutomaticRulesCount());
208                 return msgFormat.format(msgArgs);
209             }
210         }
211 
getBlockedEffectsSummary(Policy policy)212         String getBlockedEffectsSummary(Policy policy) {
213             if (policy.suppressedVisualEffects == 0) {
214                 return mContext.getResources().getString(
215                         R.string.zen_mode_restrict_notifications_summary_muted);
216             } else if (Policy.areAllVisualEffectsSuppressed(policy.suppressedVisualEffects)) {
217                 return mContext.getResources().getString(
218                         R.string.zen_mode_restrict_notifications_summary_hidden);
219             } else {
220                 return mContext.getResources().getString(
221                         R.string.zen_mode_restrict_notifications_summary_custom);
222             }
223         }
224 
getAutomaticRulesSummary()225         String getAutomaticRulesSummary() {
226             MessageFormat msgFormat = new MessageFormat(
227                     mContext.getString(R.string.zen_mode_settings_schedules_summary),
228                     Locale.getDefault());
229             Map<String, Object> msgArgs = new HashMap<>();
230             msgArgs.put("count", getEnabledAutomaticRulesCount());
231             return msgFormat.format(msgArgs);
232         }
233 
234         @VisibleForTesting
getEnabledAutomaticRulesCount()235         int getEnabledAutomaticRulesCount() {
236             int count = 0;
237             final Map<String, AutomaticZenRule> ruleMap =
238                     NotificationManager.from(mContext).getAutomaticZenRules();
239             if (ruleMap != null) {
240                 for (Entry<String, AutomaticZenRule> ruleEntry : ruleMap.entrySet()) {
241                     final AutomaticZenRule rule = ruleEntry.getValue();
242                     if (rule != null && rule.isEnabled()) {
243                         count++;
244                     }
245                 }
246             }
247             return count;
248         }
249 
getEnabledCategories(Policy policy, Predicate<Integer> filteredCategories, boolean capitalizeFirstInList)250         private List<String> getEnabledCategories(Policy policy,
251                 Predicate<Integer> filteredCategories, boolean capitalizeFirstInList) {
252             List<String> enabledCategories = new ArrayList<>();
253             for (int category : ALL_PRIORITY_CATEGORIES) {
254                 boolean isFirst = capitalizeFirstInList && enabledCategories.isEmpty();
255                 if (filteredCategories.test(category) && isCategoryEnabled(policy, category)) {
256                     if (category == Policy.PRIORITY_CATEGORY_REPEAT_CALLERS
257                             && isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_CALLS)
258                             && policy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) {
259                         continue;
260                     }
261 
262                     // For conversations, only the "priority conversations" setting is relevant; any
263                     // other setting is subsumed by the messages-specific messaging.
264                     if (category == Policy.PRIORITY_CATEGORY_CONVERSATIONS
265                             && isCategoryEnabled(policy, Policy.PRIORITY_CATEGORY_CONVERSATIONS)
266                             && policy.priorityConversationSenders
267                                     != Policy.CONVERSATION_SENDERS_IMPORTANT) {
268                         continue;
269                     }
270 
271                     enabledCategories.add(getCategory(category, policy, isFirst));
272                 }
273             }
274             return enabledCategories;
275         }
276 
isCategoryEnabled(Policy policy, int categoryType)277         private boolean isCategoryEnabled(Policy policy, int categoryType) {
278             return (policy.priorityCategories & categoryType) != 0;
279         }
280 
getCategory(int category, Policy policy, boolean isFirst)281         private String getCategory(int category, Policy policy, boolean isFirst) {
282             if (category == PRIORITY_CATEGORY_ALARMS) {
283                 if (isFirst) {
284                     return mContext.getString(R.string.zen_mode_alarms_list_first);
285                 } else {
286                     return mContext.getString(R.string.zen_mode_alarms_list);
287                 }
288             } else if (category == PRIORITY_CATEGORY_MEDIA) {
289                 if (isFirst) {
290                     return mContext.getString(R.string.zen_mode_media_list_first);
291                 } else {
292                     return mContext.getString(R.string.zen_mode_media_list);
293                 }
294             } else if (category == PRIORITY_CATEGORY_SYSTEM) {
295                 if (isFirst) {
296                     return mContext.getString(R.string.zen_mode_system_list_first);
297                 } else {
298                     return mContext.getString(R.string.zen_mode_system_list);
299                 }
300             } else if (category == Policy.PRIORITY_CATEGORY_MESSAGES) {
301                 if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_ANY) {
302                     return mContext.getString(R.string.zen_mode_from_anyone);
303                 } else if (policy.priorityMessageSenders == Policy.PRIORITY_SENDERS_CONTACTS) {
304                     return mContext.getString(R.string.zen_mode_from_contacts);
305                 } else {
306                     return mContext.getString(R.string.zen_mode_from_starred);
307                 }
308             } else if (category == Policy.PRIORITY_CATEGORY_CONVERSATIONS
309                     && policy.priorityConversationSenders
310                             == Policy.CONVERSATION_SENDERS_IMPORTANT) {
311                 if (isFirst) {
312                     return mContext.getString(R.string.zen_mode_from_important_conversations);
313                 } else {
314                     return mContext.getString(
315                             R.string.zen_mode_from_important_conversations_second);
316                 }
317             } else if (category == Policy.PRIORITY_CATEGORY_EVENTS) {
318                 if (isFirst) {
319                     return mContext.getString(R.string.zen_mode_events_list_first);
320                 } else {
321                     return mContext.getString(R.string.zen_mode_events_list);
322                 }
323             } else if (category == Policy.PRIORITY_CATEGORY_REMINDERS) {
324                 if (isFirst) {
325                     return mContext.getString(R.string.zen_mode_reminders_list_first);
326                 } else {
327                     return mContext.getString(R.string.zen_mode_reminders_list);
328                 }
329             } else if (category == Policy.PRIORITY_CATEGORY_CALLS) {
330                 if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_ANY) {
331                     if (isFirst) {
332                         return mContext.getString(R.string.zen_mode_from_anyone);
333                     }
334                     return mContext.getString(R.string.zen_mode_all_callers);
335                 } else if (policy.priorityCallSenders == Policy.PRIORITY_SENDERS_CONTACTS){
336                     if (isFirst) {
337                         return mContext.getString(R.string.zen_mode_from_contacts);
338                     }
339                     return mContext.getString(R.string.zen_mode_contacts_callers);
340                 } else {
341                     if (isFirst) {
342                         return mContext.getString(R.string.zen_mode_from_starred);
343                     }
344                     return mContext.getString(R.string.zen_mode_starred_callers);
345                 }
346             } else if (category == Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) {
347                 if (isFirst) {
348                     return mContext.getString(R.string.zen_mode_repeat_callers);
349                 } else {
350                     return mContext.getString(R.string.zen_mode_repeat_callers_list);
351                 }
352             }
353 
354             return "";
355         }
356     }
357 
358     /**
359      * For Search.
360      */
361     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
362             new BaseSearchIndexProvider(R.xml.zen_mode_settings) {
363 
364                 @Override
365                 public List<String> getNonIndexableKeys(Context context) {
366                     List<String> keys = super.getNonIndexableKeys(context);
367                     keys.add(ZenModeDurationPreferenceController.KEY);
368                     return keys;
369                 }
370 
371                 @Override
372                 public List<AbstractPreferenceController> createPreferenceControllers(Context
373                         context) {
374                     return buildPreferenceControllers(context, null, null,
375                             null, null);
376                 }
377             };
378 }
379