1 /* <lambda>null2 * 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.systemui.statusbar.notification.collection.coordinator 18 19 import com.android.systemui.statusbar.notification.collection.ListEntry 20 import com.android.systemui.statusbar.notification.collection.NotifPipeline 21 import com.android.systemui.statusbar.notification.collection.NotificationEntry 22 import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag 23 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope 24 import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeRenderListListener 25 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator 26 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter 27 import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner 28 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider 29 import com.android.systemui.statusbar.notification.collection.render.NodeController 30 import com.android.systemui.statusbar.notification.dagger.PeopleHeader 31 import com.android.systemui.statusbar.notification.icon.ConversationIconManager 32 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier 33 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType 34 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON 35 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON 36 import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE 37 import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE 38 import javax.inject.Inject 39 40 /** 41 * A Conversation/People Coordinator that: 42 * - Elevates important conversation notifications 43 * - Puts conversations into its own people section. @see [NotifCoordinators] for section ordering. 44 */ 45 @CoordinatorScope 46 class ConversationCoordinator @Inject constructor( 47 private val peopleNotificationIdentifier: PeopleNotificationIdentifier, 48 private val conversationIconManager: ConversationIconManager, 49 private val highPriorityProvider: HighPriorityProvider, 50 @PeopleHeader private val peopleHeaderController: NodeController, 51 ) : Coordinator { 52 53 private val promotedEntriesToSummaryOfSameChannel = 54 mutableMapOf<NotificationEntry, NotificationEntry>() 55 56 private val onBeforeRenderListListener = OnBeforeRenderListListener { _ -> 57 val unimportantSummaries = promotedEntriesToSummaryOfSameChannel 58 .mapNotNull { (promoted, summary) -> 59 val originalGroup = summary.parent 60 when { 61 originalGroup == null -> null 62 originalGroup == promoted.parent -> null 63 originalGroup.parent == null -> null 64 originalGroup.summary != summary -> null 65 originalGroup.children.any { it.channel == summary.channel } -> null 66 else -> summary.key 67 } 68 } 69 conversationIconManager.setUnimportantConversations(unimportantSummaries) 70 promotedEntriesToSummaryOfSameChannel.clear() 71 } 72 73 private val notificationPromoter = object : NotifPromoter(TAG) { 74 override fun shouldPromoteToTopLevel(entry: NotificationEntry): Boolean { 75 val shouldPromote = entry.channel?.isImportantConversation == true 76 if (shouldPromote) { 77 val summary = entry.parent?.summary 78 if (summary != null && entry.channel == summary.channel) { 79 promotedEntriesToSummaryOfSameChannel[entry] = summary 80 } 81 } 82 return shouldPromote 83 } 84 } 85 86 val priorityPeopleSectioner = 87 object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) { 88 override fun isInSection(entry: ListEntry): Boolean { 89 return getPeopleType(entry) == TYPE_IMPORTANT_PERSON 90 } 91 } 92 93 // TODO(b/330193582): Rename to just "People" 94 val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) { 95 override fun isInSection(entry: ListEntry): Boolean { 96 if (SortBySectionTimeFlag.isEnabled) { 97 return highPriorityProvider.isHighPriorityConversation(entry) 98 || isConversation(entry) 99 } else { 100 return highPriorityProvider.isHighPriorityConversation(entry) 101 } 102 } 103 104 override fun getComparator(): NotifComparator? { 105 return if (SortBySectionTimeFlag.isEnabled) null else notifComparator 106 } 107 108 override fun getHeaderNodeController(): NodeController? = conversationHeaderNodeController 109 } 110 111 val peopleSilentSectioner = object : NotifSectioner("People(silent)", BUCKET_PEOPLE) { 112 // Because the peopleAlertingSectioner is above this one, it will claim all conversations that are alerting. 113 // All remaining conversations must be silent. 114 override fun isInSection(entry: ListEntry): Boolean { 115 SortBySectionTimeFlag.assertInLegacyMode() 116 return isConversation(entry) 117 } 118 119 override fun getComparator(): NotifComparator { 120 SortBySectionTimeFlag.assertInLegacyMode() 121 return notifComparator 122 } 123 124 override fun getHeaderNodeController(): NodeController? { 125 SortBySectionTimeFlag.assertInLegacyMode() 126 return conversationHeaderNodeController 127 } 128 } 129 130 override fun attach(pipeline: NotifPipeline) { 131 pipeline.addPromoter(notificationPromoter) 132 pipeline.addOnBeforeRenderListListener(onBeforeRenderListListener) 133 } 134 135 private fun isConversation(entry: ListEntry): Boolean = 136 getPeopleType(entry) != TYPE_NON_PERSON 137 138 @PeopleNotificationType 139 private fun getPeopleType(entry: ListEntry): Int = 140 entry.representativeEntry?.let { 141 peopleNotificationIdentifier.getPeopleNotificationType(it) 142 } ?: TYPE_NON_PERSON 143 144 private val notifComparator: NotifComparator = object : NotifComparator("People") { 145 override fun compare(entry1: ListEntry, entry2: ListEntry): Int { 146 val type1 = getPeopleType(entry1) 147 val type2 = getPeopleType(entry2) 148 return type2.compareTo(type1) 149 } 150 } 151 152 // TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController 153 private val conversationHeaderNodeController: NodeController? = 154 if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null 155 156 private companion object { 157 private const val TAG = "ConversationCoordinator" 158 } 159 } 160