summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Julia Reynolds <juliacr@google.com> 2025-01-22 15:45:51 -0500
committer Julia Reynolds <juliacr@google.com> 2025-01-24 09:40:29 -0500
commitd49564bcc3e2f98f16d81410a05652e80078e430 (patch)
tree0d02cf5e218ea391b93e55cebb511078c011b14a
parent51dac5f7dfc2e12c0c7d8e872b55b09d18b9bb77 (diff)
Show summarized text, if any, in msg collapsed view
Test: NotificationAssistantServiceTest (with manual review) Bug: 390415390 Flag: android.app.nm_summarization_ui Change-Id: Id1a5b8ec7454144c77e4de7fc9d835260bf2dbf9
-rw-r--r--core/java/android/app/Notification.java5
-rw-r--r--core/java/com/android/internal/widget/ConversationLayout.java29
-rw-r--r--core/java/com/android/internal/widget/MessagingData.java13
-rw-r--r--core/java/com/android/internal/widget/MessagingMessage.java2
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NmSummarizationUiFlag.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt1
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java3
10 files changed, 143 insertions, 8 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/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;
}