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

Commit fecaa4b2 authored by Jeff DeCew's avatar Jeff DeCew Committed by Android (Google) Code Review
Browse files

Merge "Notification minimalism prototype update" into main

parents f06ff25d 5f6a4ead
Loading
Loading
Loading
Loading
+35 −28
Original line number Original line Diff line number Diff line
@@ -18,33 +18,29 @@ package com.android.systemui.statusbar.notification


import android.content.Context
import android.content.Context
import android.provider.DeviceConfig
import android.provider.DeviceConfig

import com.android.internal.annotations.VisibleForTesting
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NOTIFICATIONS_USE_PEOPLE_FILTERING
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_ALERTING
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.BUCKET_HEADS_UP
import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS
import com.android.systemui.statusbar.notification.stack.BUCKET_MEDIA_CONTROLS
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.notification.stack.PriorityBucket
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.Utils
import com.android.systemui.util.Utils

import javax.inject.Inject
import javax.inject.Inject


private var sUsePeopleFiltering: Boolean? = null
private var sUsePeopleFiltering: Boolean? = null


/**
/** Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config. */
 * Feature controller for the NOTIFICATIONS_USE_PEOPLE_FILTERING config.
 */
@SysUISingleton
@SysUISingleton
class NotificationSectionsFeatureManager @Inject constructor(
class NotificationSectionsFeatureManager
    val proxy: DeviceConfigProxy,
@Inject
    val context: Context
constructor(val proxy: DeviceConfigProxy, val context: Context) {
) {


    fun isFilteringEnabled(): Boolean {
    fun isFilteringEnabled(): Boolean {
        return usePeopleFiltering(proxy)
        return usePeopleFiltering(proxy)
@@ -55,30 +51,37 @@ class NotificationSectionsFeatureManager @Inject constructor(
    }
    }


    fun getNotificationBuckets(): IntArray {
    fun getNotificationBuckets(): IntArray {
        if (PriorityPeopleSection.isEnabled) {
        if (PriorityPeopleSection.isEnabled || NotificationMinimalismPrototype.V2.isEnabled) {
            // We don't need this list to be adaptive, it can be the superset of all features.
            // We don't need this list to be adaptive, it can be the superset of all features.
            return intArrayOf(
            return PriorityBucket.getAllInOrder()
                    BUCKET_MEDIA_CONTROLS,
        }
        return when {
            isFilteringEnabled() && isMediaControlsEnabled() ->
                intArrayOf(
                    BUCKET_HEADS_UP,
                    BUCKET_HEADS_UP,
                    BUCKET_FOREGROUND_SERVICE,
                    BUCKET_FOREGROUND_SERVICE,
                    BUCKET_PRIORITY_PEOPLE,
                    BUCKET_MEDIA_CONTROLS,
                    BUCKET_PEOPLE,
                    BUCKET_PEOPLE,
                    BUCKET_ALERTING,
                    BUCKET_ALERTING,
                    BUCKET_SILENT,
                    BUCKET_SILENT
                )
                )
        }
        return when {
            isFilteringEnabled() && isMediaControlsEnabled() ->
                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
                        BUCKET_PEOPLE, BUCKET_ALERTING, BUCKET_SILENT)
            !isFilteringEnabled() && isMediaControlsEnabled() ->
            !isFilteringEnabled() && isMediaControlsEnabled() ->
                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_MEDIA_CONTROLS,
                intArrayOf(
                        BUCKET_ALERTING, BUCKET_SILENT)
                    BUCKET_HEADS_UP,
                    BUCKET_FOREGROUND_SERVICE,
                    BUCKET_MEDIA_CONTROLS,
                    BUCKET_ALERTING,
                    BUCKET_SILENT
                )
            isFilteringEnabled() && !isMediaControlsEnabled() ->
            isFilteringEnabled() && !isMediaControlsEnabled() ->
                intArrayOf(BUCKET_HEADS_UP, BUCKET_FOREGROUND_SERVICE, BUCKET_PEOPLE,
                intArrayOf(
                        BUCKET_ALERTING, BUCKET_SILENT)
                    BUCKET_HEADS_UP,
            else ->
                    BUCKET_FOREGROUND_SERVICE,
                intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
                    BUCKET_PEOPLE,
                    BUCKET_ALERTING,
                    BUCKET_SILENT
                )
            else -> intArrayOf(BUCKET_ALERTING, BUCKET_SILENT)
        }
        }
    }
    }


@@ -94,8 +97,12 @@ class NotificationSectionsFeatureManager @Inject constructor(


private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
private fun usePeopleFiltering(proxy: DeviceConfigProxy): Boolean {
    if (sUsePeopleFiltering == null) {
    if (sUsePeopleFiltering == null) {
        sUsePeopleFiltering = proxy.getBoolean(
        sUsePeopleFiltering =
                DeviceConfig.NAMESPACE_SYSTEMUI, NOTIFICATIONS_USE_PEOPLE_FILTERING, true)
            proxy.getBoolean(
                DeviceConfig.NAMESPACE_SYSTEMUI,
                NOTIFICATIONS_USE_PEOPLE_FILTERING,
                true
            )
    }
    }


    return sUsePeopleFiltering!!
    return sUsePeopleFiltering!!
+18 −13
Original line number Original line Diff line number Diff line
@@ -60,22 +60,27 @@ public class ColorizedFgsCoordinator implements Coordinator {
        public boolean isInSection(ListEntry entry) {
        public boolean isInSection(ListEntry entry) {
            NotificationEntry notificationEntry = entry.getRepresentativeEntry();
            NotificationEntry notificationEntry = entry.getRepresentativeEntry();
            if (notificationEntry != null) {
            if (notificationEntry != null) {
                return isColorizedForegroundService(notificationEntry) || isCall(notificationEntry);
                return isRichOngoing(notificationEntry);
            }
            }
            return false;
            return false;
        }
        }
    };

    /** Determines if the given notification is a colorized or call notification */
    public static boolean isRichOngoing(NotificationEntry entry) {
        return isColorizedForegroundService(entry) || isCall(entry);
    }


        private boolean isColorizedForegroundService(NotificationEntry entry) {
    private static boolean isColorizedForegroundService(NotificationEntry entry) {
        Notification notification = entry.getSbn().getNotification();
        Notification notification = entry.getSbn().getNotification();
        return notification.isForegroundService()
        return notification.isForegroundService()
                && notification.isColorized()
                && notification.isColorized()
                && entry.getImportance() > IMPORTANCE_MIN;
                && entry.getImportance() > IMPORTANCE_MIN;
    }
    }


        private boolean isCall(NotificationEntry entry) {
    private static boolean isCall(NotificationEntry entry) {
        Notification notification = entry.getSbn().getNotification();
        Notification notification = entry.getSbn().getNotification();
        return entry.getImportance() > IMPORTANCE_MIN
        return entry.getImportance() > IMPORTANCE_MIN
                && notification.isStyle(Notification.CallStyle.class);
                && notification.isStyle(Notification.CallStyle.class);
    }
    }
    };
}
}
+39 −18
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@


