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

Commit f2ff8bc7 authored by Julia Tuttle's avatar Julia Tuttle
Browse files

Add and plumb PromotedNotificationContentModel

This will be used by both the AOD skeleton RON view and the status bar
chips for RONs.

Bug: 369151941
Test: builds
Flag: android.app.ui_rich_ongoing
Flag: com.android.systemui.status_bar_notification_chips
Change-Id: I740c136e67fbbd80e73ee3a8b83ae8e73ca3a990
parent bbcbceee
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
@@ -49,6 +49,7 @@ import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.ContentInfo;

import androidx.annotation.NonNull;
@@ -65,6 +66,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
import com.android.systemui.statusbar.notification.icon.IconPack;
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotificationGuts;
@@ -194,6 +196,10 @@ public final class NotificationEntry extends ListEntry {
     */
    private boolean mIsDemoted = false;

    // TODO(b/377565433): Move into NotificationContentModel during/after
    //  NotificationRowContentBinderRefactor.
    private PromotedNotificationContentModel mPromotedNotificationContentModel;

    /**
     * True if both
     *  1) app provided full screen intent but does not have the permission to send it
@@ -1061,6 +1067,32 @@ public final class NotificationEntry extends ListEntry {
        this.mHeadsUpStatusBarTextPublic.setValue(headsUpStatusBarModel.getPublicText());
    }

    /**
     * Gets the content needed to render this notification as a promoted notification on various
     * surfaces (like status bar chips and AOD).
     */
    public PromotedNotificationContentModel getPromotedNotificationContentModel() {
        if (PromotedNotificationContentModel.featureFlagEnabled()) {
            return mPromotedNotificationContentModel;
        } else {
            Log.wtf(TAG, "getting promoted content without feature flag enabled");
            return null;
        }
    }

    /**
     * Sets the content needed to render this notification as a promoted notification on various
     * surfaces (like status bar chips and AOD).
     */
    public void setPromotedNotificationContentModel(
            @Nullable PromotedNotificationContentModel promotedNotificationContentModel) {
        if (PromotedNotificationContentModel.featureFlagEnabled()) {
            this.mPromotedNotificationContentModel = promotedNotificationContentModel;
        } else {
            Log.wtf(TAG, "setting promoted content without feature flag enabled");
        }
    }

    /** Information about a suggestion that is being edited. */
    public static class EditedSuggestionInfo {

@@ -1101,4 +1133,6 @@ public final class NotificationEntry extends ListEntry {
    private static final long INITIALIZATION_DELAY = 400;
    private static final long NOT_LAUNCHED_YET = -LAUNCH_COOLDOWN;
    private static final int COLOR_INVALID = 1;

    private static final String TAG = "NotificationEntry";
}
+9 −0
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.notification.collection.provider.SectionSt
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.PromotedNotificationsProvider
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
@@ -173,6 +174,7 @@ private class ActiveNotificationsStoreBuilder(
            isGroupSummary = sbn.notification.isGroupSummary,
            bucket = bucket,
            callType = sbn.toCallType(),
            promotedContent = promotedNotificationContentModel,
        )
    }
}
@@ -199,6 +201,7 @@ private fun ActiveNotificationsStore.createOrReuse(
    isGroupSummary: Boolean,
    bucket: Int,
    callType: CallType,
    promotedContent: PromotedNotificationContentModel?,
): ActiveNotificationModel {
    return individuals[key]?.takeIf {
        it.isCurrent(
@@ -223,6 +226,7 @@ private fun ActiveNotificationsStore.createOrReuse(
            contentIntent = contentIntent,
            bucket = bucket,
            callType = callType,
            promotedContent = promotedContent,
        )
    }
        ?: ActiveNotificationModel(
@@ -247,6 +251,7 @@ private fun ActiveNotificationsStore.createOrReuse(
            contentIntent = contentIntent,
            bucket = bucket,
            callType = callType,
            promotedContent = promotedContent,
        )
}

@@ -272,6 +277,7 @@ private fun ActiveNotificationModel.isCurrent(
    isGroupSummary: Boolean,
    bucket: Int,
    callType: CallType,
    promotedContent: PromotedNotificationContentModel?,
): Boolean {
    return when {
        key != this.key -> false
@@ -295,6 +301,9 @@ private fun ActiveNotificationModel.isCurrent(
        contentIntent != this.contentIntent -> false
        bucket != this.bucket -> false
        callType != this.callType -> false
        // QQQ: Do we need to do the same `isCurrent` thing within the content model to avoid
        // recreating the active notification model constantly?
        promotedContent != this.promotedContent -> false
        else -> true
    }
}
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.statusbar.notification.promoted.shared.model

import android.annotation.DrawableRes
import android.graphics.drawable.Icon
import com.android.internal.widget.NotificationProgressModel
import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi

/**
 * The content needed to render a promoted notification to surfaces besides the notification stack,
 * like the skeleton view on AOD or the status bar chip.
 */
data class PromotedNotificationContentModel(
    val key: String,

    // for all styles:
    val skeletonSmallIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
    val appName: CharSequence?,
    val subText: CharSequence?,
    val time: When?,
    val lastAudiblyAlertedMs: Long,
    @DrawableRes val profileBadgeResId: Int?,
    val title: CharSequence?,
    val text: CharSequence?,
    val skeletonLargeIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
    val style: Style,

    // for CallStyle:
    val personIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
    val personName: CharSequence?,
    val verificationIcon: Icon?, // TODO(b/377568176): Make into an IconModel.
    val verificationText: CharSequence?,

    // for ProgressStyle:
    val progress: NotificationProgressModel?,
) {
    class Builder(val key: String) {
        var skeletonSmallIcon: Icon? = null
        var appName: CharSequence? = null
        var subText: CharSequence? = null
        var time: When? = null
        var lastAudiblyAlertedMs: Long = 0L
        @DrawableRes var profileBadgeResId: Int? = null
        var title: CharSequence? = null
        var text: CharSequence? = null
        var skeletonLargeIcon: Icon? = null
        var style: Style = Style.Ineligible

        // for CallStyle:
        var personIcon: Icon? = null
        var personName: CharSequence? = null
        var verificationIcon: Icon? = null
        var verificationText: CharSequence? = null

        // for ProgressStyle:
        var progress: NotificationProgressModel? = null

        fun build() =
            PromotedNotificationContentModel(
                key = key,
                skeletonSmallIcon = skeletonSmallIcon,
                appName = appName,
                subText = subText,
                time = time,
                lastAudiblyAlertedMs = lastAudiblyAlertedMs,
                profileBadgeResId = profileBadgeResId,
                title = title,
                text = text,
                skeletonLargeIcon = skeletonLargeIcon,
                style = style,
                personIcon = personIcon,
                personName = personName,
                verificationIcon = verificationIcon,
                verificationText = verificationText,
                progress = progress,
            )
    }

    /** The timestamp associated with a notification, along with the mode used to display it. */
    data class When(val time: Long, val mode: Mode) {
        /** The mode used to display a notification's `when` value. */
        enum class Mode {
            Absolute,
            CountDown,
            CountUp,
        }
    }

    /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */
    enum class Style {
        BigPicture,
        BigText,
        Call,
        Progress,
        Ineligible,
    }

    companion object {
        @JvmStatic
        fun featureFlagEnabled(): Boolean =
            PromotedNotificationUi.isEnabled || StatusBarNotifChips.isEnabled
    }
}
+21 −1
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@ package com.android.systemui.statusbar.notification.shared

import android.app.PendingIntent
import android.graphics.drawable.Icon
import android.util.Log
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.stack.PriorityBucket

/**
@@ -36,6 +38,7 @@ data class ActiveNotificationModel(
    val groupKey: String?,
    /** When this notification was posted. */
    val whenTime: Long,
    // TODO(b/377566661): Make isPromoted just check if promotedContent != null.
    /** True if this notification should be promoted and false otherwise. */
    val isPromoted: Boolean,
    /** Is this entry in the ambient / minimized section (lowest priority)? */
@@ -78,7 +81,24 @@ data class ActiveNotificationModel(
    @PriorityBucket val bucket: Int,
    /** The call type set on the notification. */
    val callType: CallType,
) : ActiveNotificationEntryModel()
    /**
     * The content needed to render this as a promoted notification on various surfaces, or null if
     * this notification cannot be rendered as a promoted notification.
     */
    val promotedContent: PromotedNotificationContentModel?,
) : ActiveNotificationEntryModel() {
    init {
        if (!PromotedNotificationContentModel.featureFlagEnabled()) {
            if (promotedContent != null) {
                Log.wtf(TAG, "passing non-null promoted content without feature flag enabled")
            }
        }
    }

    companion object {
        private const val TAG = "ActiveNotificationEntryModel"
    }
}

/** Model for a group of notifications. */
data class ActiveNotificationGroupModel(
+3 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.data.model
import android.app.PendingIntent
import android.graphics.drawable.Icon
import com.android.systemui.statusbar.StatusBarIconView
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
import com.android.systemui.statusbar.notification.shared.CallType
import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN
@@ -46,6 +47,7 @@ fun activeNotificationModel(
    contentIntent: PendingIntent? = null,
    bucket: Int = BUCKET_UNKNOWN,
    callType: CallType = CallType.None,
    promotedContent: PromotedNotificationContentModel? = null,
) =
    ActiveNotificationModel(
        key = key,
@@ -69,4 +71,5 @@ fun activeNotificationModel(
        isGroupSummary = isGroupSummary,
        bucket = bucket,
        callType = callType,
        promotedContent = promotedContent,
    )