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.app;
18 
19 import android.app.NotificationChannel;
20 import android.app.settings.SettingsEnums;
21 import android.content.Context;
22 import android.content.pm.ParceledListSlice;
23 import android.content.pm.ShortcutInfo;
24 import android.os.AsyncTask;
25 import android.os.Bundle;
26 import android.provider.Settings;
27 import android.service.notification.ConversationChannelWrapper;
28 
29 import androidx.preference.Preference;
30 import androidx.preference.PreferenceCategory;
31 
32 import com.android.settings.R;
33 import com.android.settings.applications.AppInfoBase;
34 import com.android.settings.core.SubSettingLauncher;
35 import com.android.settings.notification.NotificationBackend;
36 import com.android.settingslib.widget.AppPreference;
37 
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.Comparator;
41 import java.util.List;
42 
43 public class AppConversationListPreferenceController extends NotificationPreferenceController {
44 
45     private static final String KEY = "conversations";
46     public static final String ARG_FROM_SETTINGS = "fromSettings";
47 
48     protected List<ConversationChannelWrapper> mConversations = new ArrayList<>();
49     protected PreferenceCategory mPreference;
50 
AppConversationListPreferenceController(Context context, NotificationBackend backend)51     public AppConversationListPreferenceController(Context context, NotificationBackend backend) {
52         super(context, backend);
53     }
54 
55     @Override
getPreferenceKey()56     public String getPreferenceKey() {
57         return KEY;
58     }
59 
60     @Override
isAvailable()61     public boolean isAvailable() {
62         if (mAppRow == null) {
63             return false;
64         }
65         if (mAppRow.banned) {
66             return false;
67         }
68         if (mChannel != null) {
69             if (mBackend.onlyHasDefaultChannel(mAppRow.pkg, mAppRow.uid)
70                     || NotificationChannel.DEFAULT_CHANNEL_ID.equals(mChannel.getId())) {
71                 return false;
72             }
73         }
74         return mBackend.hasSentValidMsg(mAppRow.pkg, mAppRow.uid) || mBackend.isInInvalidMsgState(
75                 mAppRow.pkg, mAppRow.uid);
76     }
77 
78     @Override
isIncludedInFilter()79     boolean isIncludedInFilter() {
80         return false;
81     }
82 
83     @Override
updateState(Preference preference)84     public void updateState(Preference preference) {
85         mPreference = (PreferenceCategory) preference;
86         loadConversationsAndPopulate();
87     }
88 
loadConversationsAndPopulate()89     protected void loadConversationsAndPopulate() {
90         if (mAppRow == null) {
91             return;
92         }
93         // Load channel settings
94         new AsyncTask<Void, Void, Void>() {
95             @Override
96             protected Void doInBackground(Void... unused) {
97                 ParceledListSlice<ConversationChannelWrapper> list =
98                         mBackend.getConversations(mAppRow.pkg, mAppRow.uid);
99                 if (list != null) {
100                     mConversations = filterAndSortConversations(list.getList());
101                 }
102                 return null;
103             }
104 
105             @Override
106             protected void onPostExecute(Void unused) {
107                 if (mContext == null) {
108                     return;
109                 }
110                 populateList();
111             }
112         }.execute();
113     }
114 
filterAndSortConversations( List<ConversationChannelWrapper> conversations)115     protected List<ConversationChannelWrapper> filterAndSortConversations(
116             List<ConversationChannelWrapper> conversations) {
117         Collections.sort(conversations, mConversationComparator);
118         return conversations;
119     }
120 
getTitleResId()121     protected int getTitleResId() {
122         return R.string.conversations_category_title;
123     }
124 
populateList()125     protected void populateList() {
126         if (mPreference == null) {
127             return;
128         }
129 
130         mPreference.setTitle(getTitleResId());
131         populateConversations();
132     }
133 
populateConversations()134     private void populateConversations() {
135         mPreference.removeAll();
136         mPreference.setVisible(!mConversations.isEmpty());
137         for (ConversationChannelWrapper conversation : mConversations) {
138             if (conversation.getNotificationChannel().isDemoted()) {
139                 continue;
140             }
141             mPreference.addPreference(createConversationPref(conversation));
142         }
143     }
144 
createConversationPref(final ConversationChannelWrapper conversation)145     protected Preference createConversationPref(final ConversationChannelWrapper conversation) {
146         AppPreference pref = new AppPreference(mContext);
147         populateConversationPreference(conversation, pref);
148         return pref;
149     }
150 
populateConversationPreference(final ConversationChannelWrapper conversation, final Preference pref)151     protected void populateConversationPreference(final ConversationChannelWrapper conversation,
152             final Preference pref) {
153         ShortcutInfo si = conversation.getShortcutInfo();
154 
155         pref.setTitle(si != null
156                 ? si.getLabel()
157                 : conversation.getNotificationChannel().getName());
158         pref.setSummary(conversation.getNotificationChannel().getGroup() != null
159                 ? mContext.getString(R.string.notification_conversation_summary,
160                 conversation.getParentChannelLabel(), conversation.getGroupLabel())
161                 : conversation.getParentChannelLabel());
162         if (si != null) {
163             pref.setIcon(mBackend.getConversationDrawable(mContext, si, mAppRow.pkg, mAppRow.uid,
164                     conversation.getNotificationChannel().isImportantConversation()));
165         }
166         pref.setKey(conversation.getNotificationChannel().getId());
167 
168         Bundle channelArgs = new Bundle();
169         channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid);
170         channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg);
171         channelArgs.putString(Settings.EXTRA_CHANNEL_ID,
172                 conversation.getNotificationChannel().getParentChannelId());
173         channelArgs.putString(Settings.EXTRA_CONVERSATION_ID,
174                 conversation.getNotificationChannel().getConversationId());
175         channelArgs.putBoolean(ARG_FROM_SETTINGS, true);
176         pref.setIntent(new SubSettingLauncher(mContext)
177                 .setDestination(ChannelNotificationSettings.class.getName())
178                 .setArguments(channelArgs)
179                 .setExtras(channelArgs)
180                 .setTitleText(pref.getTitle())
181                 .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION)
182                 .toIntent());
183     }
184 
185     protected Comparator<ConversationChannelWrapper> mConversationComparator =
186             (left, right) -> {
187                 if (left.getNotificationChannel().isImportantConversation()
188                         != right.getNotificationChannel().isImportantConversation()) {
189                     // important first
190                     return Boolean.compare(right.getNotificationChannel().isImportantConversation(),
191                             left.getNotificationChannel().isImportantConversation());
192                 }
193                 return left.getNotificationChannel().getId().compareTo(
194                         right.getNotificationChannel().getId());
195             };
196 }
197