diff options
18 files changed, 270 insertions, 17 deletions
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 35308ee43dea..40db6dd1b0ba 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1772,6 +1772,11 @@ public class Notification implements Parcelable */ public static final String EXTRA_FOREGROUND_APPS = "android.foregroundApps"; + /** + * @hide + */ + public static final String EXTRA_SUMMARIZED_CONTENT = "android.summarization"; + @UnsupportedAppUsage private Icon mSmallIcon; @UnsupportedAppUsage diff --git a/core/java/com/android/internal/widget/ConversationLayout.java b/core/java/com/android/internal/widget/ConversationLayout.java index b3ab5d3cd258..04ce9bcd7afd 100644 --- a/core/java/com/android/internal/widget/ConversationLayout.java +++ b/core/java/com/android/internal/widget/ConversationLayout.java @@ -105,6 +105,7 @@ public class ConversationLayout extends FrameLayout private ArrayList<MessagingGroup> mAddedGroups = new ArrayList<>(); private Person mUser; private CharSequence mNameReplacement; + private CharSequence mSummarizedContent; private boolean mIsCollapsed; private ImageResolver mImageResolver; private CachingIconView mConversationIconView; @@ -397,7 +398,7 @@ public class ConversationLayout extends FrameLayout * * @param isCollapsed is it collapsed */ - @RemotableViewMethod + @RemotableViewMethod(asyncImpl = "setIsCollapsedAsync") public void setIsCollapsed(boolean isCollapsed) { mIsCollapsed = isCollapsed; mMessagingLinearLayout.setMaxDisplayedLines(isCollapsed ? 1 : Integer.MAX_VALUE); @@ -406,6 +407,15 @@ public class ConversationLayout extends FrameLayout } /** + * setDataAsync needs to do different stuff for the collapsed vs expanded view, so store the + * collapsed state early. + */ + public Runnable setIsCollapsedAsync(boolean isCollapsed) { + mIsCollapsed = isCollapsed; + return () -> setIsCollapsed(isCollapsed); + } + + /** * Set conversation data * * @param extras Bundle contains conversation data @@ -439,8 +449,16 @@ public class ConversationLayout extends FrameLayout extras.getBoolean(Notification.EXTRA_SHOW_REMOTE_INPUT_SPINNER, false); int unreadCount = extras.getInt(Notification.EXTRA_CONVERSATION_UNREAD_MESSAGE_COUNT); - final List<MessagingMessage> newMessagingMessages = - createMessages(newMessages, /* isHistoric= */false, usePrecomputedText); + List<MessagingMessage> newMessagingMessages; + mSummarizedContent = extras.getCharSequence(Notification.EXTRA_SUMMARIZED_CONTENT); + if (mSummarizedContent != null && mIsCollapsed) { + Notification.MessagingStyle.Message summary = + new Notification.MessagingStyle.Message(mSummarizedContent, 0, ""); + newMessagingMessages = createMessages(List.of(summary), false, usePrecomputedText); + } else { + newMessagingMessages = + createMessages(newMessages, /* isHistoric= */false, usePrecomputedText); + } final List<MessagingMessage> newHistoricMessagingMessages = createMessages(newHistoricMessages, /* isHistoric= */true, usePrecomputedText); @@ -463,7 +481,7 @@ public class ConversationLayout extends FrameLayout return new MessagingData(user, showSpinner, unreadCount, newHistoricMessagingMessages, newMessagingMessages, groups, senders, - conversationHeaderData); + conversationHeaderData, mSummarizedContent); } /** @@ -1622,6 +1640,9 @@ public class ConversationLayout extends FrameLayout @Nullable public CharSequence getConversationText() { + if (mSummarizedContent != null) { + return mSummarizedContent; + } if (mMessages.isEmpty()) { return null; } diff --git a/core/java/com/android/internal/widget/MessagingData.java b/core/java/com/android/internal/widget/MessagingData.java index fb1f28fb8ef3..cb5041efd10f 100644 --- a/core/java/com/android/internal/widget/MessagingData.java +++ b/core/java/com/android/internal/widget/MessagingData.java @@ -32,6 +32,7 @@ final class MessagingData { private final List<List<MessagingMessage>> mGroups; private final List<Person> mSenders; private final int mUnreadCount; + private final CharSequence mSummarization; private ConversationHeaderData mConversationHeaderData; @@ -41,8 +42,7 @@ final class MessagingData { List<Person> senders) { this(user, showSpinner, /* unreadCount= */0, historicMessagingMessages, newMessagingMessages, - groups, - senders, null); + groups, senders, null, null); } MessagingData(Person user, boolean showSpinner, @@ -51,7 +51,8 @@ final class MessagingData { List<MessagingMessage> newMessagingMessages, List<List<MessagingMessage>> groups, List<Person> senders, - @Nullable ConversationHeaderData conversationHeaderData) { + @Nullable ConversationHeaderData conversationHeaderData, + CharSequence summarization) { mUser = user; mShowSpinner = showSpinner; mUnreadCount = unreadCount; @@ -60,6 +61,7 @@ final class MessagingData { mGroups = groups; mSenders = senders; mConversationHeaderData = conversationHeaderData; + mSummarization = summarization; } public Person getUser() { @@ -94,4 +96,9 @@ final class MessagingData { public ConversationHeaderData getConversationHeaderData() { return mConversationHeaderData; } + + @Nullable + public CharSequence getSummarization() { + return mSummarization; + } } diff --git a/core/java/com/android/internal/widget/MessagingMessage.java b/core/java/com/android/internal/widget/MessagingMessage.java index a59ee77cc693..c7f22836dd93 100644 --- a/core/java/com/android/internal/widget/MessagingMessage.java +++ b/core/java/com/android/internal/widget/MessagingMessage.java @@ -24,7 +24,7 @@ import java.util.ArrayList; import java.util.Objects; /** - * A message of a {@link MessagingLayout}. + * A message or summary of a {@link MessagingLayout}. */ public interface MessagingMessage extends MessagingLinearLayout.MessagingChild { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt index 3c772fdbe0b2..356eedbc9a45 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt @@ -29,6 +29,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC +import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection @@ -242,4 +243,42 @@ class NotifUiAdjustmentProviderTest : SysuiTestCase() { // Then: need no re-inflation assertFalse(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) } + + @Test + @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI) + fun changeIsSummarization_needReInflation_newlySummarized() { + // Given: an Entry with no summarization + val oldAdjustment = adjustmentProvider.calculateAdjustment(entry) + assertThat(oldAdjustment.summarization).isNull() + + // When: the Entry now has a summarization + val rb = RankingBuilder(entry.ranking) + rb.setSummarization("summary!") + entry.ranking = rb.build() + val newAdjustment = adjustmentProvider.calculateAdjustment(entry) + assertThat(newAdjustment).isNotEqualTo(oldAdjustment) + + // Then: Need re-inflation + assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) + } + + @Test + @EnableFlags(android.app.Flags.FLAG_NM_SUMMARIZATION_UI) + fun changeIsSummarization_needReInflation_summarizationChanged() { + // Given: an Entry with no summarization + val rb = RankingBuilder(entry.ranking) + rb.setSummarization("summary!") + entry.ranking = rb.build() + val oldAdjustment = adjustmentProvider.calculateAdjustment(entry) + + // When: the Entry now has a new summarization + val rb2 = RankingBuilder(entry.ranking) + rb2.setSummarization("summary new!") + entry.ranking = rb2.build() + val newAdjustment = adjustmentProvider.calculateAdjustment(entry) + assertThat(newAdjustment).isNotEqualTo(oldAdjustment) + + // Then: Need re-inflation + assertTrue(NotifUiAdjustment.needReinflate(oldAdjustment, newAdjustment)) + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt index 3825c098ca5d..b6ef95893036 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt @@ -17,6 +17,7 @@ package com.android.systemui.statusbar.notification import android.app.Notification +import android.app.Notification.EXTRA_SUMMARIZED_CONTENT import android.content.Context import android.content.pm.LauncherApps import android.graphics.drawable.AnimatedImageDrawable @@ -66,6 +67,12 @@ constructor( messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo) shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label } } + if (NmSummarizationUiFlag.isEnabled) { + entry.sbn.notification.extras.putCharSequence( + EXTRA_SUMMARIZED_CONTENT, entry.ranking.summarization + ) + } + messagingStyle.unreadMessageCount = conversationNotificationManager.getUnreadCount(entry, recoveredBuilder) return messagingStyle diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt new file mode 100644 index 000000000000..feac0a514828 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2025 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 + +import android.app.Flags; +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** + * Helper for android.app.nm_summarization and android.nm_summarization_ui. The new functionality + * should be enabled if either flag is enabled. + */ +@Suppress("NOTHING_TO_INLINE") +object NmSummarizationUiFlag { + const val FLAG_DESC = "android.app.nm_summarization(_ui)" + + @JvmStatic + inline val isEnabled + get() = Flags.nmSummarizationUi() || Flags.nmSummarization() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_DESC) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = + RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_DESC) +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt index 331ef1c01596..aa5008b8416e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt @@ -40,6 +40,7 @@ internal constructor( @RedactionType val redactionType: Int, val isChildInGroup: Boolean, val isGroupSummary: Boolean, + val summarization: String?, ) { companion object { @JvmStatic @@ -61,6 +62,7 @@ internal constructor( AsyncGroupHeaderViewInflation.isEnabled && !oldAdjustment.isGroupSummary && newAdjustment.isGroupSummary -> true + oldAdjustment.summarization != newAdjustment.summarization -> true else -> false } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt index 97e55c19d2f4..465bc288cbc1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt @@ -152,5 +152,6 @@ constructor( }, isChildInGroup = entry.hasEverBeenGroupChild(), isGroupSummary = entry.hasEverBeenGroupSummary(), + summarization = entry.ranking.summarization ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java index 1ff0d9262476..92c10abff735 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/HybridConversationNotificationView.java @@ -288,14 +288,21 @@ public class HybridConversationNotificationView extends HybridNotificationView { public void setText( CharSequence titleText, CharSequence contentText, - CharSequence conversationSenderName + CharSequence conversationSenderName, + @Nullable String summarization ) { if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return; - if (conversationSenderName == null) { + if (summarization != null) { mConversationSenderName.setVisibility(GONE); + titleText = null; + contentText = summarization; } else { - mConversationSenderName.setVisibility(VISIBLE); - mConversationSenderName.setText(conversationSenderName); + if (conversationSenderName == null) { + mConversationSenderName.setVisibility(GONE); + } else { + mConversationSenderName.setVisibility(VISIBLE); + mConversationSenderName.setText(conversationSenderName); + } } // TODO (b/217799515): super.bind() doesn't use contentView, remove the contentView // argument when the flag is removed diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index c0dbb37c1b36..13ed6c449797 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -217,7 +217,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder messagingStyle, builder, row.getContext(), - false + false, + entry.getRanking().getSummarization() ); // If the messagingStyle is null, we want to inflate the normal view isConversation = viewModel.isConversation(); @@ -239,7 +240,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder messagingStyle, builder, row.getContext(), - true); + true, + entry.getRanking().getSummarization()); } else { result.mPublicInflatedSingleLineViewModel = SingleLineViewInflater.inflateRedactedSingleLineViewModel( @@ -1318,7 +1320,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder messagingStyle, recoveredBuilder, mContext, - false + false, + mEntry.getRanking().getSummarization() ); result.mInflatedSingleLineView = SingleLineViewInflater.inflatePrivateSingleLineView( @@ -1338,7 +1341,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder messagingStyle, recoveredBuilder, mContext, - true + true, + null ); } else { result.mPublicInflatedSingleLineViewModel = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index 0b299d965b09..f4aae6e288a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -719,6 +719,7 @@ constructor( builder = builder, systemUiContext = systemUiContext, redactText = false, + summarization = entry.ranking.summarization ) } else null @@ -735,6 +736,7 @@ constructor( builder = builder, systemUiContext = systemUiContext, redactText = true, + summarization = null ) } else { SingleLineViewInflater.inflateRedactedSingleLineViewModel( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt index fe2803bfc5d6..c051513ef3b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt @@ -61,6 +61,7 @@ internal object SingleLineViewInflater { builder: Notification.Builder, systemUiContext: Context, redactText: Boolean, + summarization: String? ): SingleLineViewModel { if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) { return SingleLineViewModel(null, null, null) @@ -108,6 +109,7 @@ internal object SingleLineViewInflater { conversationSenderName = if (isGroupConversation) conversationTextData?.senderName else null, avatar = conversationAvatar, + summarization = summarization ) return SingleLineViewModel( @@ -132,6 +134,7 @@ internal object SingleLineViewInflater { .ic_redacted_notification_single_line_icon ) ), + null ) } else { null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt index a17197c1f8ea..a50fc4c7986a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewbinder/SingleLineViewBinder.kt @@ -32,6 +32,7 @@ object SingleLineViewBinder { viewModel?.titleText, viewModel?.contentText, viewModel?.conversationData?.conversationSenderName, + viewModel?.conversationData?.summarization ) } else { // bind the title and content text views diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt index d583fa5d97ed..32ded25f18a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ui/viewmodel/SingleLineViewModel.kt @@ -46,6 +46,7 @@ data class SingleLineViewModel( data class ConversationData( val conversationSenderName: CharSequence?, val avatar: ConversationAvatar, + val summarization: String? ) /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt index 1eb88c5a5616..0457255fee4e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewBinderTest.kt @@ -19,10 +19,12 @@ import android.app.Notification import android.app.Person import android.platform.test.annotations.EnableFlags import android.testing.TestableLooper +import android.view.View.GONE import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.RankingBuilder import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_SINGLE_LINE import com.android.systemui.statusbar.notification.row.SingleLineViewInflater.inflatePrivateSingleLineView @@ -90,6 +92,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { builder = notificationBuilder, systemUiContext = context, redactText = false, + summarization = null ) // WHEN: binds the viewHolder @@ -151,6 +154,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { builder = notificationBuilder, systemUiContext = context, redactText = false, + summarization = null ) // WHEN: binds the view SingleLineViewBinder.bind(viewModel, view) @@ -200,6 +204,7 @@ class SingleLineViewBinderTest : SysuiTestCase() { builder = notificationBuilder, systemUiContext = context, redactText = false, + summarization = null ) // WHEN: binds the view with the view model SingleLineViewBinder.bind(viewModel, view) @@ -211,6 +216,70 @@ class SingleLineViewBinderTest : SysuiTestCase() { assertNull(viewModel.conversationData) } + @Test + @EnableFlags(AsyncHybridViewInflation.FLAG_NAME, android.app.Flags.FLAG_NM_SUMMARIZATION_UI, + android.app.Flags.FLAG_NM_SUMMARIZATION) + fun bindSummarizedGroupConversationSingleLineView() { + // GIVEN a row with a group conversation notification + val user = + Person.Builder() + .setName(USER_NAME) + .build() + val style = + Notification.MessagingStyle(user) + .addMessage(MESSAGE_TEXT, System.currentTimeMillis(), user) + .addMessage( + "How about lunch?", + System.currentTimeMillis(), + Person.Builder().setName("user2").build(), + ) + .setGroupConversation(true) + notificationBuilder.setStyle(style).setShortcutId(SHORTCUT_ID) + val notification = notificationBuilder.build() + val row = helper.createRow(notification) + val rb = RankingBuilder(row.entry.ranking) + rb.setSummarization("summary!") + row.entry.ranking = rb.build() + + val view = + inflatePrivateSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock(), + ) + as HybridConversationNotificationView + + val publicView = + inflatePublicSingleLineView( + isConversation = true, + reinflateFlags = FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE, + entry = row.entry, + context = context, + logger = mock(), + ) + as HybridConversationNotificationView + assertNotNull(publicView) + + val viewModel = + SingleLineViewInflater.inflateSingleLineViewModel( + notification = notification, + messagingStyle = style, + builder = notificationBuilder, + systemUiContext = context, + redactText = false, + summarization = "summary" + ) + // WHEN: binds the view + SingleLineViewBinder.bind(viewModel, view) + + // THEN: the single-line conversation view should only include summarization content + assertEquals(viewModel.conversationData?.summarization, view.textView.text) + assertEquals("", view.conversationSenderNameView.text) + assertEquals(GONE, view.conversationSenderNameView.visibility) + } + private companion object { const val CHANNEL_ID = "CHANNEL_ID" const val CONTENT_TITLE = "A Cool New Feature" @@ -218,5 +287,6 @@ class SingleLineViewBinderTest : SysuiTestCase() { const val USER_NAME = "USER_NAME" const val MESSAGE_TEXT = "MESSAGE_TEXT" const val SHORTCUT_ID = "Shortcut" + const val SUMMARIZATION = "summarization" } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt index ef70e277832e..13724a8b44da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt @@ -272,6 +272,35 @@ class SingleLineViewInflaterTest : SysuiTestCase() { } } + @Test + fun createViewModelForSummarizedConversationNotification() { + // Given: a non-group conversation notification + val notificationType = OneToOneConversation() + val notification = getNotification(notificationType) + + // When: inflate the SingleLineViewModel + val singleLineViewModel = notification.makeSingleLineViewModel(notificationType) + + // Then: the inflated SingleLineViewModel should be as expected + // titleText: Notification.ConversationTitle + // contentText: the last message text + // conversationSenderName: null, because it's not a group conversation + // conversationData.avatar: a single icon of the last sender + // summarizedText: the summary text from the ranking + assertEquals(CONVERSATION_TITLE, singleLineViewModel.titleText) + assertEquals(LAST_MESSAGE, singleLineViewModel.contentText) + assertNull( + singleLineViewModel.conversationData?.conversationSenderName, + "Sender name should be null for one-on-one conversation" + ) + assertTrue { + singleLineViewModel.conversationData + ?.avatar + ?.equalsTo(SingleIcon(firstSenderIcon.loadDrawable(context))) == true + } + assertEquals("summary", singleLineViewModel.conversationData?.summarization) + } + sealed class NotificationType(val largeIcon: Icon? = null) class NonMessaging(largeIcon: Icon? = null) : NotificationType(largeIcon) @@ -380,7 +409,8 @@ class SingleLineViewInflaterTest : SysuiTestCase() { if (isConversation) messagingStyle else null, builder, context, - false + false, + "summary" ) } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 0d3c18ac339f..d19eb235c9d5 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -8505,6 +8505,9 @@ public class NotificationManagerService extends SystemService { (userId == USER_ALL) ? USER_SYSTEM : userId); Notification.addFieldsFromContext(ai, notification); + // can't be set by an app + notification.extras.remove(Notification.EXTRA_SUMMARIZED_CONTENT); + if (notification.isForegroundService() && fgsPolicy == NOT_FOREGROUND_SERVICE) { notification.flags &= ~FLAG_FOREGROUND_SERVICE; } |