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