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

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

Merge "model bundles in ActiveNotificationListRepository" into main

parents a0eaad40 a09e15cc
Loading
Loading
Loading
Loading
+49 −8
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ package com.android.systemui.statusbar.notification.data.repository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.data.model.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key
import com.android.systemui.statusbar.notification.shared.ActiveBundleModel
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 com.android.systemui.statusbar.notification.shared.ActivePipelineEntryModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow

@@ -51,14 +53,16 @@ class ActiveNotificationListRepository @Inject constructor() {

/** Represents the notification list, comprised of groups and individual notifications. */
data class ActiveNotificationsStore(
    /** Notification bundles, stored by key. */
    val bundles: Map<String, ActiveBundleModel> = emptyMap(),
    /** 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].
     * represented as [Key]s. The associated [ActivePipelineEntryModel] can be retrieved by invoking
     * [get].
     */
    val renderList: List<Key> = emptyList(),

@@ -68,30 +72,42 @@ data class ActiveNotificationsStore(
     */
    val rankingsMap: Map<String, Int> = emptyMap(),
) {
    operator fun get(key: Key): ActiveNotificationEntryModel? {
    operator fun get(key: Key): ActivePipelineEntryModel? {
        return when (key) {
            is Key.Bundle -> bundles[key.key]
            is Key.Group -> groups[key.key]
            is Key.Individual -> individuals[key.key]
        }
    }

    /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */
    /** Unique key identifying an [ActivePipelineEntryModel] in the store. */
    sealed class Key {
        data class Individual(val key: String) : Key()

        data class Group(val key: String) : Key()

        data class Bundle(val key: String) : Key()
    }

    /** Mutable builder for an [ActiveNotificationsStore]. */
    class Builder {
        private val bundles = mutableMapOf<String, ActiveBundleModel>()
        private val groups = mutableMapOf<String, ActiveNotificationGroupModel>()
        private val individuals = mutableMapOf<String, ActiveNotificationModel>()
        private val renderList = mutableListOf<Key>()
        private var rankingsMap: Map<String, Int> = emptyMap()

        fun build() = ActiveNotificationsStore(groups, individuals, renderList, rankingsMap)
        fun build() =
            ActiveNotificationsStore(bundles, groups, individuals, renderList, rankingsMap)

        fun addNotifEntry(entry: ActivePipelineEntryModel) {
            when (entry) {
                is ActiveBundleModel -> addBundle(entry)
                is ActiveNotificationEntryModel -> addNotifEntry(entry)
            }
        }

        fun addEntry(entry: ActiveNotificationEntryModel) {
        fun addNotifEntry(entry: ActiveNotificationEntryModel) {
            when (entry) {
                is ActiveNotificationModel -> addIndividualNotif(entry)
                is ActiveNotificationGroupModel -> addNotifGroup(entry)
@@ -100,14 +116,39 @@ data class ActiveNotificationsStore(

        fun addIndividualNotif(notif: ActiveNotificationModel) {
            renderList.add(Key.Individual(notif.key))
            individuals[notif.key] = notif
            trackIndividualNotif(notif)
        }

        fun addNotifGroup(group: ActiveNotificationGroupModel) {
            renderList.add(Key.Group(group.key))
            trackNotifGroup(group)
        }

        fun addBundle(bundle: ActiveBundleModel) {
            renderList.add(Key.Bundle(bundle.key))
            trackBundle(bundle)
        }

        private fun trackNotifEntry(entry: ActiveNotificationEntryModel) {
            when (entry) {
                is ActiveNotificationGroupModel -> trackNotifGroup(entry)
                is ActiveNotificationModel -> trackIndividualNotif(entry)
            }
        }

        private fun trackIndividualNotif(notif: ActiveNotificationModel) {
            individuals[notif.key] = notif
        }

        private fun trackNotifGroup(group: ActiveNotificationGroupModel) {
            groups[group.key] = group
            individuals[group.summary.key] = group.summary
            group.children.forEach { individuals[it.key] = it }
            group.children.forEach { trackIndividualNotif(it) }
        }

        private fun trackBundle(bundle: ActiveBundleModel) {
            bundles[bundle.key] = bundle
            bundle.children.forEach { child -> trackNotifEntry(child) }
        }

        fun setRankingsMap(map: Map<String, Int>) {
+30 −15
Original line number Diff line number Diff line
@@ -19,9 +19,12 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.notification.data.model.NotifStats
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.shared.ActiveBundleModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.ActivePipelineEntryModel
import com.android.systemui.statusbar.notification.shared.CallType
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -39,24 +42,14 @@ constructor(
    @Background private val backgroundDispatcher: CoroutineDispatcher,
) {
    /**
     * Top level list of Notifications actively presented to the user in the notification stack, in
     * order.
     * List of top-level entries in the notification stack that are backed by a notification.
     *
     * This omits bundles and bundled notifications; the bundle is top-level, and it is not backed
     * by a notification.
     */
    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
                    }
                }
            }
            .map { store -> topLevelRepresentativeModels(store) }
            .flowOn(backgroundDispatcher)

    /**
@@ -172,6 +165,28 @@ constructor(
        repository.notifStats.value = notifStats
    }

    /**
     * Returns the representative model for each top-level entry in the [store]. By definition, this
     * will omit bundles, which are always top-level and do not have a representative entry.
     */
    private fun topLevelRepresentativeModels(
        store: ActiveNotificationsStore
    ): List<ActiveNotificationModel> =
        store.renderList.mapNotNull { key -> representativeModelForKey(store, key) }

    private fun representativeModelForKey(
        store: ActiveNotificationsStore,
        key: ActiveNotificationsStore.Key,
    ): ActiveNotificationModel? {
        val entry: ActivePipelineEntryModel =
            store[key] ?: error("Could not find entry with key=$key in active notif store.")
        return when (entry) {
            is ActiveNotificationGroupModel -> entry.summary
            is ActiveNotificationModel -> entry
            is ActiveBundleModel -> null
        }
    }

    companion object {
        fun ActiveNotificationModel.isOngoingCallNotification() = this.callType == CallType.Ongoing
    }
+71 −26
Original line number Diff line number Diff line
@@ -40,6 +40,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific
import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels
import com.android.systemui.statusbar.notification.shared.ActiveBundleModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -100,35 +101,31 @@ private class ActiveNotificationsStoreBuilder(
     */
    fun addPipelineEntry(entry: PipelineEntry) {
        when (entry) {
            is BundleEntry -> {
                // TODO(b/410815667): Handle BundleEntry
            }
            is ListEntry -> {
                addListEntry(entry)
            }
            is BundleEntry -> addBundleEntry(entry)
            is ListEntry -> addListEntry(entry)
        }
    }

    private 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,
                        )
                    )
            is GroupEntry -> addGroupEntry(entry)
            is NotificationEntry -> addNotificationEntry(entry)
        }
    }

            is NotificationEntry -> {
                builder.addIndividualNotif(entry.toModel())
    private fun addBundleEntry(entry: BundleEntry) {
        val childModels = entry.children.mapNotNull { it.toModel() }
        builder.addBundle(
            existingModels.createOrReuseBundle(key = entry.key, children = childModels)
        )
    }

    private fun addGroupEntry(entry: GroupEntry) {
        entry.toModel()?.let { builder.addNotifGroup(it) }
    }

    private fun addNotificationEntry(entry: NotificationEntry) {
        builder.addIndividualNotif(entry.toModel())
    }

    fun setRankingsMap(entries: List<PipelineEntry>) {
@@ -165,6 +162,24 @@ private class ActiveNotificationsStoreBuilder(
        }
    }

    private fun ListEntry.toModel(): ActiveNotificationEntryModel? =
        when (this) {
            is GroupEntry -> toModel()
            is NotificationEntry -> toModel()
            else -> null
        }

    private fun GroupEntry.toModel(): ActiveNotificationGroupModel? =
        summary?.let { summary ->
            val summaryModel = summary.toModel()
            val childModels = children.map { it.toModel() }
            existingModels.createOrReuseGroup(
                key = key,
                summary = summaryModel,
                children = childModels,
            )
        }

    private fun NotificationEntry.toModel(): ActiveNotificationModel {
        val promotedContent =
            if (PromotedNotificationContentModel.featureFlagEnabled()) {
@@ -173,7 +188,7 @@ private class ActiveNotificationsStoreBuilder(
                null
            }

        return existingModels.createOrReuse(
        return existingModels.createOrReuseNotif(
            key = key,
            groupKey = sbn.groupKey,
            whenTime = sbn.notification.`when`,
@@ -202,7 +217,7 @@ private class ActiveNotificationsStoreBuilder(
    }
}

private fun ActiveNotificationsStore.createOrReuse(
private fun ActiveNotificationsStore.createOrReuseNotif(
    key: String,
    groupKey: String?,
    whenTime: Long,
@@ -341,7 +356,7 @@ private fun ActiveNotificationModel.isCurrent(
    }
}

private fun ActiveNotificationsStore.createOrReuse(
private fun ActiveNotificationsStore.createOrReuseGroup(
    key: String,
    summary: ActiveNotificationModel,
    children: List<ActiveNotificationModel>,
@@ -372,3 +387,33 @@ private fun StatusBarNotification.toCallType(): CallType =
        CALL_TYPE_UNKNOWN -> CallType.Unknown
        else -> CallType.Unknown
    }

private fun ActiveNotificationsStore.createOrReuseBundle(
    key: String,
    children: List<ActiveNotificationEntryModel>,
): ActiveBundleModel {
    return bundles[key]?.takeIf { it.isCurrent(key, children) } ?: ActiveBundleModel(key, children)
}

private fun ActiveBundleModel.isCurrent(
    key: String,
    children: List<ActiveNotificationEntryModel>,
): Boolean {
    return when {
        key != this.key -> false
        !hasSameInstances(children, this.children) -> false
        else -> true
    }
}

private fun hasSameInstances(list1: List<*>, list2: List<*>): Boolean {
    if (list1.size != list2.size) {
        return false
    }
    for (i in list1.indices) {
        if (list1[i] !== list2[i]) {
            return false
        }
    }
    return true
}
+12 −1
Original line number Diff line number Diff line
@@ -26,9 +26,20 @@ import com.android.systemui.statusbar.notification.stack.PriorityBucket

/**
 * Model for a top-level "entry" in the notification list, either an
 * [individual notification][ActiveNotificationModel], a [group][ActiveNotificationGroupModel], or a
 * [bundle][ActiveBundleModel].
 */
sealed class ActivePipelineEntryModel

/** Model for a bundle of notifications. */
data class ActiveBundleModel(val key: String, val children: List<ActiveNotificationEntryModel>) :
    ActivePipelineEntryModel()

/**
 * Model for a notification-backed "entry" in the notification list, either an
 * [individual notification][ActiveNotificationModel], or a [group][ActiveNotificationGroupModel].
 */
sealed class ActiveNotificationEntryModel
sealed class ActiveNotificationEntryModel : ActivePipelineEntryModel()

/**
 * Model for an individual notification in the notification list. These can appear as either an
+1 −1
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ fun ActiveNotificationListRepository.setActiveNotifs(count: Int) {
                val rankingsMap = mutableMapOf<String, Int>()
                repeat(count) { i ->
                    val key = "$i"
                    addEntry(activeNotificationModel(key = key))
                    addNotifEntry(activeNotificationModel(key = key))
                    rankingsMap[key] = i
                }