diff options
5 files changed, 186 insertions, 1 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index e74ed8d27ec2..c487ff5d35bd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -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"; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt index 23da90d426c7..8edbc5e8e4bb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -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 } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt new file mode 100644 index 000000000000..41ee3b992c5a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt @@ -0,0 +1,119 @@ +/* + * 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 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt index 19a92a2230ba..a2b71551eca8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt @@ -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( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt index 32c582f79ed7..2ec801620212 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt @@ -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, ) |