1 /*
2  * Copyright (C) 2020 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 android.app.NotificationManager;
20 import android.app.settings.SettingsEnums;
21 import android.content.Context;
22 import android.content.pm.ParceledListSlice;
23 import android.icu.text.MessageFormat;
24 import android.os.AsyncTask;
25 import android.service.notification.ConversationChannelWrapper;
26 import android.view.View;
27 
28 import androidx.annotation.VisibleForTesting;
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceCategory;
31 import androidx.preference.PreferenceScreen;
32 
33 import com.android.settings.R;
34 import com.android.settings.core.SubSettingLauncher;
35 import com.android.settings.notification.NotificationBackend;
36 import com.android.settings.notification.app.ConversationListSettings;
37 import com.android.settingslib.core.lifecycle.Lifecycle;
38 import com.android.settingslib.widget.RadioButtonPreference;
39 
40 import java.util.ArrayList;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Locale;
44 import java.util.Map;
45 
46 /**
47  * Options to choose the priority conversations that are allowed to bypass DND.
48  */
49 public class ZenModePriorityConversationsPreferenceController
50         extends AbstractZenModePreferenceController {
51     private static final int UNSET = -1;
52     @VisibleForTesting static final String KEY_ALL = "conversations_all";
53     @VisibleForTesting static final String KEY_IMPORTANT = "conversations_important";
54     @VisibleForTesting static final String KEY_NONE = "conversations_none";
55 
56     private final NotificationBackend mNotificationBackend;
57 
58     private int mNumImportantConversations = UNSET;
59     private int mNumConversations = UNSET;
60     private PreferenceCategory mPreferenceCategory;
61     private List<RadioButtonPreference> mRadioButtonPreferences = new ArrayList<>();
62     private Context mPreferenceScreenContext;
63 
ZenModePriorityConversationsPreferenceController(Context context, String key, Lifecycle lifecycle, NotificationBackend notificationBackend)64     public ZenModePriorityConversationsPreferenceController(Context context, String key,
65             Lifecycle lifecycle, NotificationBackend notificationBackend) {
66         super(context, key, lifecycle);
67         mNotificationBackend = notificationBackend;
68     }
69 
70     @Override
displayPreference(PreferenceScreen screen)71     public void displayPreference(PreferenceScreen screen) {
72         mPreferenceScreenContext = screen.getContext();
73         mPreferenceCategory = screen.findPreference(getPreferenceKey());
74         if (mPreferenceCategory.findPreference(KEY_ALL) == null) {
75             makeRadioPreference(KEY_ALL, R.string.zen_mode_from_all_conversations);
76             makeRadioPreference(KEY_IMPORTANT, R.string.zen_mode_from_important_conversations);
77             makeRadioPreference(KEY_NONE, R.string.zen_mode_from_no_conversations);
78             updateChannelCounts();
79         }
80 
81         super.displayPreference(screen);
82     }
83 
84     @Override
onResume()85     public void onResume() {
86         super.onResume();
87         updateChannelCounts();
88     }
89 
90     @Override
isAvailable()91     public boolean isAvailable() {
92         return true;
93     }
94 
95     @Override
getPreferenceKey()96     public String getPreferenceKey() {
97         return KEY;
98     }
99 
100     @Override
updateState(Preference preference)101     public void updateState(Preference preference) {
102         final int currSetting = mBackend.getPriorityConversationSenders();
103 
104         for (RadioButtonPreference pref : mRadioButtonPreferences) {
105             pref.setChecked(keyToSetting(pref.getKey()) == currSetting);
106             pref.setSummary(getSummary(pref.getKey()));
107         }
108     }
109 
keyToSetting(String key)110     private static int keyToSetting(String key) {
111         switch (key) {
112             case KEY_ALL:
113                 return NotificationManager.Policy.CONVERSATION_SENDERS_ANYONE;
114             case KEY_IMPORTANT:
115                 return NotificationManager.Policy.CONVERSATION_SENDERS_IMPORTANT;
116             default:
117                 return NotificationManager.Policy.CONVERSATION_SENDERS_NONE;
118         }
119     }
120 
getSummary(String key)121     private String getSummary(String key) {
122         int numConversations;
123         if (KEY_ALL.equals(key)) {
124             numConversations = mNumConversations;
125         } else if (KEY_IMPORTANT.equals(key)) {
126             numConversations = mNumImportantConversations;
127         } else {
128             return null;
129         }
130 
131         if (numConversations == UNSET) {
132             return null;
133         } else {
134             MessageFormat msgFormat = new MessageFormat(
135                     mContext.getString(R.string.zen_mode_conversations_count),
136                     Locale.getDefault());
137             Map<String, Object> args = new HashMap<>();
138             args.put("count", numConversations);
139             return msgFormat.format(args);
140         }
141     }
142 
updateChannelCounts()143     private void updateChannelCounts() {
144         // Load conversations
145         new AsyncTask<Void, Void, Void>() {
146             @Override
147             protected Void doInBackground(Void... unused) {
148                 ParceledListSlice<ConversationChannelWrapper> allConversations =
149                         mNotificationBackend.getConversations(false);
150                 int numConversations = 0;
151                 if (allConversations != null) {
152                     for (ConversationChannelWrapper conversation : allConversations.getList()) {
153                         if (!conversation.getNotificationChannel().isDemoted()) {
154                             numConversations++;
155                         }
156                     }
157                 }
158                 mNumConversations = numConversations;
159 
160                 ParceledListSlice<ConversationChannelWrapper> impConversations =
161                         mNotificationBackend.getConversations(true);
162                 int numImportantConversations = 0;
163                 if (impConversations != null) {
164                     for (ConversationChannelWrapper conversation : impConversations.getList()) {
165                         if (!conversation.getNotificationChannel().isDemoted()) {
166                             numImportantConversations++;
167                         }
168                     }
169                 }
170                 mNumImportantConversations = numImportantConversations;
171                 return null;
172             }
173 
174             @Override
175             protected void onPostExecute(Void unused) {
176                 if (mContext == null) {
177                     return;
178                 }
179                 updateState(mPreferenceCategory);
180             }
181         }.execute();
182     }
183 
makeRadioPreference(String key, int titleId)184     private RadioButtonPreference makeRadioPreference(String key, int titleId) {
185         final RadioButtonPreference pref =
186                 new RadioButtonPreference(mPreferenceCategory.getContext());
187         if (KEY_ALL.equals(key) || KEY_IMPORTANT.equals(key)) {
188             pref.setExtraWidgetOnClickListener(mConversationSettingsWidgetClickListener);
189         }
190         pref.setKey(key);
191         pref.setTitle(titleId);
192         pref.setOnClickListener(mRadioButtonClickListener);
193         mPreferenceCategory.addPreference(pref);
194         mRadioButtonPreferences.add(pref);
195         return pref;
196     }
197 
198     private View.OnClickListener mConversationSettingsWidgetClickListener =
199             new View.OnClickListener() {
200         @Override
201         public void onClick(View v) {
202             new SubSettingLauncher(mPreferenceScreenContext)
203                     .setDestination(ConversationListSettings.class.getName())
204                     .setSourceMetricsCategory(SettingsEnums.DND_CONVERSATIONS)
205                     .launch();
206         }
207     };
208 
209     private RadioButtonPreference.OnClickListener mRadioButtonClickListener =
210             new RadioButtonPreference.OnClickListener() {
211         @Override
212         public void onRadioButtonClicked(RadioButtonPreference preference) {
213             int selectedConversationSetting = keyToSetting(preference.getKey());
214             if (selectedConversationSetting != mBackend.getPriorityConversationSenders()) {
215                 mBackend.saveConversationSenders(selectedConversationSetting);
216             }
217         }
218     };
219 }
220