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 if (!mConversations.isEmpty()) { 131 // TODO: if preference has children, compare with newly loaded list 132 mPreference.removeAll(); 133 mPreference.setTitle(getTitleResId()); 134 populateConversations(); 135 } 136 } 137 populateConversations()138 private void populateConversations() { 139 for (ConversationChannelWrapper conversation : mConversations) { 140 if (conversation.getNotificationChannel().isDemoted()) { 141 continue; 142 } 143 mPreference.addPreference(createConversationPref(conversation)); 144 } 145 } 146 createConversationPref(final ConversationChannelWrapper conversation)147 protected Preference createConversationPref(final ConversationChannelWrapper conversation) { 148 AppPreference pref = new AppPreference(mContext); 149 populateConversationPreference(conversation, pref); 150 return pref; 151 } 152 populateConversationPreference(final ConversationChannelWrapper conversation, final Preference pref)153 protected void populateConversationPreference(final ConversationChannelWrapper conversation, 154 final Preference pref) { 155 ShortcutInfo si = conversation.getShortcutInfo(); 156 157 pref.setTitle(si != null 158 ? si.getLabel() 159 : conversation.getNotificationChannel().getName()); 160 pref.setSummary(conversation.getNotificationChannel().getGroup() != null 161 ? mContext.getString(R.string.notification_conversation_summary, 162 conversation.getParentChannelLabel(), conversation.getGroupLabel()) 163 : conversation.getParentChannelLabel()); 164 if (si != null) { 165 pref.setIcon(mBackend.getConversationDrawable(mContext, si, mAppRow.pkg, mAppRow.uid, 166 conversation.getNotificationChannel().isImportantConversation())); 167 } 168 pref.setKey(conversation.getNotificationChannel().getId()); 169 170 Bundle channelArgs = new Bundle(); 171 channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, mAppRow.uid); 172 channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, mAppRow.pkg); 173 channelArgs.putString(Settings.EXTRA_CHANNEL_ID, 174 conversation.getNotificationChannel().getParentChannelId()); 175 channelArgs.putString(Settings.EXTRA_CONVERSATION_ID, 176 conversation.getNotificationChannel().getConversationId()); 177 channelArgs.putBoolean(ARG_FROM_SETTINGS, true); 178 pref.setIntent(new SubSettingLauncher(mContext) 179 .setDestination(ChannelNotificationSettings.class.getName()) 180 .setArguments(channelArgs) 181 .setExtras(channelArgs) 182 .setTitleText(pref.getTitle()) 183 .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_APP_NOTIFICATION) 184 .toIntent()); 185 } 186 187 protected Comparator<ConversationChannelWrapper> mConversationComparator = 188 (left, right) -> { 189 if (left.getNotificationChannel().isImportantConversation() 190 != right.getNotificationChannel().isImportantConversation()) { 191 // important first 192 return Boolean.compare(right.getNotificationChannel().isImportantConversation(), 193 left.getNotificationChannel().isImportantConversation()); 194 } 195 return left.getNotificationChannel().getId().compareTo( 196 right.getNotificationChannel().getId()); 197 }; 198 } 199