Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9488420e authored by Steve Elliott's avatar Steve Elliott Committed by Android (Google) Code Review
Browse files

Merge "Preserve notif group stucture in repository" into main

parents 0e020e5c 01bcfa46
Loading
Loading
Loading
Loading
+60 −6
Original line number Diff line number Diff line
@@ -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
@@ -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 }
        }
    }
}
+13 −2
Original line number Diff line number Diff line
@@ -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
@@ -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
                }
            }
        }
}
+143 −82
Original line number Diff line number Diff line
@@ -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
@@ -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,
@@ -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,
@@ -83,7 +123,7 @@ constructor(
    shelfIcon: Icon?,
    statusBarIcon: Icon?
): ActiveNotificationModel {
        return this[key]?.takeIf {
    return individuals[key]?.takeIf {
        it.isCurrent(
            key = key,
            groupKey = groupKey,
@@ -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
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ constructor(
        showPulsing: Boolean = true,
    ): Flow<Set<ActiveNotificationModel>> {
        return combine(
            activeNotificationsInteractor.notifications,
            activeNotificationsInteractor.topLevelRepresentativeNotifications,
            keyguardViewStateRepository.areNotificationsFullyHidden,
        ) { notifications, notifsFullyHidden ->
            notifications
+1 −1
Original line number Diff line number Diff line
@@ -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