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