Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt +7 −12 Original line number Diff line number Diff line Loading @@ -32,12 +32,11 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.policy.domain.interactor.sensitiveNotificationProtectionInteractor import com.android.systemui.statusbar.policy.mockSensitiveNotificationProtectionController import com.android.systemui.testKosmos Loading @@ -50,12 +49,8 @@ import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @EnableFlags( PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME, ) @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) @EnableChipsModernization class AODPromotedNotificationsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() Loading Loading @@ -111,10 +106,10 @@ class AODPromotedNotificationsInteractorTest : SysuiTestCase() { renderNotificationListInteractor.setRenderedList(listOf(ronEntry)) // THEN aod content is sensitive // THEN aod content is redacted val content by collectLastValue(underTest.content) assertThat(content).isNotNull() assertThat(content?.title).isNull() // SOON: .isEqualTo("REDACTED") assertThat(content!!.title).isEqualTo("REDACTED") } @Test Loading @@ -128,10 +123,10 @@ class AODPromotedNotificationsInteractorTest : SysuiTestCase() { renderNotificationListInteractor.setRenderedList(listOf(ronEntry)) // THEN aod content is sensitive // THEN aod content is redacted val content by collectLastValue(underTest.content) assertThat(content).isNotNull() assertThat(content?.title).isNull() // SOON: .isEqualTo("REDACTED") assertThat(content!!.title).isEqualTo("REDACTED") } private fun Kosmos.setKeyguardLocked(locked: Boolean) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +56 −29 Original line number Diff line number Diff line Loading @@ -205,18 +205,22 @@ private val PromotedNotificationContentModel.layoutResource: 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 } Loading Loading @@ -333,10 +337,12 @@ private class AODPromotedNotificationViewUpdater(root: View) { fun update(content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean) { when (content.style) { Style.Base -> updateBase(content) Style.Base -> updateBase(content, collapsed = false) Style.CollapsedBase -> updateBase(content, collapsed = true) Style.BigPicture -> updateBigPictureStyle(content) Style.BigText -> updateBigTextStyle(content) Style.Call -> updateCallStyle(content) Style.Call -> updateCallStyle(content, collapsed = false) Style.CollapsedCall -> updateCallStyle(content, collapsed = true) Style.Progress -> updateProgressStyle(content) Style.Ineligible -> {} } Loading @@ -346,11 +352,15 @@ private class AODPromotedNotificationViewUpdater(root: View) { private fun updateBase( content: PromotedNotificationContentModel, collapsed: Boolean, textView: ImageFloatingTextView? = text, ) { updateHeader(content) val headerTitleView = if (collapsed) title else null updateHeader(content, titleView = headerTitleView, collapsed = collapsed) if (headerTitleView == null) { updateTitle(title, content) } updateText(textView, content) updateSmallIcon(icon, content) updateImageView(rightIcon, content.skeletonLargeIcon) Loading @@ -358,21 +368,21 @@ private class AODPromotedNotificationViewUpdater(root: View) { } private fun updateBigPictureStyle(content: PromotedNotificationContentModel) { updateBase(content) updateBase(content, collapsed = false) } private fun updateBigTextStyle(content: PromotedNotificationContentModel) { updateBase(content, textView = bigText) updateBase(content, collapsed = false, textView = bigText) } private fun updateCallStyle(content: PromotedNotificationContentModel) { updateConversationHeader(content) private fun updateCallStyle(content: PromotedNotificationContentModel, collapsed: Boolean) { updateConversationHeader(content, collapsed = collapsed) updateText(text, content) } private fun updateProgressStyle(content: PromotedNotificationContentModel) { updateBase(content) updateBase(content, collapsed = false) updateNewProgressBar(content) } Loading Loading @@ -409,24 +419,35 @@ private class AODPromotedNotificationViewUpdater(root: View) { } } private fun updateHeader(content: PromotedNotificationContentModel) { updateAppName(content) private fun updateHeader( content: PromotedNotificationContentModel, collapsed: Boolean, titleView: TextView?, ) { val hasTitle = titleView != null && content.title != null val hasSubText = content.subText != null // the collapsed form doesn't show the app name unless there is no other text in the header val appNameRequired = !hasTitle && !hasSubText val hideAppName = (!appNameRequired && collapsed) updateAppName(content, forceHide = hideAppName) updateTextView(headerTextSecondary, content.subText) // Not calling updateTitle(headerText, content) because the title is always a separate // element in the expanded layout used for AOD RONs. updateTitle(titleView, content) updateTimeAndChronometer(content) updateHeaderDividers(content) updateHeaderDividers(content, hideTitle = !hasTitle, hideAppName = hideAppName) updateTopLine(content) } private fun updateHeaderDividers(content: PromotedNotificationContentModel) { val hasAppName = content.appName != null private fun updateHeaderDividers( content: PromotedNotificationContentModel, hideAppName: Boolean, hideTitle: Boolean, ) { val hasAppName = content.appName != null && !hideAppName val hasSubText = content.subText != null // Not setting hasHeader = content.title because the title is always a separate element in // the expanded layout used for AOD RONs. val hasHeader = false val hasHeader = content.title != null && !hideTitle val hasTimeOrChronometer = content.time != null val hasTextBeforeSubText = hasAppName Loading @@ -442,13 +463,17 @@ private class AODPromotedNotificationViewUpdater(root: View) { timeDivider?.isVisible = showDividerBeforeTime } private fun updateConversationHeader(content: PromotedNotificationContentModel) { updateAppName(content) private fun updateConversationHeader( content: PromotedNotificationContentModel, collapsed: Boolean, ) { updateAppName(content, forceHide = collapsed) updateTimeAndChronometer(content) updateImageView(verificationIcon, content.verificationIcon) updateTextView(verificationText, content.verificationText) updateConversationHeaderDividers(content) updateConversationHeaderDividers(content, hideTitle = true, hideAppName = collapsed) updateTopLine(content) Loading @@ -456,11 +481,13 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTitle(conversationText, content) } private fun updateConversationHeaderDividers(content: PromotedNotificationContentModel) { // Not setting hasTitle = content.title because the title is always a separate element in // the expanded layout used for AOD RONs. val hasTitle = false val hasAppName = content.appName != null private fun updateConversationHeaderDividers( content: PromotedNotificationContentModel, hideTitle: Boolean, hideAppName: Boolean, ) { val hasTitle = content.title != null && !hideTitle val hasAppName = content.appName != null && !hideAppName val hasTimeOrChronometer = content.time != null val hasVerification = !content.verificationIcon.isNullOrEmpty() || content.verificationText != null Loading @@ -478,8 +505,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { verificationDivider?.isVisible = showDividerBeforeVerification } private fun updateAppName(content: PromotedNotificationContentModel) { updateTextView(appNameText, content.appName) private fun updateAppName(content: PromotedNotificationContentModel, forceHide: Boolean) { updateTextView(appNameText, content.appName?.takeUnless { forceHide }) } private fun updateTitle(titleView: TextView?, content: PromotedNotificationContentModel) { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +77 −46 Original line number Diff line number Diff line Loading @@ -112,12 +112,13 @@ constructor( if (redactionType == REDACTION_TYPE_NONE) { privateVersion } else { if (notification.publicVersion == null) { privateVersion.toDefaultPublicVersion() } else { // TODO(b/400991304): implement extraction for [Notification.publicVersion] privateVersion.toDefaultPublicVersion() } notification.publicVersion?.let { publicNotification -> createAppDefinedPublicVersion( privateModel = privateVersion, publicNotification = publicNotification, imageModelProvider = imageModelProvider, ) } ?: createDefaultPublicVersion(privateModel = privateVersion) } return PromotedNotificationContentModels( privateVersion = privateVersion, Loading @@ -126,19 +127,59 @@ constructor( .also { logger.logExtractionSucceeded(entry, it) } } private fun PromotedNotificationContentModel.toDefaultPublicVersion(): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = identity.key).let { it.style = if (style == Style.Ineligible) Style.Ineligible else Style.Base it.smallIcon = smallIcon it.iconLevel = iconLevel it.appName = appName it.time = time it.lastAudiblyAlertedMs = lastAudiblyAlertedMs it.profileBadgeResId = profileBadgeResId it.colors = colors it.build() } private fun copyNonSensitiveFields( privateModel: PromotedNotificationContentModel, publicBuilder: PromotedNotificationContentModel.Builder, ) { publicBuilder.smallIcon = privateModel.smallIcon publicBuilder.iconLevel = privateModel.iconLevel publicBuilder.appName = privateModel.appName publicBuilder.time = privateModel.time publicBuilder.lastAudiblyAlertedMs = privateModel.lastAudiblyAlertedMs publicBuilder.profileBadgeResId = privateModel.profileBadgeResId publicBuilder.colors = privateModel.colors } private fun createDefaultPublicVersion( privateModel: PromotedNotificationContentModel ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { it.style = if (privateModel.style == Style.Ineligible) Style.Ineligible else Style.Base copyNonSensitiveFields(privateModel, it) } .build() private fun createAppDefinedPublicVersion( privateModel: PromotedNotificationContentModel, publicNotification: Notification, imageModelProvider: ImageModelProvider, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { publicBuilder -> val notificationStyle = publicNotification.notificationStyle publicBuilder.style = when { privateModel.style == Style.Ineligible -> Style.Ineligible notificationStyle == CallStyle::class.java -> Style.CollapsedCall else -> Style.CollapsedBase } copyNonSensitiveFields(privateModel = privateModel, publicBuilder = publicBuilder) publicBuilder.shortCriticalText = publicNotification.shortCriticalText() publicBuilder.subText = publicNotification.subText() // The standard public version is extracted as a collapsed notification, // so avoid using bigTitle or bigText, and instead get the collapsed versions. publicBuilder.title = publicNotification.title(notificationStyle, expanded = false) publicBuilder.text = publicNotification.text() publicBuilder.skeletonLargeIcon = publicNotification.skeletonLargeIcon(imageModelProvider) // Only CallStyle has styled content that shows in the collapsed version. if (publicBuilder.style == Style.Call) { extractCallStyleContent(publicNotification, publicBuilder, imageModelProvider) } } .build() private fun extractPrivateContent( key: String, Loading @@ -163,8 +204,8 @@ constructor( contentBuilder.shortCriticalText = notification.shortCriticalText() contentBuilder.lastAudiblyAlertedMs = lastAudiblyAlertedMs contentBuilder.profileBadgeResId = null // TODO contentBuilder.title = notification.title(recoveredBuilder.style) contentBuilder.text = notification.text(recoveredBuilder.style) contentBuilder.title = notification.title(recoveredBuilder.style?.javaClass) contentBuilder.text = notification.text(recoveredBuilder.style?.javaClass) contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider) contentBuilder.oldProgress = notification.oldProgress() Loading @@ -191,12 +232,16 @@ constructor( private fun Notification.callPerson(): Person? = extras?.getParcelable(EXTRA_CALL_PERSON, Person::class.java) private fun Notification.title(style: Notification.Style?): CharSequence? { return when (style) { is BigTextStyle, is BigPictureStyle, is InboxStyle -> bigTitle() is CallStyle -> callPerson()?.name private fun Notification.title( styleClass: Class<out Notification.Style>?, expanded: Boolean = true, ): CharSequence? { // bigTitle is only used in the expanded form of 3 styles. return when (styleClass) { BigTextStyle::class.java, BigPictureStyle::class.java, InboxStyle::class.java -> if (expanded) bigTitle() else null CallStyle::class.java -> callPerson()?.name?.takeUnlessEmpty() else -> null } ?: title() } Loading @@ -206,9 +251,9 @@ constructor( private fun Notification.bigText(): CharSequence? = getCharSequenceExtraUnlessEmpty(EXTRA_BIG_TEXT) private fun Notification.text(style: Notification.Style?): CharSequence? { return when (style) { is BigTextStyle -> bigText() private fun Notification.text(styleClass: Class<out Notification.Style>?): CharSequence? { return when (styleClass) { BigTextStyle::class.java -> bigText() else -> null } ?: text() } Loading Loading @@ -293,17 +338,15 @@ constructor( null -> Style.Base is BigPictureStyle -> { style.extractContent(contentBuilder) Style.BigPicture } is BigTextStyle -> { style.extractContent(contentBuilder) Style.BigText } is CallStyle -> { style.extractContent(notification, contentBuilder, imageModelProvider) extractCallStyleContent(notification, contentBuilder, imageModelProvider) Style.Call } Loading @@ -316,19 +359,7 @@ constructor( } } private fun BigPictureStyle.extractContent( contentBuilder: PromotedNotificationContentModel.Builder ) { // Big title is handled in resolveTitle, and big picture is unsupported. } private fun BigTextStyle.extractContent( contentBuilder: PromotedNotificationContentModel.Builder ) { // Big title and big text are handled in resolveTitle and resolveText. } private fun CallStyle.extractContent( private fun extractCallStyleContent( notification: Notification, contentBuilder: PromotedNotificationContentModel.Builder, imageModelProvider: ImageModelProvider, Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -174,9 +174,11 @@ data class PromotedNotificationContentModel( /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */ enum class Style { Base, // style == null CollapsedBase, // style == null BigPicture, BigText, Call, CollapsedCall, Progress, Ineligible, } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt +7 −12 Original line number Diff line number Diff line Loading @@ -32,12 +32,11 @@ import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.policy.domain.interactor.sensitiveNotificationProtectionInteractor import com.android.systemui.statusbar.policy.mockSensitiveNotificationProtectionController import com.android.systemui.testKosmos Loading @@ -50,12 +49,8 @@ import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) @EnableFlags( PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME, StatusBarChipsModernization.FLAG_NAME, StatusBarRootModernization.FLAG_NAME, ) @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) @EnableChipsModernization class AODPromotedNotificationsInteractorTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() Loading Loading @@ -111,10 +106,10 @@ class AODPromotedNotificationsInteractorTest : SysuiTestCase() { renderNotificationListInteractor.setRenderedList(listOf(ronEntry)) // THEN aod content is sensitive // THEN aod content is redacted val content by collectLastValue(underTest.content) assertThat(content).isNotNull() assertThat(content?.title).isNull() // SOON: .isEqualTo("REDACTED") assertThat(content!!.title).isEqualTo("REDACTED") } @Test Loading @@ -128,10 +123,10 @@ class AODPromotedNotificationsInteractorTest : SysuiTestCase() { renderNotificationListInteractor.setRenderedList(listOf(ronEntry)) // THEN aod content is sensitive // THEN aod content is redacted val content by collectLastValue(underTest.content) assertThat(content).isNotNull() assertThat(content?.title).isNull() // SOON: .isEqualTo("REDACTED") assertThat(content!!.title).isEqualTo("REDACTED") } private fun Kosmos.setKeyguardLocked(locked: Boolean) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt +56 −29 Original line number Diff line number Diff line Loading @@ -205,18 +205,22 @@ private val PromotedNotificationContentModel.layoutResource: 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 } Loading Loading @@ -333,10 +337,12 @@ private class AODPromotedNotificationViewUpdater(root: View) { fun update(content: PromotedNotificationContentModel, audiblyAlertedIconVisible: Boolean) { when (content.style) { Style.Base -> updateBase(content) Style.Base -> updateBase(content, collapsed = false) Style.CollapsedBase -> updateBase(content, collapsed = true) Style.BigPicture -> updateBigPictureStyle(content) Style.BigText -> updateBigTextStyle(content) Style.Call -> updateCallStyle(content) Style.Call -> updateCallStyle(content, collapsed = false) Style.CollapsedCall -> updateCallStyle(content, collapsed = true) Style.Progress -> updateProgressStyle(content) Style.Ineligible -> {} } Loading @@ -346,11 +352,15 @@ private class AODPromotedNotificationViewUpdater(root: View) { private fun updateBase( content: PromotedNotificationContentModel, collapsed: Boolean, textView: ImageFloatingTextView? = text, ) { updateHeader(content) val headerTitleView = if (collapsed) title else null updateHeader(content, titleView = headerTitleView, collapsed = collapsed) if (headerTitleView == null) { updateTitle(title, content) } updateText(textView, content) updateSmallIcon(icon, content) updateImageView(rightIcon, content.skeletonLargeIcon) Loading @@ -358,21 +368,21 @@ private class AODPromotedNotificationViewUpdater(root: View) { } private fun updateBigPictureStyle(content: PromotedNotificationContentModel) { updateBase(content) updateBase(content, collapsed = false) } private fun updateBigTextStyle(content: PromotedNotificationContentModel) { updateBase(content, textView = bigText) updateBase(content, collapsed = false, textView = bigText) } private fun updateCallStyle(content: PromotedNotificationContentModel) { updateConversationHeader(content) private fun updateCallStyle(content: PromotedNotificationContentModel, collapsed: Boolean) { updateConversationHeader(content, collapsed = collapsed) updateText(text, content) } private fun updateProgressStyle(content: PromotedNotificationContentModel) { updateBase(content) updateBase(content, collapsed = false) updateNewProgressBar(content) } Loading Loading @@ -409,24 +419,35 @@ private class AODPromotedNotificationViewUpdater(root: View) { } } private fun updateHeader(content: PromotedNotificationContentModel) { updateAppName(content) private fun updateHeader( content: PromotedNotificationContentModel, collapsed: Boolean, titleView: TextView?, ) { val hasTitle = titleView != null && content.title != null val hasSubText = content.subText != null // the collapsed form doesn't show the app name unless there is no other text in the header val appNameRequired = !hasTitle && !hasSubText val hideAppName = (!appNameRequired && collapsed) updateAppName(content, forceHide = hideAppName) updateTextView(headerTextSecondary, content.subText) // Not calling updateTitle(headerText, content) because the title is always a separate // element in the expanded layout used for AOD RONs. updateTitle(titleView, content) updateTimeAndChronometer(content) updateHeaderDividers(content) updateHeaderDividers(content, hideTitle = !hasTitle, hideAppName = hideAppName) updateTopLine(content) } private fun updateHeaderDividers(content: PromotedNotificationContentModel) { val hasAppName = content.appName != null private fun updateHeaderDividers( content: PromotedNotificationContentModel, hideAppName: Boolean, hideTitle: Boolean, ) { val hasAppName = content.appName != null && !hideAppName val hasSubText = content.subText != null // Not setting hasHeader = content.title because the title is always a separate element in // the expanded layout used for AOD RONs. val hasHeader = false val hasHeader = content.title != null && !hideTitle val hasTimeOrChronometer = content.time != null val hasTextBeforeSubText = hasAppName Loading @@ -442,13 +463,17 @@ private class AODPromotedNotificationViewUpdater(root: View) { timeDivider?.isVisible = showDividerBeforeTime } private fun updateConversationHeader(content: PromotedNotificationContentModel) { updateAppName(content) private fun updateConversationHeader( content: PromotedNotificationContentModel, collapsed: Boolean, ) { updateAppName(content, forceHide = collapsed) updateTimeAndChronometer(content) updateImageView(verificationIcon, content.verificationIcon) updateTextView(verificationText, content.verificationText) updateConversationHeaderDividers(content) updateConversationHeaderDividers(content, hideTitle = true, hideAppName = collapsed) updateTopLine(content) Loading @@ -456,11 +481,13 @@ private class AODPromotedNotificationViewUpdater(root: View) { updateTitle(conversationText, content) } private fun updateConversationHeaderDividers(content: PromotedNotificationContentModel) { // Not setting hasTitle = content.title because the title is always a separate element in // the expanded layout used for AOD RONs. val hasTitle = false val hasAppName = content.appName != null private fun updateConversationHeaderDividers( content: PromotedNotificationContentModel, hideTitle: Boolean, hideAppName: Boolean, ) { val hasTitle = content.title != null && !hideTitle val hasAppName = content.appName != null && !hideAppName val hasTimeOrChronometer = content.time != null val hasVerification = !content.verificationIcon.isNullOrEmpty() || content.verificationText != null Loading @@ -478,8 +505,8 @@ private class AODPromotedNotificationViewUpdater(root: View) { verificationDivider?.isVisible = showDividerBeforeVerification } private fun updateAppName(content: PromotedNotificationContentModel) { updateTextView(appNameText, content.appName) private fun updateAppName(content: PromotedNotificationContentModel, forceHide: Boolean) { updateTextView(appNameText, content.appName?.takeUnless { forceHide }) } private fun updateTitle(titleView: TextView?, content: PromotedNotificationContentModel) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +77 −46 Original line number Diff line number Diff line Loading @@ -112,12 +112,13 @@ constructor( if (redactionType == REDACTION_TYPE_NONE) { privateVersion } else { if (notification.publicVersion == null) { privateVersion.toDefaultPublicVersion() } else { // TODO(b/400991304): implement extraction for [Notification.publicVersion] privateVersion.toDefaultPublicVersion() } notification.publicVersion?.let { publicNotification -> createAppDefinedPublicVersion( privateModel = privateVersion, publicNotification = publicNotification, imageModelProvider = imageModelProvider, ) } ?: createDefaultPublicVersion(privateModel = privateVersion) } return PromotedNotificationContentModels( privateVersion = privateVersion, Loading @@ -126,19 +127,59 @@ constructor( .also { logger.logExtractionSucceeded(entry, it) } } private fun PromotedNotificationContentModel.toDefaultPublicVersion(): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = identity.key).let { it.style = if (style == Style.Ineligible) Style.Ineligible else Style.Base it.smallIcon = smallIcon it.iconLevel = iconLevel it.appName = appName it.time = time it.lastAudiblyAlertedMs = lastAudiblyAlertedMs it.profileBadgeResId = profileBadgeResId it.colors = colors it.build() } private fun copyNonSensitiveFields( privateModel: PromotedNotificationContentModel, publicBuilder: PromotedNotificationContentModel.Builder, ) { publicBuilder.smallIcon = privateModel.smallIcon publicBuilder.iconLevel = privateModel.iconLevel publicBuilder.appName = privateModel.appName publicBuilder.time = privateModel.time publicBuilder.lastAudiblyAlertedMs = privateModel.lastAudiblyAlertedMs publicBuilder.profileBadgeResId = privateModel.profileBadgeResId publicBuilder.colors = privateModel.colors } private fun createDefaultPublicVersion( privateModel: PromotedNotificationContentModel ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { it.style = if (privateModel.style == Style.Ineligible) Style.Ineligible else Style.Base copyNonSensitiveFields(privateModel, it) } .build() private fun createAppDefinedPublicVersion( privateModel: PromotedNotificationContentModel, publicNotification: Notification, imageModelProvider: ImageModelProvider, ): PromotedNotificationContentModel = PromotedNotificationContentModel.Builder(key = privateModel.identity.key) .also { publicBuilder -> val notificationStyle = publicNotification.notificationStyle publicBuilder.style = when { privateModel.style == Style.Ineligible -> Style.Ineligible notificationStyle == CallStyle::class.java -> Style.CollapsedCall else -> Style.CollapsedBase } copyNonSensitiveFields(privateModel = privateModel, publicBuilder = publicBuilder) publicBuilder.shortCriticalText = publicNotification.shortCriticalText() publicBuilder.subText = publicNotification.subText() // The standard public version is extracted as a collapsed notification, // so avoid using bigTitle or bigText, and instead get the collapsed versions. publicBuilder.title = publicNotification.title(notificationStyle, expanded = false) publicBuilder.text = publicNotification.text() publicBuilder.skeletonLargeIcon = publicNotification.skeletonLargeIcon(imageModelProvider) // Only CallStyle has styled content that shows in the collapsed version. if (publicBuilder.style == Style.Call) { extractCallStyleContent(publicNotification, publicBuilder, imageModelProvider) } } .build() private fun extractPrivateContent( key: String, Loading @@ -163,8 +204,8 @@ constructor( contentBuilder.shortCriticalText = notification.shortCriticalText() contentBuilder.lastAudiblyAlertedMs = lastAudiblyAlertedMs contentBuilder.profileBadgeResId = null // TODO contentBuilder.title = notification.title(recoveredBuilder.style) contentBuilder.text = notification.text(recoveredBuilder.style) contentBuilder.title = notification.title(recoveredBuilder.style?.javaClass) contentBuilder.text = notification.text(recoveredBuilder.style?.javaClass) contentBuilder.skeletonLargeIcon = notification.skeletonLargeIcon(imageModelProvider) contentBuilder.oldProgress = notification.oldProgress() Loading @@ -191,12 +232,16 @@ constructor( private fun Notification.callPerson(): Person? = extras?.getParcelable(EXTRA_CALL_PERSON, Person::class.java) private fun Notification.title(style: Notification.Style?): CharSequence? { return when (style) { is BigTextStyle, is BigPictureStyle, is InboxStyle -> bigTitle() is CallStyle -> callPerson()?.name private fun Notification.title( styleClass: Class<out Notification.Style>?, expanded: Boolean = true, ): CharSequence? { // bigTitle is only used in the expanded form of 3 styles. return when (styleClass) { BigTextStyle::class.java, BigPictureStyle::class.java, InboxStyle::class.java -> if (expanded) bigTitle() else null CallStyle::class.java -> callPerson()?.name?.takeUnlessEmpty() else -> null } ?: title() } Loading @@ -206,9 +251,9 @@ constructor( private fun Notification.bigText(): CharSequence? = getCharSequenceExtraUnlessEmpty(EXTRA_BIG_TEXT) private fun Notification.text(style: Notification.Style?): CharSequence? { return when (style) { is BigTextStyle -> bigText() private fun Notification.text(styleClass: Class<out Notification.Style>?): CharSequence? { return when (styleClass) { BigTextStyle::class.java -> bigText() else -> null } ?: text() } Loading Loading @@ -293,17 +338,15 @@ constructor( null -> Style.Base is BigPictureStyle -> { style.extractContent(contentBuilder) Style.BigPicture } is BigTextStyle -> { style.extractContent(contentBuilder) Style.BigText } is CallStyle -> { style.extractContent(notification, contentBuilder, imageModelProvider) extractCallStyleContent(notification, contentBuilder, imageModelProvider) Style.Call } Loading @@ -316,19 +359,7 @@ constructor( } } private fun BigPictureStyle.extractContent( contentBuilder: PromotedNotificationContentModel.Builder ) { // Big title is handled in resolveTitle, and big picture is unsupported. } private fun BigTextStyle.extractContent( contentBuilder: PromotedNotificationContentModel.Builder ) { // Big title and big text are handled in resolveTitle and resolveText. } private fun CallStyle.extractContent( private fun extractCallStyleContent( notification: Notification, contentBuilder: PromotedNotificationContentModel.Builder, imageModelProvider: ImageModelProvider, Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt +2 −0 Original line number Diff line number Diff line Loading @@ -174,9 +174,11 @@ data class PromotedNotificationContentModel( /** The promotion-eligible style of a notification, or [Style.Ineligible] if not. */ enum class Style { Base, // style == null CollapsedBase, // style == null BigPicture, BigText, Call, CollapsedCall, Progress, Ineligible, } Loading