package com.android.systemui.statusbar.notification.collection.coordinator
package com.android.systemui.statusbar.notification.collection.coordinator


import android.app.NotificationManager
import android.os.UserHandle
import android.os.UserHandle
import android.provider.Settings
import android.provider.Settings
import androidx.annotation.VisibleForTesting
import androidx.annotation.VisibleForTesting
@@ -44,7 +45,8 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionHe
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.statusbar.notification.interruption.KeyguardNotificationVisibilityProvider
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype
import com.android.systemui.statusbar.notification.stack.BUCKET_FOREGROUND_SERVICE
import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_ONGOING
import com.android.systemui.statusbar.notification.stack.BUCKET_TOP_UNSEEN
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.HeadsUpManager
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.util.asIndenting
import com.android.systemui.util.asIndenting
@@ -113,7 +115,7 @@ constructor(
    private fun attachUnseenFilter(pipeline: NotifPipeline) {
    private fun attachUnseenFilter(pipeline: NotifPipeline) {
        if (NotificationMinimalismPrototype.V2.isEnabled) {
        if (NotificationMinimalismPrototype.V2.isEnabled) {
            pipeline.addPromoter(unseenNotifPromoter)
            pipeline.addPromoter(unseenNotifPromoter)
            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotif)
            pipeline.addOnBeforeTransformGroupsListener(::pickOutTopUnseenNotifs)
        }
        }
        pipeline.addFinalizeFilter(unseenNotifFilter)
        pipeline.addFinalizeFilter(unseenNotifFilter)
        pipeline.addCollectionListener(collectionListener)
        pipeline.addCollectionListener(collectionListener)
@@ -347,15 +349,16 @@ constructor(
            }
            }
        }
        }


    private fun pickOutTopUnseenNotif(list: List<ListEntry>) {
    private fun pickOutTopUnseenNotifs(list: List<ListEntry>) {
        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
        if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return
        // Only ever elevate a top unseen notification on keyguard, not even locked shade
        // Only ever elevate a top unseen notification on keyguard, not even locked shade
        if (statusBarStateController.state != StatusBarState.KEYGUARD) {
        if (statusBarStateController.state != StatusBarState.KEYGUARD) {
            seenNotificationsInteractor.setTopOngoingNotification(null)
            seenNotificationsInteractor.setTopUnseenNotification(null)
            seenNotificationsInteractor.setTopUnseenNotification(null)
            return
            return
        }
        }
        // On keyguard pick the top-ranked unseen or ongoing notification to elevate
        // On keyguard pick the top-ranked unseen or ongoing notification to elevate
        seenNotificationsInteractor.setTopUnseenNotification(
        val nonSummaryEntries: Sequence<NotificationEntry> =
            list
            list
                .asSequence()
                .asSequence()
                .flatMap {
                .flatMap {
@@ -365,7 +368,15 @@ constructor(
                        else -> error("unhandled type of $it")
                        else -> error("unhandled type of $it")
                    }
                    }
                }
                }
                .filter { shouldIgnoreUnseenCheck(it) || it in unseenNotifications }
                .filter { it.importance >= NotificationManager.IMPORTANCE_DEFAULT }
        seenNotificationsInteractor.setTopOngoingNotification(
            nonSummaryEntries
                .filter { ColorizedFgsCoordinator.isRichOngoing(it) }
                .minByOrNull { it.ranking.rank }
        )
        seenNotificationsInteractor.setTopUnseenNotification(
            nonSummaryEntries
                .filter { !ColorizedFgsCoordinator.isRichOngoing(it) && it in unseenNotifications }
                .minByOrNull { it.ranking.rank }
                .minByOrNull { it.ranking.rank }
        )
        )
    }
    }
