summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt123
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt2
4 files changed, 142 insertions, 87 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt
index bad33a402ff7..915edc03952d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationsInteractorTest.kt
@@ -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
@@ -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()
@@ -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
@@ -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) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index 3d55feb0023b..3bfe9f2c372c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -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
}
@@ -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 -> {}
}
@@ -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)
- updateTitle(title, content)
+ if (headerTitleView == null) {
+ updateTitle(title, content)
+ }
updateText(textView, content)
updateSmallIcon(icon, content)
updateImageView(rightIcon, content.skeletonLargeIcon)
@@ -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)
}
@@ -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
@@ -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)
@@ -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
@@ -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) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
index d9bdfbc81145..9fe3ff4c4bce 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -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,
@@ -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,
@@ -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()
@@ -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()
}
@@ -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()
}
@@ -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
}
@@ -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,
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
index 339a5bb29a34..ae6b2cc6cb1f 100644
--- 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
@@ -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,
}