| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.settings.notification.app; |
| |
| import static android.app.NotificationManager.IMPORTANCE_NONE; |
| |
| import android.app.people.ConversationChannel; |
| import android.app.people.IPeopleManager; |
| import android.app.settings.SettingsEnums; |
| import android.content.Context; |
| import android.content.pm.ShortcutInfo; |
| import android.os.AsyncTask; |
| import android.os.Bundle; |
| import android.os.RemoteException; |
| import android.os.UserHandle; |
| import android.provider.Settings; |
| import android.util.Slog; |
| import android.widget.Button; |
| |
| import androidx.preference.Preference; |
| import androidx.preference.PreferenceCategory; |
| import androidx.preference.PreferenceGroup; |
| |
| import com.android.settings.R; |
| import com.android.settings.applications.AppInfoBase; |
| import com.android.settings.core.SubSettingLauncher; |
| import com.android.settings.notification.NotificationBackend; |
| import com.android.settingslib.core.AbstractPreferenceController; |
| import com.android.settingslib.widget.LayoutPreference; |
| |
| import java.text.Collator; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| |
| public class RecentConversationsPreferenceController extends AbstractPreferenceController { |
| |
| private static final String TAG = "RecentConversationsPC"; |
| private static final String KEY = "recent_conversations"; |
| private List<ConversationChannel> mConversations; |
| private final IPeopleManager mPs; |
| private final NotificationBackend mBackend; |
| |
| public RecentConversationsPreferenceController(Context context, NotificationBackend backend, |
| IPeopleManager ps) { |
| super(context); |
| mBackend = backend; |
| mPs = ps; |
| } |
| |
| @Override |
| public String getPreferenceKey() { |
| return KEY; |
| } |
| |
| @Override |
| public boolean isAvailable() { |
| return true; |
| } |
| |
| LayoutPreference getClearAll(PreferenceGroup parent) { |
| LayoutPreference pref = new LayoutPreference( |
| mContext, R.layout.conversations_clear_recents); |
| pref.setOrder(1); |
| Button button = pref.findViewById(R.id.conversation_settings_clear_recents); |
| button.setOnClickListener(v -> { |
| try { |
| mPs.removeAllRecentConversations(); |
| // Removing recents is asynchronous, so we can't immediately reload the list from |
| // the backend. Instead, proactively remove all of items that were marked as |
| // clearable, so long as we didn't get an error |
| |
| for (int i = parent.getPreferenceCount() - 1; i >= 0; i--) { |
| Preference p = parent.getPreference(i); |
| if (p instanceof RecentConversationPreference) { |
| if (((RecentConversationPreference) p).hasClearListener()) { |
| parent.removePreference(p); |
| } |
| } |
| } |
| button.announceForAccessibility(mContext.getString(R.string.recent_convos_removed)); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Could not clear recents", e); |
| } |
| }); |
| return pref; |
| } |
| |
| @Override |
| public void updateState(Preference preference) { |
| PreferenceCategory pref = (PreferenceCategory) preference; |
| // Load conversations |
| try { |
| mConversations = mPs.getRecentConversations().getList(); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Could get recents", e); |
| } |
| Collections.sort(mConversations, mConversationComparator); |
| |
| populateList(mConversations, pref); |
| |
| } |
| |
| protected void populateList(List<ConversationChannel> conversations, |
| PreferenceGroup containerGroup) { |
| containerGroup.removeAll(); |
| boolean hasClearable = false; |
| if (conversations != null) { |
| hasClearable = populateConversations(conversations, containerGroup); |
| } |
| |
| if (containerGroup.getPreferenceCount() == 0) { |
| containerGroup.setVisible(false); |
| } else { |
| containerGroup.setVisible(true); |
| if (hasClearable) { |
| Preference clearAll = getClearAll(containerGroup); |
| if (clearAll != null) { |
| containerGroup.addPreference(clearAll); |
| } |
| } |
| } |
| } |
| |
| protected boolean populateConversations(List<ConversationChannel> conversations, |
| PreferenceGroup containerGroup) { |
| int order = 100; |
| boolean hasClearable = false; |
| for (ConversationChannel conversation : conversations) { |
| if (conversation.getNotificationChannel().getImportance() == IMPORTANCE_NONE |
| || (conversation.getNotificationChannelGroup() != null |
| && conversation.getNotificationChannelGroup().isBlocked())) { |
| continue; |
| } |
| RecentConversationPreference pref = |
| createConversationPref(containerGroup, conversation, order++); |
| containerGroup.addPreference(pref); |
| if (pref.hasClearListener()) { |
| hasClearable = true; |
| } |
| } |
| return hasClearable; |
| } |
| |
| protected RecentConversationPreference createConversationPref(PreferenceGroup parent, |
| final ConversationChannel conversation, int order) { |
| final String pkg = conversation.getShortcutInfo().getPackage(); |
| final int uid = conversation.getUid(); |
| final String conversationId = conversation.getShortcutInfo().getId(); |
| RecentConversationPreference pref = new RecentConversationPreference(mContext); |
| |
| if (!conversation.hasActiveNotifications()) { |
| pref.setOnClearClickListener(() -> { |
| try { |
| mPs.removeRecentConversation(pkg, UserHandle.getUserId(uid), conversationId); |
| pref.getClearView().announceForAccessibility( |
| mContext.getString(R.string.recent_convo_removed)); |
| parent.removePreference(pref); |
| } catch (RemoteException e) { |
| Slog.w(TAG, "Could not clear recent", e); |
| } |
| }); |
| } |
| pref.setOrder(order); |
| |
| pref.setTitle(getTitle(conversation)); |
| pref.setSummary(getSummary(conversation)); |
| pref.setIcon(mBackend.getConversationDrawable(mContext, conversation.getShortcutInfo(), |
| pkg, uid, false)); |
| pref.setKey(conversation.getNotificationChannel().getId() |
| + ":" + conversationId); |
| pref.setOnPreferenceClickListener(preference -> { |
| mBackend.createConversationNotificationChannel( |
| pkg, uid, |
| conversation.getNotificationChannel(), |
| conversationId); |
| getSubSettingLauncher(conversation, pref.getTitle()).launch(); |
| return true; |
| }); |
| |
| return pref; |
| } |
| |
| CharSequence getSummary(ConversationChannel conversation) { |
| return conversation.getNotificationChannelGroup() == null |
| ? conversation.getNotificationChannel().getName() |
| : mContext.getString(R.string.notification_conversation_summary, |
| conversation.getNotificationChannel().getName(), |
| conversation.getNotificationChannelGroup().getName()); |
| } |
| |
| CharSequence getTitle(ConversationChannel conversation) { |
| ShortcutInfo si = conversation.getShortcutInfo(); |
| return si.getLabel(); |
| } |
| |
| SubSettingLauncher getSubSettingLauncher(ConversationChannel conversation, |
| CharSequence title) { |
| Bundle channelArgs = new Bundle(); |
| channelArgs.putInt(AppInfoBase.ARG_PACKAGE_UID, conversation.getUid()); |
| channelArgs.putString(AppInfoBase.ARG_PACKAGE_NAME, |
| conversation.getShortcutInfo().getPackage()); |
| channelArgs.putString(Settings.EXTRA_CHANNEL_ID, |
| conversation.getNotificationChannel().getId()); |
| channelArgs.putString(Settings.EXTRA_CONVERSATION_ID, |
| conversation.getShortcutInfo().getId()); |
| |
| return new SubSettingLauncher(mContext) |
| .setDestination(ChannelNotificationSettings.class.getName()) |
| .setArguments(channelArgs) |
| .setExtras(channelArgs) |
| .setUserHandle(UserHandle.getUserHandleForUid(conversation.getUid())) |
| .setTitleText(title) |
| .setSourceMetricsCategory(SettingsEnums.NOTIFICATION_CONVERSATION_LIST_SETTINGS); |
| } |
| |
| protected Comparator<ConversationChannel> mConversationComparator = |
| new Comparator<ConversationChannel>() { |
| private final Collator sCollator = Collator.getInstance(); |
| @Override |
| public int compare(ConversationChannel o1, ConversationChannel o2) { |
| int labelComparison = 0; |
| if (o1.getShortcutInfo().getLabel() != null |
| && o2.getShortcutInfo().getLabel() != null) { |
| labelComparison = sCollator.compare( |
| o1.getShortcutInfo().getLabel().toString(), |
| o2.getShortcutInfo().getLabel().toString()); |
| } |
| |
| if (labelComparison == 0) { |
| return o1.getNotificationChannel().getId().compareTo( |
| o2.getNotificationChannel().getId()); |
| } |
| |
| return labelComparison; |
| } |
| }; |
| } |