Loading packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -2050,3 +2050,13 @@ flag { description: "Enables MinMode feature" bug: "411746510" } flag { name: "ui_rich_ongoing_aod_skeleton_bg_inflation" namespace: "systemui" description: "Offload AOD Skeleton inflation to the background thread" bug: "416714129" metadata { purpose: PURPOSE_BUGFIX } } packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +1 −0 Original line number Diff line number Diff line Loading @@ -654,6 +654,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { redactionType, imageModelProvider, context, context, ) } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +1 −1 Original line number Diff line number Diff line Loading @@ -327,7 +327,7 @@ public interface NotificationsModule { return implProvider.get(); } else { return (entry, recoveredBuilder, redactionType, imageModelProvider, packageContext) -> null; packageContext, sysUIContext) -> null; } } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +38 −20 Original line number Diff line number Diff line Loading @@ -94,25 +94,44 @@ fun AODPromotedNotification( val content = viewModel.content ?: return val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible if (com.android.systemui.Flags.uiRichOngoingAodSkeletonBgInflation()) { val notificationView = content.notificationView if (notificationView == null) { Log.w(TAG, "not displaying promoted notif with ineligible style on AOD") return } key(content.identity) { AODPromotedNotificationView( notificationViewFactory = { notificationView }, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = modifier, ) } } else { val layoutResource = content.layoutResource if (layoutResource == null) { Log.w(TAG, "not displaying promoted notif with ineligible style on AOD") return } key(content.identity) { AODPromotedNotificationView( layoutResource = layoutResource, notificationViewFactory = { context -> traceSection("$TAG.inflate") { LayoutInflater.from(context).inflate(layoutResource, /* root= */ null) } }, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = modifier, ) } } } @Composable fun AODPromotedNotificationView( layoutResource: Int, notificationViewFactory: (Context) -> View, content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean, modifier: Modifier = Modifier, Loading @@ -139,15 +158,14 @@ fun AODPromotedNotificationView( Box(modifier = boxModifier) { AndroidView( factory = { context -> val notif = traceSection("$TAG.inflate") { LayoutInflater.from(context).inflate(layoutResource, /* root= */ null) } val notificationView = notificationViewFactory(context) val updater = traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) } traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notificationView) } val frame = FrameLayoutWithMaxHeight(maxHeight, context) frame.addView(notif) frame.addView(notificationView) frame.setTag(viewUpdaterTagId, updater) frame Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +69 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.promoted import android.annotation.WorkerThread import android.app.Flags.notificationsRedesignTemplates import android.app.Notification import android.app.Notification.BigPictureStyle import android.app.Notification.BigTextStyle Loading @@ -38,6 +40,9 @@ import android.app.Person import android.content.Context import android.graphics.drawable.Icon import android.service.notification.StatusBarNotification import android.view.LayoutInflater import androidx.compose.ui.util.trace import com.android.internal.R import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE Loading @@ -62,12 +67,14 @@ import com.android.systemui.util.time.SystemClock import javax.inject.Inject interface PromotedNotificationContentExtractor { @WorkerThread fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModels? } Loading @@ -81,12 +88,15 @@ constructor( private val systemClock: SystemClock, private val logger: PromotedNotificationLogger, ) : PromotedNotificationContentExtractor { @WorkerThread override fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModels? { if (!PromotedNotificationContentModel.featureFlagEnabled()) { if (LOG_NOT_EXTRACTED) { Loading Loading @@ -118,6 +128,7 @@ constructor( lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs, imageModelProvider = imageModelProvider, packageContext = packageContext, systemUiContext = systemUiContext, ) val publicVersion = if (redactionType == REDACTION_TYPE_NONE) { Loading @@ -128,9 +139,13 @@ constructor( privateModel = privateVersion, publicNotification = publicNotification, imageModelProvider = imageModelProvider, packageContext = packageContext, systemUiContext = systemUiContext, ) } ?: createDefaultPublicVersion( privateModel = privateVersion, systemUiContext = systemUiContext, ) } ?: createDefaultPublicVersion(privateModel = privateVersion) } return PromotedNotificationContentModels( privateVersion = privateVersion, Loading @@ -153,13 +168,15 @@ constructor( } private fun createDefaultPublicVersion( privateModel: PromotedNotificationContentModel privateModel: PromotedNotificationContentModel, systemUiContext: Context, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { it.style = if (privateModel.style == Style.Ineligible) Style.Ineligible else Style.Base copyNonSensitiveFields(privateModel, it) inflateNotificationView(it, systemUiContext) } .build() Loading @@ -167,7 +184,7 @@ constructor( privateModel: PromotedNotificationContentModel, publicNotification: Notification, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { publicBuilder -> Loading @@ -191,6 +208,7 @@ constructor( if (publicBuilder.style == Style.Call) { extractCallStyleContent(publicNotification, publicBuilder, imageModelProvider) } inflateNotificationView(publicBuilder, systemUiContext) } .build() Loading @@ -201,6 +219,7 @@ constructor( lastAudiblyAlertedMs: Long, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModel { val notification = sbn.notification Loading @@ -227,7 +246,6 @@ constructor( contentBuilder.text = notification.text(recoveredBuilder.style?.javaClass) contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider) contentBuilder.oldProgress = notification.oldProgress() val colorsFromNotif = recoveredBuilder.getColors(/* isHeader= */ false) contentBuilder.colors = PromotedNotificationContentModel.Colors( Loading @@ -236,10 +254,56 @@ constructor( ) recoveredBuilder.extractStyleContent(notification, contentBuilder, imageModelProvider) inflateNotificationView(contentBuilder, systemUiContext) return contentBuilder.build() } private fun inflateNotificationView( contentBuilder: PromotedNotificationContentModel.Builder, systemUiContext: Context, ) { if (!Flags.uiRichOngoingAodSkeletonBgInflation()) return val style = contentBuilder.style ?: return val res = getLayoutSource(style) ?: return // Inflating with `sysuiContext` is intentional here. // As we transition to Jetpack Compose, the view layer will no longer have direct // access to the application's context. Using `sysuiContext` ensures we can // properly inflate this view while adhering to upcoming architectural constraints. trace("AODPromotedNotification#inflate") { contentBuilder.notificationView = LayoutInflater.from(systemUiContext).inflate(res, /* root= */ null) } } private fun getLayoutSource(style: Style): Int? { return if (notificationsRedesignTemplates()) { when (style) { Style.Base -> R.layout.notification_2025_template_expanded_base Style.CollapsedBase -> R.layout.notification_2025_template_collapsed_base Style.BigPicture -> R.layout.notification_2025_template_expanded_big_picture Style.BigText -> R.layout.notification_2025_template_expanded_big_text Style.Call -> R.layout.notification_2025_template_expanded_call Style.CollapsedCall -> R.layout.notification_2025_template_collapsed_call Style.Progress -> R.layout.notification_2025_template_expanded_progress Style.Ineligible -> null } } else { when (style) { Style.Base -> R.layout.notification_template_material_big_base Style.CollapsedBase -> R.layout.notification_template_material_base Style.BigPicture -> R.layout.notification_template_material_big_picture Style.BigText -> R.layout.notification_template_material_big_text Style.Call -> R.layout.notification_template_material_big_call Style.CollapsedCall -> R.layout.notification_template_material_call Style.Progress -> R.layout.notification_template_material_progress Style.Ineligible -> null } } } private fun Notification.skeletonSmallIcon( imageModelProvider: ImageModelProvider ): NotifIcon.SmallIcon? = Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +10 −0 Original line number Diff line number Diff line Loading @@ -2050,3 +2050,13 @@ flag { description: "Enables MinMode feature" bug: "411746510" } flag { name: "ui_rich_ongoing_aod_skeleton_bg_inflation" namespace: "systemui" description: "Offload AOD Skeleton inflation to the background thread" bug: "416714129" metadata { purpose: PURPOSE_BUGFIX } }
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +1 −0 Original line number Diff line number Diff line Loading @@ -654,6 +654,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { redactionType, imageModelProvider, context, context, ) } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +1 −1 Original line number Diff line number Diff line Loading @@ -327,7 +327,7 @@ public interface NotificationsModule { return implProvider.get(); } else { return (entry, recoveredBuilder, redactionType, imageModelProvider, packageContext) -> null; packageContext, sysUIContext) -> null; } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +38 −20 Original line number Diff line number Diff line Loading @@ -94,25 +94,44 @@ fun AODPromotedNotification( val content = viewModel.content ?: return val audiblyAlertedIconVisible = viewModel.audiblyAlertedIconVisible if (com.android.systemui.Flags.uiRichOngoingAodSkeletonBgInflation()) { val notificationView = content.notificationView if (notificationView == null) { Log.w(TAG, "not displaying promoted notif with ineligible style on AOD") return } key(content.identity) { AODPromotedNotificationView( notificationViewFactory = { notificationView }, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = modifier, ) } } else { val layoutResource = content.layoutResource if (layoutResource == null) { Log.w(TAG, "not displaying promoted notif with ineligible style on AOD") return } key(content.identity) { AODPromotedNotificationView( layoutResource = layoutResource, notificationViewFactory = { context -> traceSection("$TAG.inflate") { LayoutInflater.from(context).inflate(layoutResource, /* root= */ null) } }, content = content, audiblyAlertedIconVisible = audiblyAlertedIconVisible, modifier = modifier, ) } } } @Composable fun AODPromotedNotificationView( layoutResource: Int, notificationViewFactory: (Context) -> View, content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean, modifier: Modifier = Modifier, Loading @@ -139,15 +158,14 @@ fun AODPromotedNotificationView( Box(modifier = boxModifier) { AndroidView( factory = { context -> val notif = traceSection("$TAG.inflate") { LayoutInflater.from(context).inflate(layoutResource, /* root= */ null) } val notificationView = notificationViewFactory(context) val updater = traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notif) } traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(notificationView) } val frame = FrameLayoutWithMaxHeight(maxHeight, context) frame.addView(notif) frame.addView(notificationView) frame.setTag(viewUpdaterTagId, updater) frame Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +69 −5 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.promoted import android.annotation.WorkerThread import android.app.Flags.notificationsRedesignTemplates import android.app.Notification import android.app.Notification.BigPictureStyle import android.app.Notification.BigTextStyle Loading @@ -38,6 +40,9 @@ import android.app.Person import android.content.Context import android.graphics.drawable.Icon import android.service.notification.StatusBarNotification import android.view.LayoutInflater import androidx.compose.ui.util.trace import com.android.internal.R import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE Loading @@ -62,12 +67,14 @@ import com.android.systemui.util.time.SystemClock import javax.inject.Inject interface PromotedNotificationContentExtractor { @WorkerThread fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModels? } Loading @@ -81,12 +88,15 @@ constructor( private val systemClock: SystemClock, private val logger: PromotedNotificationLogger, ) : PromotedNotificationContentExtractor { @WorkerThread override fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModels? { if (!PromotedNotificationContentModel.featureFlagEnabled()) { if (LOG_NOT_EXTRACTED) { Loading Loading @@ -118,6 +128,7 @@ constructor( lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs, imageModelProvider = imageModelProvider, packageContext = packageContext, systemUiContext = systemUiContext, ) val publicVersion = if (redactionType == REDACTION_TYPE_NONE) { Loading @@ -128,9 +139,13 @@ constructor( privateModel = privateVersion, publicNotification = publicNotification, imageModelProvider = imageModelProvider, packageContext = packageContext, systemUiContext = systemUiContext, ) } ?: createDefaultPublicVersion( privateModel = privateVersion, systemUiContext = systemUiContext, ) } ?: createDefaultPublicVersion(privateModel = privateVersion) } return PromotedNotificationContentModels( privateVersion = privateVersion, Loading @@ -153,13 +168,15 @@ constructor( } private fun createDefaultPublicVersion( privateModel: PromotedNotificationContentModel privateModel: PromotedNotificationContentModel, systemUiContext: Context, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { it.style = if (privateModel.style == Style.Ineligible) Style.Ineligible else Style.Base copyNonSensitiveFields(privateModel, it) inflateNotificationView(it, systemUiContext) } .build() Loading @@ -167,7 +184,7 @@ constructor( privateModel: PromotedNotificationContentModel, publicNotification: Notification, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { publicBuilder -> Loading @@ -191,6 +208,7 @@ constructor( if (publicBuilder.style == Style.Call) { extractCallStyleContent(publicNotification, publicBuilder, imageModelProvider) } inflateNotificationView(publicBuilder, systemUiContext) } .build() Loading @@ -201,6 +219,7 @@ constructor( lastAudiblyAlertedMs: Long, imageModelProvider: ImageModelProvider, packageContext: Context, systemUiContext: Context, ): PromotedNotificationContentModel { val notification = sbn.notification Loading @@ -227,7 +246,6 @@ constructor( contentBuilder.text = notification.text(recoveredBuilder.style?.javaClass) contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider) contentBuilder.oldProgress = notification.oldProgress() val colorsFromNotif = recoveredBuilder.getColors(/* isHeader= */ false) contentBuilder.colors = PromotedNotificationContentModel.Colors( Loading @@ -236,10 +254,56 @@ constructor( ) recoveredBuilder.extractStyleContent(notification, contentBuilder, imageModelProvider) inflateNotificationView(contentBuilder, systemUiContext) return contentBuilder.build() } private fun inflateNotificationView( contentBuilder: PromotedNotificationContentModel.Builder, systemUiContext: Context, ) { if (!Flags.uiRichOngoingAodSkeletonBgInflation()) return val style = contentBuilder.style ?: return val res = getLayoutSource(style) ?: return // Inflating with `sysuiContext` is intentional here. // As we transition to Jetpack Compose, the view layer will no longer have direct // access to the application's context. Using `sysuiContext` ensures we can // properly inflate this view while adhering to upcoming architectural constraints. trace("AODPromotedNotification#inflate") { contentBuilder.notificationView = LayoutInflater.from(systemUiContext).inflate(res, /* root= */ null) } } private fun getLayoutSource(style: Style): Int? { return if (notificationsRedesignTemplates()) { when (style) { Style.Base -> R.layout.notification_2025_template_expanded_base Style.CollapsedBase -> R.layout.notification_2025_template_collapsed_base Style.BigPicture -> R.layout.notification_2025_template_expanded_big_picture Style.BigText -> R.layout.notification_2025_template_expanded_big_text Style.Call -> R.layout.notification_2025_template_expanded_call Style.CollapsedCall -> R.layout.notification_2025_template_collapsed_call Style.Progress -> R.layout.notification_2025_template_expanded_progress Style.Ineligible -> null } } else { when (style) { Style.Base -> R.layout.notification_template_material_big_base Style.CollapsedBase -> R.layout.notification_template_material_base Style.BigPicture -> R.layout.notification_template_material_big_picture Style.BigText -> R.layout.notification_template_material_big_text Style.Call -> R.layout.notification_template_material_big_call Style.CollapsedCall -> R.layout.notification_template_material_call Style.Progress -> R.layout.notification_template_material_progress Style.Ineligible -> null } } } private fun Notification.skeletonSmallIcon( imageModelProvider: ImageModelProvider ): NotifIcon.SmallIcon? = Loading