Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt +60 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.data.repository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -29,13 +32,64 @@ import kotlinx.coroutines.flow.MutableStateFlow */ @SysUISingleton class ActiveNotificationListRepository @Inject constructor() { /** * Notifications actively presented to the user in the notification stack. * * @see com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener */ val activeNotifications = MutableStateFlow(emptyMap<String, ActiveNotificationModel>()) /** Notifications actively presented to the user in the notification list. */ val activeNotifications = MutableStateFlow(ActiveNotificationsStore()) /** Are any already-seen notifications currently filtered out of the active list? */ val hasFilteredOutSeenNotifications = MutableStateFlow(false) } /** Represents the notification list, comprised of groups and individual notifications. */ data class ActiveNotificationsStore( /** Notification groups, stored by key. */ val groups: Map<String, ActiveNotificationGroupModel> = emptyMap(), /** All individual notifications, including top-level and group children, stored by key. */ val individuals: Map<String, ActiveNotificationModel> = emptyMap(), /** * Ordered top-level list of entries in the notification list (either groups or individual), * represented as [Key]s. The associated [ActiveNotificationEntryModel] can be retrieved by * invoking [get]. */ val renderList: List<Key> = emptyList(), ) { operator fun get(key: Key): ActiveNotificationEntryModel? { return when (key) { is Key.Group -> groups[key.key] is Key.Individual -> individuals[key.key] } } /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */ sealed class Key { data class Individual(val key: String) : Key() data class Group(val key: String) : Key() } /** Mutable builder for an [ActiveNotificationsStore]. */ class Builder { private val groups = mutableMapOf<String, ActiveNotificationGroupModel>() private val individuals = mutableMapOf<String, ActiveNotificationModel>() private val renderList = mutableListOf<Key>() fun build() = ActiveNotificationsStore(groups, individuals, renderList) fun addEntry(entry: ActiveNotificationEntryModel) { when (entry) { is ActiveNotificationModel -> addIndividualNotif(entry) is ActiveNotificationGroupModel -> addNotifGroup(entry) } } fun addIndividualNotif(notif: ActiveNotificationModel) { renderList.add(Key.Individual(notif.key)) individuals[notif.key] = notif } fun addNotifGroup(group: ActiveNotificationGroupModel) { renderList.add(Key.Group(group.key)) groups[group.key] = group individuals[group.summary.key] = group.summary group.children.forEach { individuals[it.key] = it } } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +13 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.Flow Loading @@ -27,6 +28,16 @@ constructor( repository: ActiveNotificationListRepository, ) { /** Notifications actively presented to the user in the notification stack, in order. */ val notifications: Flow<Collection<ActiveNotificationModel>> = repository.activeNotifications.map { it.values } val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> = repository.activeNotifications.map { store -> store.renderList.map { key -> val entry = store[key] ?: error("Could not find notification with key $key in active notif store.") when (entry) { is ActiveNotificationGroupModel -> entry.summary is ActiveNotificationModel -> entry } } } } packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +143 −82 Original line number Diff line number Diff line Loading @@ -16,16 +16,18 @@ package com.android.systemui.statusbar.notification.domain.interactor import android.graphics.drawable.Icon import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.update private typealias ModelStore = Map<String, ActiveNotificationModel> /** * Logic for passing information from the * [com.android.systemui.statusbar.notification.collection.NotifPipeline] to the presentation Loading @@ -38,24 +40,61 @@ constructor( private val sectionStyleProvider: SectionStyleProvider, ) { /** * Sets the current list of rendered notification entries as displayed in the notification * stack. * * @see com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository.activeNotifications * Sets the current list of rendered notification entries as displayed in the notification list. */ fun setRenderedList(entries: List<ListEntry>) { repository.activeNotifications.update { existingModels -> entries .asSequence() .mapNotNull { it.representativeEntry } .associateBy( keySelector = { it.key }, valueTransform = { it.toModel(existingModels) }, buildActiveNotificationsStore(existingModels, sectionStyleProvider) { entries.forEach(::addListEntry) } } } } private fun buildActiveNotificationsStore( existingModels: ActiveNotificationsStore, sectionStyleProvider: SectionStyleProvider, block: ActiveNotificationsStoreBuilder.() -> Unit ): ActiveNotificationsStore = ActiveNotificationsStoreBuilder(existingModels, sectionStyleProvider).apply(block).build() private class ActiveNotificationsStoreBuilder( private val existingModels: ActiveNotificationsStore, private val sectionStyleProvider: SectionStyleProvider, ) { private val builder = ActiveNotificationsStore.Builder() fun build(): ActiveNotificationsStore = builder.build() /** * Convert a [ListEntry] into [ActiveNotificationEntryModel]s, and add them to the * [ActiveNotificationsStore]. Special care is taken to avoid re-allocating models if the result * would be identical to an existing model (at the expense of additional computations). */ fun addListEntry(entry: ListEntry) { when (entry) { is GroupEntry -> { entry.summary?.let { summary -> val summaryModel = summary.toModel() val childModels = entry.children.map { it.toModel() } builder.addNotifGroup( existingModels.createOrReuse( key = entry.key, summary = summaryModel, children = childModels ) ) } } else -> { entry.representativeEntry?.let { notifEntry -> builder.addIndividualNotif(notifEntry.toModel()) } } } } private fun NotificationEntry.toModel(existingModels: ModelStore): ActiveNotificationModel = private fun NotificationEntry.toModel(): ActiveNotificationModel = existingModels.createOrReuse( key = key, groupKey = sbn.groupKey, Loading @@ -69,8 +108,9 @@ constructor( shelfIcon = icons.shelfIcon?.sourceIcon, statusBarIcon = icons.statusBarIcon?.sourceIcon, ) } private fun ModelStore.createOrReuse( private fun ActiveNotificationsStore.createOrReuse( key: String, groupKey: String?, isAmbient: Boolean, Loading @@ -83,7 +123,7 @@ constructor( shelfIcon: Icon?, statusBarIcon: Icon? ): ActiveNotificationModel { return this[key]?.takeIf { return individuals[key]?.takeIf { it.isCurrent( key = key, groupKey = groupKey, Loading Loading @@ -141,4 +181,25 @@ constructor( else -> true } } private fun ActiveNotificationsStore.createOrReuse( key: String, summary: ActiveNotificationModel, children: List<ActiveNotificationModel>, ): ActiveNotificationGroupModel { return groups[key]?.takeIf { it.isCurrent(key, summary, children) } ?: ActiveNotificationGroupModel(key, summary, children) } private fun ActiveNotificationGroupModel.isCurrent( key: String, summary: ActiveNotificationModel, children: List<ActiveNotificationModel>, ): Boolean { return when { key != this.key -> false summary != this.summary -> false children != this.children -> false else -> true } } packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt +1 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,7 @@ constructor( showPulsing: Boolean = true, ): Flow<Set<ActiveNotificationModel>> { return combine( activeNotificationsInteractor.notifications, activeNotificationsInteractor.topLevelRepresentativeNotifications, keyguardViewStateRepository.areNotificationsFullyHidden, ) { notifications, notifsFullyHidden -> notifications Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ constructor( darkIconInteractor.tintAreas, darkIconInteractor.tintColor, // Included so that tints are re-applied after entries are changed. notificationsInteractor.notifications, notificationsInteractor.topLevelRepresentativeNotifications, ) { areas, tint, _ -> NotificationIconColorLookup { viewBounds: Rect -> if (DarkIconDispatcher.isInAreas(areas, viewBounds)) { Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt +60 −6 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.notification.data.repository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow Loading @@ -29,13 +32,64 @@ import kotlinx.coroutines.flow.MutableStateFlow */ @SysUISingleton class ActiveNotificationListRepository @Inject constructor() { /** * Notifications actively presented to the user in the notification stack. * * @see com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener */ val activeNotifications = MutableStateFlow(emptyMap<String, ActiveNotificationModel>()) /** Notifications actively presented to the user in the notification list. */ val activeNotifications = MutableStateFlow(ActiveNotificationsStore()) /** Are any already-seen notifications currently filtered out of the active list? */ val hasFilteredOutSeenNotifications = MutableStateFlow(false) } /** Represents the notification list, comprised of groups and individual notifications. */ data class ActiveNotificationsStore( /** Notification groups, stored by key. */ val groups: Map<String, ActiveNotificationGroupModel> = emptyMap(), /** All individual notifications, including top-level and group children, stored by key. */ val individuals: Map<String, ActiveNotificationModel> = emptyMap(), /** * Ordered top-level list of entries in the notification list (either groups or individual), * represented as [Key]s. The associated [ActiveNotificationEntryModel] can be retrieved by * invoking [get]. */ val renderList: List<Key> = emptyList(), ) { operator fun get(key: Key): ActiveNotificationEntryModel? { return when (key) { is Key.Group -> groups[key.key] is Key.Individual -> individuals[key.key] } } /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */ sealed class Key { data class Individual(val key: String) : Key() data class Group(val key: String) : Key() } /** Mutable builder for an [ActiveNotificationsStore]. */ class Builder { private val groups = mutableMapOf<String, ActiveNotificationGroupModel>() private val individuals = mutableMapOf<String, ActiveNotificationModel>() private val renderList = mutableListOf<Key>() fun build() = ActiveNotificationsStore(groups, individuals, renderList) fun addEntry(entry: ActiveNotificationEntryModel) { when (entry) { is ActiveNotificationModel -> addIndividualNotif(entry) is ActiveNotificationGroupModel -> addNotifGroup(entry) } } fun addIndividualNotif(notif: ActiveNotificationModel) { renderList.add(Key.Individual(notif.key)) individuals[notif.key] = notif } fun addNotifGroup(group: ActiveNotificationGroupModel) { renderList.add(Key.Group(group.key)) groups[group.key] = group individuals[group.summary.key] = group.summary group.children.forEach { individuals[it.key] = it } } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractor.kt +13 −2 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.domain.interactor import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.Flow Loading @@ -27,6 +28,16 @@ constructor( repository: ActiveNotificationListRepository, ) { /** Notifications actively presented to the user in the notification stack, in order. */ val notifications: Flow<Collection<ActiveNotificationModel>> = repository.activeNotifications.map { it.values } val topLevelRepresentativeNotifications: Flow<List<ActiveNotificationModel>> = repository.activeNotifications.map { store -> store.renderList.map { key -> val entry = store[key] ?: error("Could not find notification with key $key in active notif store.") when (entry) { is ActiveNotificationGroupModel -> entry.summary is ActiveNotificationModel -> entry } } } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +143 −82 Original line number Diff line number Diff line Loading @@ -16,16 +16,18 @@ package com.android.systemui.statusbar.notification.domain.interactor import android.graphics.drawable.Icon import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import javax.inject.Inject import kotlinx.coroutines.flow.update private typealias ModelStore = Map<String, ActiveNotificationModel> /** * Logic for passing information from the * [com.android.systemui.statusbar.notification.collection.NotifPipeline] to the presentation Loading @@ -38,24 +40,61 @@ constructor( private val sectionStyleProvider: SectionStyleProvider, ) { /** * Sets the current list of rendered notification entries as displayed in the notification * stack. * * @see com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository.activeNotifications * Sets the current list of rendered notification entries as displayed in the notification list. */ fun setRenderedList(entries: List<ListEntry>) { repository.activeNotifications.update { existingModels -> entries .asSequence() .mapNotNull { it.representativeEntry } .associateBy( keySelector = { it.key }, valueTransform = { it.toModel(existingModels) }, buildActiveNotificationsStore(existingModels, sectionStyleProvider) { entries.forEach(::addListEntry) } } } } private fun buildActiveNotificationsStore( existingModels: ActiveNotificationsStore, sectionStyleProvider: SectionStyleProvider, block: ActiveNotificationsStoreBuilder.() -> Unit ): ActiveNotificationsStore = ActiveNotificationsStoreBuilder(existingModels, sectionStyleProvider).apply(block).build() private class ActiveNotificationsStoreBuilder( private val existingModels: ActiveNotificationsStore, private val sectionStyleProvider: SectionStyleProvider, ) { private val builder = ActiveNotificationsStore.Builder() fun build(): ActiveNotificationsStore = builder.build() /** * Convert a [ListEntry] into [ActiveNotificationEntryModel]s, and add them to the * [ActiveNotificationsStore]. Special care is taken to avoid re-allocating models if the result * would be identical to an existing model (at the expense of additional computations). */ fun addListEntry(entry: ListEntry) { when (entry) { is GroupEntry -> { entry.summary?.let { summary -> val summaryModel = summary.toModel() val childModels = entry.children.map { it.toModel() } builder.addNotifGroup( existingModels.createOrReuse( key = entry.key, summary = summaryModel, children = childModels ) ) } } else -> { entry.representativeEntry?.let { notifEntry -> builder.addIndividualNotif(notifEntry.toModel()) } } } } private fun NotificationEntry.toModel(existingModels: ModelStore): ActiveNotificationModel = private fun NotificationEntry.toModel(): ActiveNotificationModel = existingModels.createOrReuse( key = key, groupKey = sbn.groupKey, Loading @@ -69,8 +108,9 @@ constructor( shelfIcon = icons.shelfIcon?.sourceIcon, statusBarIcon = icons.statusBarIcon?.sourceIcon, ) } private fun ModelStore.createOrReuse( private fun ActiveNotificationsStore.createOrReuse( key: String, groupKey: String?, isAmbient: Boolean, Loading @@ -83,7 +123,7 @@ constructor( shelfIcon: Icon?, statusBarIcon: Icon? ): ActiveNotificationModel { return this[key]?.takeIf { return individuals[key]?.takeIf { it.isCurrent( key = key, groupKey = groupKey, Loading Loading @@ -141,4 +181,25 @@ constructor( else -> true } } private fun ActiveNotificationsStore.createOrReuse( key: String, summary: ActiveNotificationModel, children: List<ActiveNotificationModel>, ): ActiveNotificationGroupModel { return groups[key]?.takeIf { it.isCurrent(key, summary, children) } ?: ActiveNotificationGroupModel(key, summary, children) } private fun ActiveNotificationGroupModel.isCurrent( key: String, summary: ActiveNotificationModel, children: List<ActiveNotificationModel>, ): Boolean { return when { key != this.key -> false summary != this.summary -> false children != this.children -> false else -> true } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractor.kt +1 −1 Original line number Diff line number Diff line Loading @@ -48,7 +48,7 @@ constructor( showPulsing: Boolean = true, ): Flow<Set<ActiveNotificationModel>> { return combine( activeNotificationsInteractor.notifications, activeNotificationsInteractor.topLevelRepresentativeNotifications, keyguardViewStateRepository.areNotificationsFullyHidden, ) { notifications, notifsFullyHidden -> notifications Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt +1 −1 Original line number Diff line number Diff line Loading @@ -61,7 +61,7 @@ constructor( darkIconInteractor.tintAreas, darkIconInteractor.tintColor, // Included so that tints are re-applied after entries are changed. notificationsInteractor.notifications, notificationsInteractor.topLevelRepresentativeNotifications, ) { areas, tint, _ -> NotificationIconColorLookup { viewBounds: Rect -> if (DarkIconDispatcher.isInAreas(areas, viewBounds)) { Loading