Loading packages/SystemUI/aconfig/systemui.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,13 @@ flag { bug: "316404716" } flag { name: "priority_people_section" namespace: "systemui" description: "Add a new section for priority people (aka important conversations)." bug: "340294566" } flag { name: "notification_minimalism_prototype" namespace: "systemui" Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt→packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +88 −67 Original line number Diff line number Diff line Loading @@ -23,9 +23,8 @@ import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_LOW import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntry Loading @@ -44,25 +43,29 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifierImpl import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidTestingRunner::class) @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: Loading @@ -72,22 +75,20 @@ class ConversationCoordinatorTest : SysuiTestCase() { private lateinit var peopleComparator: NotifComparator private lateinit var beforeRenderListListener: OnBeforeRenderListListener private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier private lateinit var peopleAlertingSection: NotifSection @Mock private lateinit var pipeline: NotifPipeline @Mock private lateinit var conversationIconManager: ConversationIconManager @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier @Mock private lateinit var channel: NotificationChannel @Mock private lateinit var headerController: NodeController private lateinit var entry: NotificationEntry private lateinit var entryA: NotificationEntry private lateinit var entryB: NotificationEntry private lateinit var coordinator: ConversationCoordinator @Rule @JvmField public val setFlagsRule = SetFlagsRule() @Before fun setUp() { MockitoAnnotations.initMocks(this) peopleNotificationIdentifier = PeopleNotificationIdentifierImpl(mock(), GroupMembershipManagerImpl()) coordinator = ConversationCoordinator( peopleNotificationIdentifier, Loading @@ -95,7 +96,6 @@ class ConversationCoordinatorTest : SysuiTestCase() { HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()), headerController ) whenever(channel.isImportantConversation).thenReturn(true) coordinator.attach(pipeline) Loading @@ -110,49 +110,65 @@ class ConversationCoordinatorTest : SysuiTestCase() { if (!SortBySectionTimeFlag.isEnabled) peopleComparator = peopleAlertingSectioner.comparator!! entry = NotificationEntryBuilder().setChannel(channel).build() peopleAlertingSection = NotifSection(peopleAlertingSectioner, 0) } val section = NotifSection(peopleAlertingSectioner, 0) entryA = NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build() entryB = NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build() @Test fun priorityPeopleSectionerClaimsOnlyImportantConversations() { val sectioner = coordinator.priorityPeopleSectioner assertTrue(sectioner.isInSection(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_FULL_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_NON_PERSON))) assertFalse(sectioner.isInSection(NotificationEntryBuilder().build())) } @Test fun testPromotesImportantConversations() { // only promote important conversations assertTrue(promoter.shouldPromoteToTopLevel(entry)) assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_NON_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build())) } @Test fun testPromotedImportantConversationsMakesSummaryUnimportant() { val altChildA = NotificationEntryBuilder().setTag("A").build() val altChildB = NotificationEntryBuilder().setTag("B").build() val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build() val importantChannel = mock<NotificationChannel>().also { whenever(it.isImportantConversation).thenReturn(true) } val otherChannel = mock<NotificationChannel>().also { whenever(it.isImportantConversation).thenReturn(false) } val importantChild = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel) } val altChildA = makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("A") } val altChildB = makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("B") } val summary = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel).setId(2) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) .setChildren(listOf(entry, altChildA, altChildB)) .setChildren(listOf(importantChild, altChildA, altChildB)) .build() assertTrue(promoter.shouldPromoteToTopLevel(entry)) assertTrue(promoter.shouldPromoteToTopLevel(importantChild)) assertFalse(promoter.shouldPromoteToTopLevel(altChildA)) assertFalse(promoter.shouldPromoteToTopLevel(altChildB)) NotificationEntryBuilder.setNewParent(entry, GroupEntry.ROOT_ENTRY) GroupEntryBuilder.getRawChildren(groupEntry).remove(entry) beforeRenderListListener.onBeforeRenderList(listOf(entry, groupEntry)) NotificationEntryBuilder.setNewParent(importantChild, GroupEntry.ROOT_ENTRY) GroupEntryBuilder.getRawChildren(groupEntry).remove(importantChild) beforeRenderListListener.onBeforeRenderList(listOf(importantChild, groupEntry)) verify(conversationIconManager).setUnimportantConversations(eq(listOf(summary.key))) } @Test fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() { // GIVEN val alertingEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry)) .thenReturn(TYPE_PERSON) val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) } // put alerting people notifications in this section assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue() Loading @@ -162,10 +178,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInAlertingPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN val silentEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) .thenReturn(TYPE_PERSON) val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in alerting section assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isTrue() Loading @@ -175,10 +188,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN val silentEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) .thenReturn(TYPE_PERSON) val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in this section assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue() Loading @@ -191,18 +201,14 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test fun testNotInPeopleSection() { // GIVEN val entry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() val entry = makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_LOW) } val importantEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry)) .thenReturn(TYPE_NON_PERSON) makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_HIGH) } // THEN - only put people notification either silent or alerting if (!SortBySectionTimeFlag.isEnabled) if (!SortBySectionTimeFlag.isEnabled) { assertThat(peopleSilentSectioner.isInSection(entry)).isFalse() } assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse() } Loading @@ -210,22 +216,16 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() { // GIVEN val altChildA = NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build() val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build() val summary = NotificationEntryBuilder() .setId(2) .setImportance(IMPORTANCE_LOW) .setChannel(channel) .build() makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("A").setImportance(IMPORTANCE_DEFAULT) } val altChildB = makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("B").setImportance(IMPORTANCE_LOW) } val summary = makeEntryOfPeopleType(TYPE_PERSON) { setId(2).setImportance(IMPORTANCE_LOW) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) .setChildren(listOf(altChildA, altChildB)) .build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary)) .thenReturn(TYPE_PERSON) // THEN assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue() } Loading @@ -233,10 +233,12 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorPutsImportantPeopleFirst() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_IMPORTANT_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) .thenReturn(TYPE_PERSON) val entryA = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setSection(peopleAlertingSection).setTag("A") } val entryB = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1) Loading @@ -245,10 +247,10 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorEquatesPeopleWithSameType() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) .thenReturn(TYPE_PERSON) val entryA = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("A") } val entryB = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0) Loading @@ -259,4 +261,23 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testNoSecondarySortForConversations() { assertThat(peopleAlertingSectioner.comparator).isNull() } private fun makeEntryOfPeopleType( @PeopleNotificationType type: Int, buildBlock: NotificationEntryBuilder.() -> Unit = {} ): NotificationEntry { val channel: NotificationChannel = mock() whenever(channel.isImportantConversation).thenReturn(type == TYPE_IMPORTANT_PERSON) val entry = NotificationEntryBuilder() .updateRanking { it.setIsConversation(type != TYPE_NON_PERSON) it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null) it.setChannel(channel) } .also(buildBlock) .build() assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry)) return entry } } packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +3 −0 Original line number Diff line number Diff line Loading @@ -29,11 +29,13 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** A class in which engineers can define flag dependencies */ Loading @@ -49,6 +51,7 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token // SceneContainer dependencies SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +9 −0 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.notification.dagger.PeopleHeader import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE import javax.inject.Inject /** Loading Loading @@ -81,6 +83,13 @@ class ConversationCoordinator @Inject constructor( } } val priorityPeopleSectioner = object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean { return getPeopleType(entry) == TYPE_IMPORTANT_PERSON } } // TODO(b/330193582): Rename to just "People" val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean { Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +4 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** Loading Loading @@ -120,6 +121,9 @@ constructor( if (NotificationMinimalismPrototype.V2.isEnabled) { mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS) } if (PriorityPeopleSection.isEnabled) { mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People } mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting if (!SortBySectionTimeFlag.isEnabled) { mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +7 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,13 @@ flag { bug: "316404716" } flag { name: "priority_people_section" namespace: "systemui" description: "Add a new section for priority people (aka important conversations)." bug: "340294566" } flag { name: "notification_minimalism_prototype" namespace: "systemui" Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt→packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt +88 −67 Original line number Diff line number Diff line Loading @@ -23,9 +23,8 @@ import android.app.NotificationManager.IMPORTANCE_HIGH import android.app.NotificationManager.IMPORTANCE_LOW import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntry Loading @@ -44,25 +43,29 @@ import com.android.systemui.statusbar.notification.collection.render.GroupMember import com.android.systemui.statusbar.notification.collection.render.NodeController import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_FULL_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifierImpl import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations import org.mockito.kotlin.mock @SmallTest @RunWith(AndroidTestingRunner::class) @RunWith(AndroidJUnit4::class) @TestableLooper.RunWithLooper class ConversationCoordinatorTest : SysuiTestCase() { // captured listeners and pluggables: Loading @@ -72,22 +75,20 @@ class ConversationCoordinatorTest : SysuiTestCase() { private lateinit var peopleComparator: NotifComparator private lateinit var beforeRenderListListener: OnBeforeRenderListListener private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier private lateinit var peopleAlertingSection: NotifSection @Mock private lateinit var pipeline: NotifPipeline @Mock private lateinit var conversationIconManager: ConversationIconManager @Mock private lateinit var peopleNotificationIdentifier: PeopleNotificationIdentifier @Mock private lateinit var channel: NotificationChannel @Mock private lateinit var headerController: NodeController private lateinit var entry: NotificationEntry private lateinit var entryA: NotificationEntry private lateinit var entryB: NotificationEntry private lateinit var coordinator: ConversationCoordinator @Rule @JvmField public val setFlagsRule = SetFlagsRule() @Before fun setUp() { MockitoAnnotations.initMocks(this) peopleNotificationIdentifier = PeopleNotificationIdentifierImpl(mock(), GroupMembershipManagerImpl()) coordinator = ConversationCoordinator( peopleNotificationIdentifier, Loading @@ -95,7 +96,6 @@ class ConversationCoordinatorTest : SysuiTestCase() { HighPriorityProvider(peopleNotificationIdentifier, GroupMembershipManagerImpl()), headerController ) whenever(channel.isImportantConversation).thenReturn(true) coordinator.attach(pipeline) Loading @@ -110,49 +110,65 @@ class ConversationCoordinatorTest : SysuiTestCase() { if (!SortBySectionTimeFlag.isEnabled) peopleComparator = peopleAlertingSectioner.comparator!! entry = NotificationEntryBuilder().setChannel(channel).build() peopleAlertingSection = NotifSection(peopleAlertingSectioner, 0) } val section = NotifSection(peopleAlertingSectioner, 0) entryA = NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("A").build() entryB = NotificationEntryBuilder().setChannel(channel).setSection(section).setTag("B").build() @Test fun priorityPeopleSectionerClaimsOnlyImportantConversations() { val sectioner = coordinator.priorityPeopleSectioner assertTrue(sectioner.isInSection(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_FULL_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_PERSON))) assertFalse(sectioner.isInSection(makeEntryOfPeopleType(TYPE_NON_PERSON))) assertFalse(sectioner.isInSection(NotificationEntryBuilder().build())) } @Test fun testPromotesImportantConversations() { // only promote important conversations assertTrue(promoter.shouldPromoteToTopLevel(entry)) assertTrue(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_FULL_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(makeEntryOfPeopleType(TYPE_NON_PERSON))) assertFalse(promoter.shouldPromoteToTopLevel(NotificationEntryBuilder().build())) } @Test fun testPromotedImportantConversationsMakesSummaryUnimportant() { val altChildA = NotificationEntryBuilder().setTag("A").build() val altChildB = NotificationEntryBuilder().setTag("B").build() val summary = NotificationEntryBuilder().setId(2).setChannel(channel).build() val importantChannel = mock<NotificationChannel>().also { whenever(it.isImportantConversation).thenReturn(true) } val otherChannel = mock<NotificationChannel>().also { whenever(it.isImportantConversation).thenReturn(false) } val importantChild = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel) } val altChildA = makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("A") } val altChildB = makeEntryOfPeopleType(TYPE_FULL_PERSON) { setChannel(otherChannel).setTag("B") } val summary = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setChannel(importantChannel).setId(2) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) .setChildren(listOf(entry, altChildA, altChildB)) .setChildren(listOf(importantChild, altChildA, altChildB)) .build() assertTrue(promoter.shouldPromoteToTopLevel(entry)) assertTrue(promoter.shouldPromoteToTopLevel(importantChild)) assertFalse(promoter.shouldPromoteToTopLevel(altChildA)) assertFalse(promoter.shouldPromoteToTopLevel(altChildB)) NotificationEntryBuilder.setNewParent(entry, GroupEntry.ROOT_ENTRY) GroupEntryBuilder.getRawChildren(groupEntry).remove(entry) beforeRenderListListener.onBeforeRenderList(listOf(entry, groupEntry)) NotificationEntryBuilder.setNewParent(importantChild, GroupEntry.ROOT_ENTRY) GroupEntryBuilder.getRawChildren(groupEntry).remove(importantChild) beforeRenderListListener.onBeforeRenderList(listOf(importantChild, groupEntry)) verify(conversationIconManager).setUnimportantConversations(eq(listOf(summary.key))) } @Test fun testInAlertingPeopleSectionWhenTheImportanceIsAtLeastDefault() { // GIVEN val alertingEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_DEFAULT).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(alertingEntry)) .thenReturn(TYPE_PERSON) val alertingEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_DEFAULT) } // put alerting people notifications in this section assertThat(peopleAlertingSectioner.isInSection(alertingEntry)).isTrue() Loading @@ -162,10 +178,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @EnableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInAlertingPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN val silentEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) .thenReturn(TYPE_PERSON) val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in alerting section assertThat(peopleAlertingSectioner.isInSection(silentEntry)).isTrue() Loading @@ -175,10 +188,7 @@ class ConversationCoordinatorTest : SysuiTestCase() { @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testInSilentPeopleSectionWhenTheImportanceIsLowerThanDefault() { // GIVEN val silentEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(silentEntry)) .thenReturn(TYPE_PERSON) val silentEntry = makeEntryOfPeopleType(TYPE_PERSON) { setImportance(IMPORTANCE_LOW) } // THEN put silent people notifications in this section assertThat(peopleSilentSectioner.isInSection(silentEntry)).isTrue() Loading @@ -191,18 +201,14 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test fun testNotInPeopleSection() { // GIVEN val entry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_LOW).build() val entry = makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_LOW) } val importantEntry = NotificationEntryBuilder().setChannel(channel).setImportance(IMPORTANCE_HIGH).build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(entry)) .thenReturn(TYPE_NON_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(importantEntry)) .thenReturn(TYPE_NON_PERSON) makeEntryOfPeopleType(TYPE_NON_PERSON) { setImportance(IMPORTANCE_HIGH) } // THEN - only put people notification either silent or alerting if (!SortBySectionTimeFlag.isEnabled) if (!SortBySectionTimeFlag.isEnabled) { assertThat(peopleSilentSectioner.isInSection(entry)).isFalse() } assertThat(peopleAlertingSectioner.isInSection(importantEntry)).isFalse() } Loading @@ -210,22 +216,16 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testInAlertingPeopleSectionWhenThereIsAnImportantChild() { // GIVEN val altChildA = NotificationEntryBuilder().setTag("A").setImportance(IMPORTANCE_DEFAULT).build() val altChildB = NotificationEntryBuilder().setTag("B").setImportance(IMPORTANCE_LOW).build() val summary = NotificationEntryBuilder() .setId(2) .setImportance(IMPORTANCE_LOW) .setChannel(channel) .build() makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("A").setImportance(IMPORTANCE_DEFAULT) } val altChildB = makeEntryOfPeopleType(TYPE_NON_PERSON) { setTag("B").setImportance(IMPORTANCE_LOW) } val summary = makeEntryOfPeopleType(TYPE_PERSON) { setId(2).setImportance(IMPORTANCE_LOW) } val groupEntry = GroupEntryBuilder() .setParent(GroupEntry.ROOT_ENTRY) .setSummary(summary) .setChildren(listOf(altChildA, altChildB)) .build() whenever(peopleNotificationIdentifier.getPeopleNotificationType(summary)) .thenReturn(TYPE_PERSON) // THEN assertThat(peopleAlertingSectioner.isInSection(groupEntry)).isTrue() } Loading @@ -233,10 +233,12 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorPutsImportantPeopleFirst() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_IMPORTANT_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) .thenReturn(TYPE_PERSON) val entryA = makeEntryOfPeopleType(TYPE_IMPORTANT_PERSON) { setSection(peopleAlertingSection).setTag("A") } val entryB = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(-1) Loading @@ -245,10 +247,10 @@ class ConversationCoordinatorTest : SysuiTestCase() { @Test @DisableFlags(Flags.FLAG_SORT_SECTION_BY_TIME) fun testComparatorEquatesPeopleWithSameType() { whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA)) .thenReturn(TYPE_PERSON) whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryB)) .thenReturn(TYPE_PERSON) val entryA = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("A") } val entryB = makeEntryOfPeopleType(TYPE_PERSON) { setSection(peopleAlertingSection).setTag("B") } // only put people notifications in this section assertThat(peopleComparator.compare(entryA, entryB)).isEqualTo(0) Loading @@ -259,4 +261,23 @@ class ConversationCoordinatorTest : SysuiTestCase() { fun testNoSecondarySortForConversations() { assertThat(peopleAlertingSectioner.comparator).isNull() } private fun makeEntryOfPeopleType( @PeopleNotificationType type: Int, buildBlock: NotificationEntryBuilder.() -> Unit = {} ): NotificationEntry { val channel: NotificationChannel = mock() whenever(channel.isImportantConversation).thenReturn(type == TYPE_IMPORTANT_PERSON) val entry = NotificationEntryBuilder() .updateRanking { it.setIsConversation(type != TYPE_NON_PERSON) it.setShortcutInfo(if (type >= TYPE_FULL_PERSON) mock() else null) it.setChannel(channel) } .also(buildBlock) .build() assertEquals(type, peopleNotificationIdentifier.getPeopleNotificationType(entry)) return entry } }
packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt +3 −0 Original line number Diff line number Diff line Loading @@ -29,11 +29,13 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** A class in which engineers can define flag dependencies */ Loading @@ -49,6 +51,7 @@ class FlagDependencies @Inject constructor(featureFlags: FeatureFlagsClassic, ha NotificationsLiveDataStoreRefactor.token dependsOn NotificationIconContainerRefactor.token FooterViewRefactor.token dependsOn NotificationIconContainerRefactor.token NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token // SceneContainer dependencies SceneContainerFlag.getFlagDependencies().forEach { (alpha, beta) -> alpha dependsOn beta } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt +9 −0 Original line number Diff line number Diff line Loading @@ -31,8 +31,10 @@ import com.android.systemui.statusbar.notification.dagger.PeopleHeader import com.android.systemui.statusbar.notification.icon.ConversationIconManager import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.PeopleNotificationType import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_NON_PERSON import com.android.systemui.statusbar.notification.stack.BUCKET_PEOPLE import com.android.systemui.statusbar.notification.stack.BUCKET_PRIORITY_PEOPLE import javax.inject.Inject /** Loading Loading @@ -81,6 +83,13 @@ class ConversationCoordinator @Inject constructor( } } val priorityPeopleSectioner = object : NotifSectioner("Priority People", BUCKET_PRIORITY_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean { return getPeopleType(entry) == TYPE_IMPORTANT_PERSON } } // TODO(b/330193582): Rename to just "People" val peopleAlertingSectioner = object : NotifSectioner("People(alerting)", BUCKET_PEOPLE) { override fun isInSection(entry: ListEntry): Boolean { Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +4 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import com.android.systemui.statusbar.notification.collection.listbuilder.plugga import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.shared.NotificationMinimalismPrototype import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor import com.android.systemui.statusbar.notification.shared.PriorityPeopleSection import javax.inject.Inject /** Loading Loading @@ -120,6 +121,9 @@ constructor( if (NotificationMinimalismPrototype.V2.isEnabled) { mOrderedSections.add(keyguardCoordinator.unseenNotifSectioner) // Unseen (FGS) } if (PriorityPeopleSection.isEnabled) { mOrderedSections.add(conversationCoordinator.priorityPeopleSectioner) // Priority People } mOrderedSections.add(conversationCoordinator.peopleAlertingSectioner) // People Alerting if (!SortBySectionTimeFlag.isEnabled) { mOrderedSections.add(conversationCoordinator.peopleSilentSectioner) // People Silent Loading