@@ -375,29 +386,39 @@ constructor(
        object : NotifPromoter("$TAG-unseen") {
        object : NotifPromoter("$TAG-unseen") {
            override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
            override fun shouldPromoteToTopLevel(child: NotificationEntry): Boolean =
                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) false
                else if (!NotificationMinimalismPrototype.V2.ungroupTopUnseen) false
                else
                else
                    seenNotificationsInteractor.isTopUnseenNotification(child) &&
                    seenNotificationsInteractor.isTopOngoingNotification(child) ||
                        NotificationMinimalismPrototype.V2.ungroupTopUnseen
                        seenNotificationsInteractor.isTopUnseenNotification(child)
        }
        }


    val unseenNotifSectioner =
    val topOngoingSectioner =
        object : NotifSectioner("Unseen", BUCKET_FOREGROUND_SERVICE) {
        object : NotifSectioner("TopOngoing", BUCKET_TOP_ONGOING) {
            override fun isInSection(entry: ListEntry): Boolean {
            override fun isInSection(entry: ListEntry): Boolean {
                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
                if (
                return entry.anyEntry { notificationEntry ->
                    seenNotificationsInteractor.isTopUnseenNotification(entry.representativeEntry)
                    seenNotificationsInteractor.isTopOngoingNotification(notificationEntry)
                ) {
                    return true
                }
                }
                if (entry !is GroupEntry) {
                    return false
            }
            }
                return entry.children.any {
        }
                    seenNotificationsInteractor.isTopUnseenNotification(it)

    val topUnseenSectioner =
        object : NotifSectioner("TopUnseen", BUCKET_TOP_UNSEEN) {
            override fun isInSection(entry: ListEntry): Boolean {
                if (NotificationMinimalismPrototype.V2.isUnexpectedlyInLegacyMode()) return false
                return entry.anyEntry { notificationEntry ->
                    seenNotificationsInteractor.isTopUnseenNotification(notificationEntry)
                }
                }
            }
            }
        }
        }


    private fun ListEntry.anyEntry(predicate: (NotificationEntry?) -> Boolean) =
        when {
            predicate(representativeEntry) -> true
            this !is GroupEntry -> false
            else -> children.any(predicate)
        }

    @VisibleForTesting
    @VisibleForTesting
    internal val unseenNotifFilter =
    internal val unseenNotifFilter =
        object : NotifFilter("$TAG-unseen") {
        object : NotifFilter("$TAG-unseen") {
+5 −2
Original line number Original line Diff line number Diff line
@@ -116,11 +116,14 @@ constructor(
        }
        }


        // Manually add Ordered Sections
        // Manually add Ordered Sections
        if (NotificationMinimalismPrototype.V2.isEnabled) {
            mOrderedSections.add(keyguardCoordinator.topOngoingSectioner) // Top Ongoing
        }
        mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
        mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
        mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
        if (NotificationMinimalismPrototype.V2.isEnabled) {
        if (NotificationMinimalismPrototype.V2.isEnabled) {
            mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS)
            mOrderedSections.add(keyguardCoordinator.topUnseenSectioner) // Top Unseen
        }
        }
        mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
        if (PriorityPeopleSection.isEnabled) {
        if (PriorityPeopleSection.isEnabled) {
            mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People
            mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People
        }
        }
+4 −0
Original line number Original line Diff line number Diff line
@@ -42,6 +42,9 @@ class ActiveNotificationListRepository @Inject constructor() {
    /** Stats about the list of notifications attached to the shade */
    /** Stats about the list of notifications attached to the shade */
    val notifStats = MutableStateFlow(NotifStats.empty)
    val notifStats = MutableStateFlow(NotifStats.empty)


    /** The key of the top ongoing notification */
    val topOngoingNotificationKey = MutableStateFlow<String?>(null)

    /** The key of the top unseen notification */
    /** The key of the top unseen notification */
    val topUnseenNotificationKey = MutableStateFlow<String?>(null)
    val topUnseenNotificationKey = MutableStateFlow<String?>(null)
}
}
@@ -75,6 +78,7 @@ data class ActiveNotificationsStore(
    /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */
    /** Unique key identifying an [ActiveNotificationEntryModel] in the store. */
    sealed class Key {
    sealed class Key {
        data class Individual(val key: String) : Key()
        data class Individual(val key: String) : Key()

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


Loading