summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt210
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java77
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt104
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt157
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt31
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt27
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt29
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt23
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt17
16 files changed, 783 insertions, 24 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
new file mode 100644
index 000000000000..6736ccf739ce
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorTest.kt
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import android.app.Notification
+import android.app.Notification.BigPictureStyle
+import android.app.Notification.BigTextStyle
+import android.app.Notification.CallStyle
+import android.app.Notification.MessagingStyle
+import android.app.Notification.ProgressStyle
+import android.app.Notification.ProgressStyle.Segment
+import android.app.PendingIntent
+import android.app.Person
+import android.content.Intent
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class PromotedNotificationContentExtractorTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+
+ private val provider =
+ FakePromotedNotificationsProvider().also { kosmos.promotedNotificationsProvider = it }
+
+ private val underTest = kosmos.promotedNotificationContentExtractor
+
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun shouldNotExtract_bothFlagsDisabled() {
+ val notif = createEntry().also { provider.promotedEntries.add(it) }
+ val content = extractContent(notif)
+ assertThat(content).isNull()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun shouldExtract_promotedNotificationUiFlagEnabled() {
+ val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val content = extractContent(entry)
+ assertThat(content).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun shouldExtract_statusBarNotifChipsFlagEnabled() {
+ val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val content = extractContent(entry)
+ assertThat(content).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun shouldExtract_bothFlagsEnabled() {
+ val entry = createEntry().also { provider.promotedEntries.add(it) }
+ val content = extractContent(entry)
+ assertThat(content).isNotNull()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun shouldNotExtract_providerDidNotPromote() {
+ val entry = createEntry().also { provider.promotedEntries.remove(it) }
+ val content = extractContent(entry)
+ assertThat(content).isNull()
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_commonFields() {
+ val entry =
+ createEntry {
+ setSubText(TEST_SUB_TEXT)
+ setContentTitle(TEST_CONTENT_TITLE)
+ setContentText(TEST_CONTENT_TEXT)
+ }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT)
+ assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE)
+ assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromBigPictureStyle() {
+ val entry =
+ createEntry { setStyle(BigPictureStyle()) }.also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.BigPicture)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromBigTextStyle() {
+ val entry =
+ createEntry { setStyle(BigTextStyle()) }.also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.BigText)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromCallStyle() {
+ val hangUpIntent =
+ PendingIntent.getBroadcast(context, 0, Intent("hangup"), PendingIntent.FLAG_IMMUTABLE)
+
+ val entry =
+ createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.Call)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromProgressStyle() {
+ val entry =
+ createEntry {
+ setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75))
+ }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.Progress)
+ assertThat(content?.progress).isNotNull()
+ assertThat(content?.progress?.progress).isEqualTo(75)
+ assertThat(content?.progress?.progressMax).isEqualTo(100)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun extractContent_fromIneligibleStyle() {
+ val entry =
+ createEntry {
+ setStyle(
+ MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON)
+ )
+ }
+ .also { provider.promotedEntries.add(it) }
+
+ val content = extractContent(entry)
+
+ assertThat(content).isNotNull()
+ assertThat(content?.style).isEqualTo(Style.Ineligible)
+ }
+
+ private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? {
+ val recoveredBuilder = Notification.Builder(context, entry.sbn.notification)
+ return underTest.extractContent(entry, recoveredBuilder)
+ }
+
+ private fun createEntry(builderBlock: Notification.Builder.() -> Unit = {}): NotificationEntry {
+ val notif = Notification.Builder(context, "a").also(builderBlock).build()
+ return NotificationEntryBuilder().setNotification(notif).build()
+ }
+
+ companion object {
+ private const val TEST_SUB_TEXT = "sub text"
+ private const val TEST_CONTENT_TITLE = "content title"
+ private const val TEST_CONTENT_TEXT = "content text"
+
+ private const val TEST_PERSON_NAME = "person name"
+ private const val TEST_PERSON_KEY = "person key"
+ private val TEST_PERSON =
+ Person.Builder().setKey(TEST_PERSON_KEY).setName(TEST_PERSON_NAME).build()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index b278f1a48b3d..6eb2764165b5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -42,6 +43,7 @@ import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.util.TypedValue;
@@ -56,8 +58,12 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -99,6 +105,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
@Mock private NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
@Mock private HeadsUpStyleProvider mHeadsUpStyleProvider;
@Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory;
+ @Mock private PromotedNotificationContentExtractor mPromotedNotificationContentExtractor;
private final SmartReplyStateInflater mSmartReplyStateInflater =
new SmartReplyStateInflater() {
@@ -142,6 +149,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mSmartReplyStateInflater,
mNotifLayoutInflaterFactoryProvider,
mHeadsUpStyleProvider,
+ mPromotedNotificationContentExtractor,
mock(NotificationRowContentBinderLogger.class));
}
@@ -382,6 +390,75 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
verify(mRow, times(0)).onNotificationUpdated();
}
+ @Test
+ @DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
+ public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception {
+ final PromotedNotificationContentModel content =
+ new PromotedNotificationContentModel.Builder("key").build();
+ when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content);
+
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+
+ verify(mPromotedNotificationContentExtractor, never()).extractContent(any(), any());
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled()
+ throws Exception {
+ final PromotedNotificationContentModel content =
+ new PromotedNotificationContentModel.Builder("key").build();
+ when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content);
+
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+
+ verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception {
+ final PromotedNotificationContentModel content =
+ new PromotedNotificationContentModel.Builder("key").build();
+ when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content);
+
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+
+ verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ }
+
+ @Test
+ @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
+ public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception {
+ final PromotedNotificationContentModel content =
+ new PromotedNotificationContentModel.Builder("key").build();
+ when(mPromotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content);
+
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+
+ verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel());
+ }
+
+ @Test
+ @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME})
+ public void testExtractsPromotedContent_null() throws Exception {
+ when(mPromotedNotificationContentExtractor.extractContent(any(), any())).thenReturn(null);
+
+ inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow);
+
+ verify(mPromotedNotificationContentExtractor, times(1)).extractContent(any(), any());
+ assertNull(mRow.getEntry().getPromotedNotificationContentModel());
+ }
+
private static void inflateAndWait(NotificationContentInflater inflater,
@InflationFlag int contentToInflate,
ExpandableNotificationRow row)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
index 48608ebd6de0..18517998096a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt
@@ -21,6 +21,7 @@ import android.content.Context
import android.os.AsyncTask
import android.os.Build
import android.os.CancellationSignal
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.TestableLooper.RunWithLooper
import android.util.TypedValue
@@ -33,8 +34,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED
@@ -62,6 +67,7 @@ import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.times
import org.mockito.kotlin.verify
@@ -82,7 +88,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
object : NotifLayoutInflaterFactory.Provider {
override fun provide(
row: ExpandableNotificationRow,
- layoutType: Int
+ layoutType: Int,
): NotifLayoutInflaterFactory = mock()
}
private val smartReplyStateInflater: SmartReplyStateInflater =
@@ -95,7 +101,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
notifPackageContext: Context,
entry: NotificationEntry,
existingSmartReplyState: InflatedSmartReplyState?,
- newSmartReplyState: InflatedSmartReplyState
+ newSmartReplyState: InflatedSmartReplyState,
): InflatedSmartReplyViewHolder {
return inflatedSmartReplies
}
@@ -104,6 +110,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
return inflatedSmartReplyState
}
}
+ private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor = mock()
@Before
fun setUp() {
@@ -125,7 +132,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
smartReplyStateInflater,
layoutInflaterFactoryProvider,
mock<HeadsUpStyleProvider>(),
- mock()
+ promotedNotificationContentExtractor,
+ mock(),
)
}
@@ -142,7 +150,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- smartReplyStateInflater
+ smartReplyStateInflater,
+ mock(),
)
verify(builder).createHeadsUpContentView(true)
}
@@ -160,7 +169,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
FLAG_CONTENT_VIEW_ALL,
builder,
mContext,
- smartReplyStateInflater
+ smartReplyStateInflater,
+ mock(),
)
verify(builder).createContentView(true)
}
@@ -187,7 +197,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
true /* expectingException */,
notificationInflater,
FLAG_CONTENT_VIEW_ALL,
- row
+ row,
)
Assert.assertTrue(row.privateLayout.childCount == 0)
verify(row, times(0)).onNotificationUpdated()
@@ -210,7 +220,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
FLAG_CONTENT_VIEW_ALL,
BindParams(),
false /* forceInflate */,
- null /* callback */
+ null, /* callback */
)
Assert.assertNull(row.entry.runningTask)
}
@@ -223,7 +233,8 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
NotificationRowContentBinderImpl.InflationProgress(
packageContext = mContext,
remoteViews = NewRemoteViews(),
- contentModel = NotificationContentModel(headsUpStatusBarModel)
+ contentModel = NotificationContentModel(headsUpStatusBarModel),
+ extractedPromotedNotificationContentModel = null,
)
val countDownLatch = CountDownLatch(1)
NotificationRowContentBinderImpl.applyRemoteView(
@@ -261,7 +272,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
get() =
AsyncFailRemoteView(
mContext.packageName,
- com.android.systemui.tests.R.layout.custom_view_dark
+ com.android.systemui.tests.R.layout.custom_view_dark,
)
},
logger = mock(),
@@ -280,7 +291,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
val decoratedMediaView = builder.createContentView()
Assert.assertFalse(
"The decorated media style doesn't allow a view to be reapplied!",
- NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView)
+ NotificationRowContentBinderImpl.canReapplyRemoteView(mediaView, decoratedMediaView),
)
}
@@ -304,7 +315,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
Assert.assertEquals(
"Binder inflated a new view even though the old one was cached and usable.",
view,
- row.privateLayout.contractedChild
+ row.privateLayout.contractedChild,
)
}
@@ -327,7 +338,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
Assert.assertNotEquals(
"Binder (somehow) used the same view when inflating.",
view,
- row.privateLayout.contractedChild
+ row.privateLayout.contractedChild,
)
}
@@ -396,7 +407,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
private fun getValidationError(
measuredHeightDp: Float,
targetSdk: Int,
- contentView: RemoteViews?
+ contentView: RemoteViews?,
): String? {
val view: View = mock()
whenever(view.measuredHeight)
@@ -404,7 +415,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
TypedValue.applyDimension(
COMPLEX_UNIT_SP,
measuredHeightDp,
- mContext.resources.displayMetrics
+ mContext.resources.displayMetrics,
)
.toInt()
)
@@ -419,7 +430,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
row.entry.sbn.notification.contentView =
RemoteViews(
mContext.packageName,
- com.android.systemui.tests.R.layout.invalid_notification_height
+ com.android.systemui.tests.R.layout.invalid_notification_height,
)
inflateAndWait(true, notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
Assert.assertEquals(0, row.privateLayout.childCount.toLong())
@@ -451,7 +462,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
false,
notificationInflater,
FLAG_CONTENT_VIEW_PUBLIC_SINGLE_LINE,
- messagingRow
+ messagingRow,
)
Assert.assertNotNull(messagingRow.publicLayout.mSingleLineView)
// assert this is the conversation layout
@@ -460,6 +471,59 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
)
}
+ @Test
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun testExtractsPromotedContent_notWhenBothFlagsDisabled() {
+ val content = PromotedNotificationContentModel.Builder("key").build()
+ whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ verify(promotedNotificationContentExtractor, never()).extractContent(any(), any())
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME)
+ @DisableFlags(StatusBarNotifChips.FLAG_NAME)
+ fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() {
+ val content = PromotedNotificationContentModel.Builder("key").build()
+ whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ }
+
+ @Test
+ @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+ @DisableFlags(PromotedNotificationUi.FLAG_NAME)
+ fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() {
+ val content = PromotedNotificationContentModel.Builder("key").build()
+ whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ }
+
+ @Test
+ @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME)
+ fun testExtractsPromotedContent_whenBothFlagsEnabled() {
+ val content = PromotedNotificationContentModel.Builder("key").build()
+ whenever(promotedNotificationContentExtractor.extractContent(any(), any()))
+ .thenReturn(content)
+
+ inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row)
+
+ verify(promotedNotificationContentExtractor, times(1)).extractContent(any(), any())
+ Assert.assertEquals(content, row.entry.promotedNotificationContentModel)
+ }
+
private class ExceptionHolder {
var exception: Exception? = null
}
@@ -476,7 +540,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
parent: ViewGroup,
executor: Executor,
listener: OnViewAppliedListener,
- handler: InteractionHandler?
+ handler: InteractionHandler?,
): CancellationSignal {
executor.execute { listener.onError(RuntimeException("Failed to inflate async")) }
return CancellationSignal()
@@ -486,7 +550,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
context: Context,
parent: ViewGroup,
executor: Executor,
- listener: OnViewAppliedListener
+ listener: OnViewAppliedListener,
): CancellationSignal {
return applyAsync(context, parent, executor, listener, null)
}
@@ -496,7 +560,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
private fun inflateAndWait(
inflater: NotificationRowContentBinderImpl,
@InflationFlag contentToInflate: Int,
- row: ExpandableNotificationRow
+ row: ExpandableNotificationRow,
) {
inflateAndWait(false /* expectingException */, inflater, contentToInflate, row)
}
@@ -535,7 +599,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() {
contentToInflate,
BindParams(),
false /* forceInflate */,
- callback /* callback */
+ callback, /* callback */
)
Assert.assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS))
exceptionHolder.exception?.let { throw it }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 5f492970f0e7..080ac3f8c697 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -82,6 +82,7 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
@@ -204,6 +205,7 @@ public class NotificationTestHelper {
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
+ mock(PromotedNotificationContentExtractor.class),
mock(NotificationRowContentBinderLogger.class))
: new NotificationContentInflater(
mock(NotifRemoteViewCache.class),
@@ -214,6 +216,7 @@ public class NotificationTestHelper {
new MockSmartReplyInflater(),
mock(NotifLayoutInflaterFactory.Provider.class),
mock(HeadsUpStyleProvider.class),
+ mock(PromotedNotificationContentExtractor.class),
mock(NotificationRowContentBinderLogger.class));
contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 6d4d40c43e85..6b84b6d07702 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -1076,7 +1076,7 @@ public final class NotificationEntry extends ListEntry {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
return mPromotedNotificationContentModel;
} else {
- Log.wtf(TAG, "getting promoted content without feature flag enabled");
+ Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable());
return null;
}
}
@@ -1090,7 +1090,7 @@ public final class NotificationEntry extends ListEntry {
if (PromotedNotificationContentModel.featureFlagEnabled()) {
this.mPromotedNotificationContentModel = promotedNotificationContentModel;
} else {
- Log.wtf(TAG, "setting promoted content without feature flag enabled");
+ Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index a16afe9dcef0..8a1371f1c415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -77,6 +77,10 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.logging.dagger.NotificationsLogModule;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProvider;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -101,6 +105,8 @@ import kotlin.coroutines.CoroutineContext;
import kotlinx.coroutines.CoroutineScope;
+import java.util.Optional;
+
import javax.inject.Provider;
/**
@@ -308,4 +314,22 @@ public interface NotificationsModule {
@IntoMap
@ClassKey(ZenModesCleanupStartable.class)
CoreStartable bindsZenModesCleanup(ZenModesCleanupStartable zenModesCleanup);
+
+ /**
+ * Provides {@link
+ * com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor} if
+ * one of the relevant feature flags is enabled.
+ */
+ @Provides
+ @SysUISingleton
+ static Optional<PromotedNotificationContentExtractor>
+ providePromotedNotificationContentExtractor(
+ PromotedNotificationsProvider provider, Context context,
+ PromotedNotificationLogger logger) {
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ return Optional.of(new PromotedNotificationContentExtractor(provider, context, logger));
+ } else {
+ return Optional.empty();
+ }
+ }
}
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
new file mode 100644
index 000000000000..f400d605cb43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import android.app.Notification
+import android.app.Notification.BigPictureStyle
+import android.app.Notification.BigTextStyle
+import android.app.Notification.CallStyle
+import android.app.Notification.EXTRA_CHRONOMETER_COUNT_DOWN
+import android.app.Notification.EXTRA_SUB_TEXT
+import android.app.Notification.EXTRA_TEXT
+import android.app.Notification.EXTRA_TITLE
+import android.app.Notification.ProgressStyle
+import android.content.Context
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
+import javax.inject.Inject
+
+@SysUISingleton
+class PromotedNotificationContentExtractor
+@Inject
+constructor(
+ private val promotedNotificationsProvider: PromotedNotificationsProvider,
+ private val context: Context,
+ private val logger: PromotedNotificationLogger,
+) {
+ fun extractContent(
+ entry: NotificationEntry,
+ recoveredBuilder: Notification.Builder,
+ ): PromotedNotificationContentModel? {
+ if (!PromotedNotificationContentModel.featureFlagEnabled()) {
+ logger.logExtractionSkipped(entry, "feature flags disabled")
+ return null
+ }
+
+ if (!promotedNotificationsProvider.shouldPromote(entry)) {
+ logger.logExtractionSkipped(entry, "shouldPromote returned false")
+ return null
+ }
+
+ val notification = entry.sbn.notification
+ if (notification == null) {
+ logger.logExtractionFailed(entry, "entry.sbn.notification is null")
+ return null
+ }
+
+ val contentBuilder = PromotedNotificationContentModel.Builder(entry.key)
+
+ // TODO: Pitch a fit if style is unsupported or mandatory fields are missing once
+ // FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips.
+
+ contentBuilder.skeletonSmallIcon = entry.icons.aodIcon?.sourceIcon
+ contentBuilder.appName = notification.loadHeaderAppName(context)
+ contentBuilder.subText = notification.subText()
+ contentBuilder.time = notification.extractWhen()
+ contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs
+ contentBuilder.profileBadgeResId = null // TODO
+ contentBuilder.title = notification.title()
+ contentBuilder.text = notification.text()
+ contentBuilder.skeletonLargeIcon = null // TODO
+
+ recoveredBuilder.style?.extractContent(contentBuilder)
+ ?: run { contentBuilder.style = Style.Ineligible }
+
+ return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) }
+ }
+}
+
+private fun Notification.title(): CharSequence? = extras?.getCharSequence(EXTRA_TITLE)
+
+private fun Notification.text(): CharSequence? = extras?.getCharSequence(EXTRA_TEXT)
+
+private fun Notification.subText(): String? = extras?.getString(EXTRA_SUB_TEXT)
+
+private fun Notification.chronometerCountDown(): Boolean =
+ extras?.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN, /* defaultValue= */ false) ?: false
+
+private fun Notification.extractWhen(): When? {
+ val time = `when`
+ val showsTime = showsTime()
+ val showsChronometer = showsChronometer()
+ val countDown = chronometerCountDown()
+
+ return when {
+ showsTime -> When(time, When.Mode.Absolute)
+ showsChronometer -> When(time, if (countDown) When.Mode.CountDown else When.Mode.CountUp)
+ else -> null
+ }
+}
+
+private fun Notification.Style.extractContent(
+ contentBuilder: PromotedNotificationContentModel.Builder
+) {
+ contentBuilder.style =
+ when (this) {
+ is BigPictureStyle -> {
+ extractContent(contentBuilder)
+ Style.BigPicture
+ }
+
+ is BigTextStyle -> {
+ extractContent(contentBuilder)
+ Style.BigText
+ }
+
+ is CallStyle -> {
+ extractContent(contentBuilder)
+ Style.Call
+ }
+
+ is ProgressStyle -> {
+ extractContent(contentBuilder)
+ Style.Progress
+ }
+
+ else -> Style.Ineligible
+ }
+}
+
+private fun BigPictureStyle.extractContent(
+ contentBuilder: PromotedNotificationContentModel.Builder
+) {
+ // TODO?
+}
+
+private fun BigTextStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) {
+ // TODO?
+}
+
+private fun CallStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) {
+ contentBuilder.personIcon = null // TODO
+ contentBuilder.personName = null // TODO
+ contentBuilder.verificationIcon = null // TODO
+ contentBuilder.verificationText = null // TODO
+}
+
+private fun ProgressStyle.extractContent(contentBuilder: PromotedNotificationContentModel.Builder) {
+ // TODO: Create NotificationProgressModel.toSkeleton, or something similar.
+ contentBuilder.progress = createProgressModel(0xffffffff.toInt(), 0x00000000)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
new file mode 100644
index 000000000000..13ad1413e89d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
+import javax.inject.Inject
+
+class PromotedNotificationLogger
+@Inject
+constructor(@NotificationLog private val buffer: LogBuffer) {
+ fun logExtractionSkipped(entry: NotificationEntry, reason: String) {
+ buffer.log(
+ EXTRACTION_TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ str2 = reason
+ },
+ { "extraction skipped: $str2 for $str1" },
+ )
+ }
+
+ fun logExtractionFailed(entry: NotificationEntry, reason: String) {
+ buffer.log(
+ EXTRACTION_TAG,
+ ERROR,
+ {
+ str1 = entry.logKey
+ str2 = reason
+ },
+ { "extraction failed: $str2 for $str1" },
+ )
+ }
+
+ fun logExtractionSucceeded(
+ entry: NotificationEntry,
+ content: PromotedNotificationContentModel,
+ ) {
+ buffer.log(
+ EXTRACTION_TAG,
+ INFO,
+ {
+ str1 = entry.logKey
+ str2 = content.toString()
+ },
+ { "extraction succeeded: $str2 for $str1" },
+ )
+ }
+}
+
+private const val EXTRACTION_TAG = "PromotedNotificationContentExtractor"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
index 691dc6f5ccac..947d9e3e9547 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProvider.kt
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted
import android.app.Notification.FLAG_PROMOTED_ONGOING
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import javax.inject.Inject
/** A provider for making decisions on which notifications should be promoted. */
@@ -30,7 +31,7 @@ interface PromotedNotificationsProvider {
@SysUISingleton
open class PromotedNotificationsProviderImpl @Inject constructor() : PromotedNotificationsProvider {
override fun shouldPromote(entry: NotificationEntry): Boolean {
- if (!PromotedNotificationUi.isEnabled) {
+ if (!PromotedNotificationContentModel.featureFlagEnabled()) {
return false
}
return (entry.sbn.notification.flags and FLAG_PROMOTED_ONGOING) != 0
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 41abac1d47f7..6e05e8e8b80e 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
@@ -54,6 +54,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor;
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel;
import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation;
import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation;
import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
@@ -92,6 +94,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final SmartReplyStateInflater mSmartReplyStateInflater;
private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private final HeadsUpStyleProvider mHeadsUpStyleProvider;
+ private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor;
private final NotificationRowContentBinderLogger mLogger;
@@ -105,6 +108,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
SmartReplyStateInflater smartRepliesInflater,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
HeadsUpStyleProvider headsUpStyleProvider,
+ PromotedNotificationContentExtractor promotedNotificationContentExtractor,
NotificationRowContentBinderLogger logger) {
NotificationRowContentBinderRefactor.assertInLegacyMode();
mRemoteViewCache = remoteViewCache;
@@ -115,6 +119,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mSmartReplyStateInflater = smartRepliesInflater;
mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
mHeadsUpStyleProvider = headsUpStyleProvider;
+ mPromotedNotificationContentExtractor = promotedNotificationContentExtractor;
mLogger = logger;
}
@@ -165,6 +170,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mSmartReplyStateInflater,
mNotifLayoutInflaterFactoryProvider,
mHeadsUpStyleProvider,
+ mPromotedNotificationContentExtractor,
mLogger);
if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground());
@@ -913,6 +919,11 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationContentView privateLayout = row.getPrivateLayout();
NotificationContentView publicLayout = row.getPublicLayout();
logger.logAsyncTaskProgress(entry, "finishing");
+
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ entry.setPromotedNotificationContentModel(result.mExtractedPromotedNotificationContent);
+ }
+
boolean setRepliesAndActions = true;
if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) {
if (result.inflatedContentView != null) {
@@ -1123,6 +1134,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final SmartReplyStateInflater mSmartRepliesInflater;
private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
private final HeadsUpStyleProvider mHeadsUpStyleProvider;
+ private final PromotedNotificationContentExtractor mPromotedNotificationContentExtractor;
private final NotificationRowContentBinderLogger mLogger;
private AsyncInflationTask(
@@ -1142,6 +1154,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
SmartReplyStateInflater smartRepliesInflater,
NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
HeadsUpStyleProvider headsUpStyleProvider,
+ PromotedNotificationContentExtractor promotedNotificationContentExtractor,
NotificationRowContentBinderLogger logger) {
mEntry = entry;
mRow = row;
@@ -1160,6 +1173,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mIsMediaInQS = isMediaFlagEnabled;
mNotifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider;
mHeadsUpStyleProvider = headsUpStyleProvider;
+ mPromotedNotificationContentExtractor = promotedNotificationContentExtractor;
mLogger = logger;
entry.setInflationTask(this);
}
@@ -1276,6 +1290,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
);
}
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ mLogger.logAsyncTaskProgress(mEntry, "extracting promoted notification content");
+ result.mExtractedPromotedNotificationContent = mPromotedNotificationContentExtractor
+ .extractContent(mEntry, recoveredBuilder);
+ mLogger.logAsyncTaskProgress(mEntry, "extracted promoted notification content: "
+ + result.mExtractedPromotedNotificationContent);
+ }
+
mLogger.logAsyncTaskProgress(mEntry,
"getting row image resolver (on wrong thread!)");
final NotificationInlineImageResolver imageResolver = mRow.getImageResolver();
@@ -1377,6 +1399,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting
static class InflationProgress {
+ PromotedNotificationContentModel mExtractedPromotedNotificationContent;
+
private RemoteViews newContentView;
private RemoteViews newHeadsUpView;
private RemoteViews newExpandedView;
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 d0c033bb10b0..c7d80e9d03ce 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
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.row
import android.annotation.SuppressLint
+import android.app.Flags
import android.app.Notification
import android.content.Context
import android.content.ContextWrapper
@@ -46,6 +47,8 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor
import com.android.systemui.statusbar.notification.InflationException
import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED
import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP
@@ -95,6 +98,7 @@ constructor(
private val smartReplyStateInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
+ private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
private val logger: NotificationRowContentBinderLogger,
) : NotificationRowContentBinder {
@@ -147,6 +151,7 @@ constructor(
/* isMediaFlagEnabled = */ smartReplyStateInflater,
notifLayoutInflaterFactoryProvider,
headsUpStyleProvider,
+ promotedNotificationContentExtractor,
logger,
)
if (inflateSynchronously) {
@@ -166,6 +171,7 @@ constructor(
builder: Notification.Builder,
packageContext: Context,
smartRepliesInflater: SmartReplyStateInflater,
+ promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
): InflationProgress {
val systemUIContext = row.context
val result =
@@ -182,6 +188,7 @@ constructor(
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
+ promotedNotificationContentExtractor = promotedNotificationContentExtractor,
logger = logger,
)
inflateSmartReplyViews(
@@ -372,6 +379,7 @@ constructor(
private val smartRepliesInflater: SmartReplyStateInflater,
private val notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
private val headsUpStyleProvider: HeadsUpStyleProvider,
+ private val promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
private val logger: NotificationRowContentBinderLogger,
) : AsyncTask<Void, Void, Result<InflationProgress>>(), InflationCallback, InflationTask {
private val context: Context
@@ -442,6 +450,7 @@ constructor(
notifLayoutInflaterFactoryProvider = notifLayoutInflaterFactoryProvider,
headsUpStyleProvider = headsUpStyleProvider,
conversationProcessor = conversationProcessor,
+ promotedNotificationContentExtractor = promotedNotificationContentExtractor,
logger = logger,
)
logger.logAsyncTaskProgress(
@@ -582,6 +591,7 @@ constructor(
@VisibleForTesting val packageContext: Context,
val remoteViews: NewRemoteViews,
val contentModel: NotificationContentModel,
+ val extractedPromotedNotificationContentModel: PromotedNotificationContentModel?,
) {
var inflatedContentView: View? = null
@@ -670,8 +680,23 @@ constructor(
notifLayoutInflaterFactoryProvider: NotifLayoutInflaterFactory.Provider,
headsUpStyleProvider: HeadsUpStyleProvider,
conversationProcessor: ConversationNotificationProcessor,
+ promotedNotificationContentExtractor: PromotedNotificationContentExtractor,
logger: NotificationRowContentBinderLogger,
): InflationProgress {
+ val promoted =
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ logger.logAsyncTaskProgress(entry, "extracting promoted notification content")
+ val extracted =
+ promotedNotificationContentExtractor.extractContent(entry, builder)
+ logger.logAsyncTaskProgress(
+ entry,
+ "extracted promoted notification content: {extracted}",
+ )
+ extracted
+ } else {
+ null
+ }
+
// process conversations and extract the messaging style
val messagingStyle =
if (entry.ranking.isConversation) {
@@ -734,6 +759,7 @@ constructor(
packageContext = packageContext,
remoteViews = remoteViews,
contentModel = contentModel,
+ extractedPromotedNotificationContentModel = promoted,
)
}
@@ -1393,6 +1419,11 @@ constructor(
logger.logAsyncTaskProgress(entry, "finishing")
entry.setContentModel(result.contentModel)
+ if (PromotedNotificationContentModel.featureFlagEnabled()) {
+ entry.promotedNotificationContentModel =
+ result.extractedPromotedNotificationContentModel
+ }
+
result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) }
setContentViewsFromRemoteViews(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
new file mode 100644
index 000000000000..88caf6e2ba30
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationsProvider.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+
+class FakePromotedNotificationsProvider : PromotedNotificationsProvider {
+ val promotedEntries = mutableSetOf<NotificationEntry>()
+
+ override fun shouldPromote(entry: NotificationEntry): Boolean {
+ return promotedEntries.contains(entry)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
new file mode 100644
index 000000000000..5e9f12b4b1cc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import android.content.applicationContext
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.promotedNotificationContentExtractor by
+ Kosmos.Fixture {
+ PromotedNotificationContentExtractor(
+ promotedNotificationsProvider,
+ applicationContext,
+ promotedNotificationLogger,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt
new file mode 100644
index 000000000000..2805d1e02356
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLoggerKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2024 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.promoted
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.log.logcatLogBuffer
+
+val Kosmos.promotedNotificationLogger by
+ Kosmos.Fixture { PromotedNotificationLogger(logcatLogBuffer("PromotedNotifLog")) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
index a7aa0b41a7aa..580f617a3cdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationsProviderKosmos.kt
@@ -18,4 +18,5 @@ package com.android.systemui.statusbar.notification.promoted
import com.android.systemui.kosmos.Kosmos
-val Kosmos.promotedNotificationsProvider by Kosmos.Fixture { PromotedNotificationsProviderImpl() }
+var Kosmos.promotedNotificationsProvider: PromotedNotificationsProvider by
+ Kosmos.Fixture { PromotedNotificationsProviderImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
index 6f24f2a00556..2d3f68faf4b7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowBuilder.kt
@@ -36,6 +36,7 @@ import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FakeFeatureFlagsClassic
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.media.controls.util.MediaFeatureFlag
import com.android.systemui.media.dialog.MediaOutputDialogManager
import com.android.systemui.plugins.ActivityStarter
@@ -64,6 +65,9 @@ import com.android.systemui.statusbar.notification.headsup.HeadsUpManager
import com.android.systemui.statusbar.notification.icon.IconBuilder
import com.android.systemui.statusbar.notification.icon.IconManager
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationLogger
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationsProviderImpl
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.CoordinateOnClickListener
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener
@@ -221,6 +225,17 @@ class ExpandableNotificationRowBuilder(
Mockito.mock(ConversationNotificationManager::class.java, STUB_ONLY),
)
+ val promotedNotificationsProvider = PromotedNotificationsProviderImpl()
+ val promotedNotificationLog = logcatLogBuffer("PromotedNotifLog")
+ val promotedNotificationLogger = PromotedNotificationLogger(promotedNotificationLog)
+
+ val promotedNotificationContentExtractor =
+ PromotedNotificationContentExtractor(
+ promotedNotificationsProvider,
+ context,
+ promotedNotificationLogger,
+ )
+
mContentBinder =
if (NotificationRowContentBinderRefactor.isEnabled)
NotificationRowContentBinderImpl(
@@ -231,6 +246,7 @@ class ExpandableNotificationRowBuilder(
smartReplyStateInflater,
notifLayoutInflaterFactoryProvider,
Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY),
+ promotedNotificationContentExtractor,
Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY),
)
else
@@ -243,6 +259,7 @@ class ExpandableNotificationRowBuilder(
smartReplyStateInflater,
notifLayoutInflaterFactoryProvider,
Mockito.mock(HeadsUpStyleProvider::class.java, STUB_ONLY),
+ promotedNotificationContentExtractor,
Mockito.mock(NotificationRowContentBinderLogger::class.java, STUB_ONLY),
)
mContentBinder.setInflateSynchronously(true)