1 /* 2 * Copyright (C) 2019 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.people 18 19 import android.annotation.IntDef 20 import android.service.notification.NotificationListenerService.Ranking 21 import android.service.notification.StatusBarNotification 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.statusbar.notification.collection.NotificationEntry 24 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager 25 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType 26 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON 27 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON 28 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON 29 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON 30 import javax.inject.Inject 31 import kotlin.math.max 32 33 interface PeopleNotificationIdentifier { 34 35 /** 36 * Identifies if the given notification can be classified as a "People" notification. 37 * 38 * @return [TYPE_NON_PERSON] if not a people notification, [TYPE_PERSON] if it is a people 39 * notification that doesn't use shortcuts, [TYPE_FULL_PERSON] if it is a person notification 40 * that users shortcuts, and [TYPE_IMPORTANT_PERSON] if an "important" people notification 41 * that users shortcuts. 42 */ 43 @PeopleNotificationType getPeopleNotificationTypenull44 fun getPeopleNotificationType(entry: NotificationEntry): Int 45 46 fun compareTo( 47 @PeopleNotificationType a: Int, 48 @PeopleNotificationType b: Int 49 ): Int 50 51 companion object { 52 53 @Retention(AnnotationRetention.SOURCE) 54 @IntDef(prefix = ["TYPE_"], value = [TYPE_NON_PERSON, TYPE_PERSON, TYPE_FULL_PERSON, 55 TYPE_IMPORTANT_PERSON]) 56 annotation class PeopleNotificationType 57 58 const val TYPE_NON_PERSON = 0 59 const val TYPE_PERSON = 1 60 const val TYPE_FULL_PERSON = 2 61 const val TYPE_IMPORTANT_PERSON = 3 62 } 63 } 64 65 @SysUISingleton 66 class PeopleNotificationIdentifierImpl @Inject constructor( 67 private val personExtractor: NotificationPersonExtractor, 68 private val groupManager: GroupMembershipManager 69 ) : PeopleNotificationIdentifier { 70 71 @PeopleNotificationType getPeopleNotificationTypenull72 override fun getPeopleNotificationType(entry: NotificationEntry): Int = 73 when (val type = entry.ranking.personTypeInfo) { 74 TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON 75 else -> { 76 when (val type = upperBound(type, extractPersonTypeInfo(entry.sbn))) { 77 TYPE_IMPORTANT_PERSON -> TYPE_IMPORTANT_PERSON 78 else -> upperBound(type, getPeopleTypeOfSummary(entry)) 79 } 80 } 81 } 82 compareTonull83 override fun compareTo( 84 @PeopleNotificationType a: Int, 85 @PeopleNotificationType b: Int 86 ): Int 87 { 88 return b.compareTo(a) 89 } 90 91 /** 92 * Given two [PeopleNotificationType]s, determine the upper bound. Used to constrain a 93 * notification to a type given multiple signals, i.e. notification groups, where each child 94 * has a [PeopleNotificationType] that is used to constrain the summary. 95 */ 96 @PeopleNotificationType upperBoundnull97 private fun upperBound( 98 @PeopleNotificationType type: Int, 99 @PeopleNotificationType other: Int 100 ): Int = 101 max(type, other) 102 103 private val Ranking.personTypeInfo 104 get() = when { 105 !isConversation -> TYPE_NON_PERSON 106 conversationShortcutInfo == null -> TYPE_PERSON 107 channel?.isImportantConversation == true -> TYPE_IMPORTANT_PERSON 108 else -> TYPE_FULL_PERSON 109 } 110 extractPersonTypeInfonull111 private fun extractPersonTypeInfo(sbn: StatusBarNotification) = 112 if (personExtractor.isPersonNotification(sbn)) TYPE_PERSON else TYPE_NON_PERSON 113 114 private fun getPeopleTypeOfSummary(entry: NotificationEntry): Int { 115 if (!groupManager.isGroupSummary(entry)) { 116 return TYPE_NON_PERSON 117 } 118 119 val childTypes = groupManager.getChildren(entry) 120 ?.asSequence() 121 ?.map { getPeopleNotificationType(it) } 122 ?: return TYPE_NON_PERSON 123 124 var groupType = TYPE_NON_PERSON 125 for (childType in childTypes) { 126 groupType = upperBound(groupType, childType) 127 if (groupType == TYPE_IMPORTANT_PERSON) 128 break 129 } 130 return groupType 131 } 132 } 133