Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +34 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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"; } packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -173,6 +174,7 @@ private class ActiveNotificationsStoreBuilder( isGroupSummary = sbn.notification.isGroupSummary, bucket = bucket, callType = sbn.toCallType(), promotedContent = promotedNotificationContentModel, ) } } Loading @@ -199,6 +201,7 @@ private fun ActiveNotificationsStore.createOrReuse( isGroupSummary: Boolean, bucket: Int, callType: CallType, promotedContent: PromotedNotificationContentModel?, ): ActiveNotificationModel { return individuals[key]?.takeIf { it.isCurrent( Loading @@ -223,6 +226,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, promotedContent = promotedContent, ) } ?: ActiveNotificationModel( Loading @@ -247,6 +251,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, promotedContent = promotedContent, ) } Loading @@ -272,6 +277,7 @@ private fun ActiveNotificationModel.isCurrent( isGroupSummary: Boolean, bucket: Int, callType: CallType, promotedContent: PromotedNotificationContentModel?, ): Boolean { return when { key != this.key -> false Loading @@ -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 } } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt 0 → 100644 +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 } } packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +21 −1 Original line number Diff line number Diff line Loading @@ -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 /** Loading @@ -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)? */ Loading Loading @@ -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( Loading packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -46,6 +47,7 @@ fun activeNotificationModel( contentIntent: PendingIntent? = null, bucket: Int = BUCKET_UNKNOWN, callType: CallType = CallType.None, promotedContent: PromotedNotificationContentModel? = null, ) = ActiveNotificationModel( key = key, Loading @@ -69,4 +71,5 @@ fun activeNotificationModel( isGroupSummary = isGroupSummary, bucket = bucket, callType = callType, promotedContent = promotedContent, ) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +34 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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 Loading Loading @@ -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 { Loading Loading @@ -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"; }
packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +9 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -173,6 +174,7 @@ private class ActiveNotificationsStoreBuilder( isGroupSummary = sbn.notification.isGroupSummary, bucket = bucket, callType = sbn.toCallType(), promotedContent = promotedNotificationContentModel, ) } } Loading @@ -199,6 +201,7 @@ private fun ActiveNotificationsStore.createOrReuse( isGroupSummary: Boolean, bucket: Int, callType: CallType, promotedContent: PromotedNotificationContentModel?, ): ActiveNotificationModel { return individuals[key]?.takeIf { it.isCurrent( Loading @@ -223,6 +226,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, promotedContent = promotedContent, ) } ?: ActiveNotificationModel( Loading @@ -247,6 +251,7 @@ private fun ActiveNotificationsStore.createOrReuse( contentIntent = contentIntent, bucket = bucket, callType = callType, promotedContent = promotedContent, ) } Loading @@ -272,6 +277,7 @@ private fun ActiveNotificationModel.isCurrent( isGroupSummary: Boolean, bucket: Int, callType: CallType, promotedContent: PromotedNotificationContentModel?, ): Boolean { return when { key != this.key -> false Loading @@ -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 } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt 0 → 100644 +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 } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +21 −1 Original line number Diff line number Diff line Loading @@ -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 /** Loading @@ -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)? */ Loading Loading @@ -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( Loading
packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +3 −0 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -46,6 +47,7 @@ fun activeNotificationModel( contentIntent: PendingIntent? = null, bucket: Int = BUCKET_UNKNOWN, callType: CallType = CallType.None, promotedContent: PromotedNotificationContentModel? = null, ) = ActiveNotificationModel( key = key, Loading @@ -69,4 +71,5 @@ fun activeNotificationModel( isGroupSummary = isGroupSummary, bucket = bucket, callType = callType, promotedContent = promotedContent, )