Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +3 −3 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ class NotifCoordinatorsImpl @Inject constructor( mediaCoordinator: MediaCoordinator, preparationCoordinator: PreparationCoordinator, remoteInputCoordinator: RemoteInputCoordinator, rowAlertTimeCoordinator: RowAlertTimeCoordinator, rowAppearanceCoordinator: RowAppearanceCoordinator, stackCoordinator: StackCoordinator, shadeEventCoordinator: ShadeEventCoordinator, Loading @@ -69,9 +70,7 @@ class NotifCoordinatorsImpl @Inject constructor( private val mCoordinators: MutableList<Coordinator> = ArrayList() private val mOrderedSections: MutableList<NotifSectioner> = ArrayList() /** * Creates all the coordinators. */ /** Creates all the coordinators. */ init { // Attach core coordinators. mCoreCoordinators.add(dataStoreCoordinator) Loading @@ -89,6 +88,7 @@ class NotifCoordinatorsImpl @Inject constructor( mCoordinators.add(groupCountCoordinator) mCoordinators.add(groupWhenCoordinator) mCoordinators.add(mediaCoordinator) mCoordinators.add(rowAlertTimeCoordinator) mCoordinators.add(rowAppearanceCoordinator) mCoordinators.add(stackCoordinator) mCoordinators.add(shadeEventCoordinator) Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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.collection.coordinator import android.util.ArrayMap import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.render.NotifRowController import javax.inject.Inject import kotlin.math.max /** * A small coordinator which ensures the "alerted" bell shows not just for recently alerted entries, * but also on the summary for every such entry. */ @CoordinatorScope class RowAlertTimeCoordinator @Inject constructor() : Coordinator { private val latestAlertTimeBySummary = ArrayMap<NotificationEntry, Long>() override fun attach(pipeline: NotifPipeline) { pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener) pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry) } private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) { latestAlertTimeBySummary.clear() entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry -> val summary = checkNotNull(groupEntry.summary) latestAlertTimeBySummary[summary] = groupEntry.calculateLatestAlertTime() } } private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) { // Show the "alerted" bell icon based on the latest group member for summaries val lastAudiblyAlerted = latestAlertTimeBySummary[entry] ?: entry.lastAudiblyAlertedMs controller.setLastAudibleMs(lastAudiblyAlerted) } private fun GroupEntry.calculateLatestAlertTime(): Long { val lastChildAlertedTime = children.maxOf { it.lastAudiblyAlertedMs } val summaryAlertedTime = checkNotNull(summary).lastAudiblyAlertedMs return max(lastChildAlertedTime, summaryAlertedTime) } } packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +0 −2 Original line number Diff line number Diff line Loading @@ -75,7 +75,5 @@ class RowAppearanceCoordinator @Inject internal constructor( (mAutoExpandFirstNotification && entry == entryToExpand)) // Show/hide the feedback icon controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry)) // Show the "alerted" bell icon controller.setLastAudibleMs(entry.lastAudiblyAlertedMs) } } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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.collection.coordinator import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener import com.android.systemui.statusbar.notification.collection.render.NotifRowController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations.initMocks @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class RowAlertTimeCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: RowAlertTimeCoordinator private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener private lateinit var afterRenderEntryListener: OnAfterRenderEntryListener @Mock private lateinit var pipeline: NotifPipeline @Before fun setUp() { initMocks(this) coordinator = RowAlertTimeCoordinator() coordinator.attach(pipeline) beforeFinalizeFilterListener = withArgCaptor { verify(pipeline).addOnBeforeFinalizeFilterListener(capture()) } afterRenderEntryListener = withArgCaptor { verify(pipeline).addOnAfterRenderEntryListener(capture()) } } @Test fun testSetLastAudiblyAlerted() { val entry1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(10).build() val entry2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(20).build() val summary = NotificationEntryBuilder().setLastAudiblyAlertedMs(5).build() val child1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(0).build() val child2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(8).build() val group = GroupEntryBuilder() .setKey("group") .setSummary(summary) .addChild(child1) .addChild(child2) .build() val entries = listOf(entry1, summary, child1, child2, entry2) beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry1, group, entry2)) val actualTimesSet = entries.associateWith { val rowController = mock<NotifRowController>() afterRenderEntryListener.onAfterRenderEntry(it, rowController) withArgCaptor<Long> { verify(rowController).setLastAudibleMs(capture()) verifyNoMoreInteractions(rowController) } } val expectedTimesSet = mapOf( entry1 to 10L, entry2 to 20L, summary to 8L, child1 to 0L, child2 to 8L, ) assertThat(actualTimesSet).containsExactlyEntriesIn(expectedTimesSet) } } packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt +1 −7 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { verify(pipeline).addOnAfterRenderEntryListener(capture()) } whenever(assistantFeedbackController.getFeedbackIcon(any())).thenReturn(FeedbackIcon(1, 2)) entry1 = NotificationEntryBuilder().setSection(section1).setLastAudiblyAlertedMs(17).build() entry1 = NotificationEntryBuilder().setSection(section1).build() entry2 = NotificationEntryBuilder().setSection(section2).build() } Loading @@ -102,12 +102,6 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { verify(controller2).setSystemExpanded(eq(false)) } @Test fun testSetLastAudiblyAlerted() { afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) verify(controller1).setLastAudibleMs(eq(17.toLong())) } @Test fun testSetFeedbackIcon() { afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) Loading Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt +3 −3 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ class NotifCoordinatorsImpl @Inject constructor( mediaCoordinator: MediaCoordinator, preparationCoordinator: PreparationCoordinator, remoteInputCoordinator: RemoteInputCoordinator, rowAlertTimeCoordinator: RowAlertTimeCoordinator, rowAppearanceCoordinator: RowAppearanceCoordinator, stackCoordinator: StackCoordinator, shadeEventCoordinator: ShadeEventCoordinator, Loading @@ -69,9 +70,7 @@ class NotifCoordinatorsImpl @Inject constructor( private val mCoordinators: MutableList<Coordinator> = ArrayList() private val mOrderedSections: MutableList<NotifSectioner> = ArrayList() /** * Creates all the coordinators. */ /** Creates all the coordinators. */ init { // Attach core coordinators. mCoreCoordinators.add(dataStoreCoordinator) Loading @@ -89,6 +88,7 @@ class NotifCoordinatorsImpl @Inject constructor( mCoordinators.add(groupCountCoordinator) mCoordinators.add(groupWhenCoordinator) mCoordinators.add(mediaCoordinator) mCoordinators.add(rowAlertTimeCoordinator) mCoordinators.add(rowAppearanceCoordinator) mCoordinators.add(stackCoordinator) mCoordinators.add(shadeEventCoordinator) Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinator.kt 0 → 100644 +61 −0 Original line number Diff line number Diff line /* * 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.collection.coordinator import android.util.ArrayMap import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.ListEntry import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope import com.android.systemui.statusbar.notification.collection.render.NotifRowController import javax.inject.Inject import kotlin.math.max /** * A small coordinator which ensures the "alerted" bell shows not just for recently alerted entries, * but also on the summary for every such entry. */ @CoordinatorScope class RowAlertTimeCoordinator @Inject constructor() : Coordinator { private val latestAlertTimeBySummary = ArrayMap<NotificationEntry, Long>() override fun attach(pipeline: NotifPipeline) { pipeline.addOnBeforeFinalizeFilterListener(::onBeforeFinalizeFilterListener) pipeline.addOnAfterRenderEntryListener(::onAfterRenderEntry) } private fun onBeforeFinalizeFilterListener(entries: List<ListEntry>) { latestAlertTimeBySummary.clear() entries.asSequence().filterIsInstance<GroupEntry>().forEach { groupEntry -> val summary = checkNotNull(groupEntry.summary) latestAlertTimeBySummary[summary] = groupEntry.calculateLatestAlertTime() } } private fun onAfterRenderEntry(entry: NotificationEntry, controller: NotifRowController) { // Show the "alerted" bell icon based on the latest group member for summaries val lastAudiblyAlerted = latestAlertTimeBySummary[entry] ?: entry.lastAudiblyAlertedMs controller.setLastAudibleMs(lastAudiblyAlerted) } private fun GroupEntry.calculateLatestAlertTime(): Long { val lastChildAlertedTime = children.maxOf { it.lastAudiblyAlertedMs } val summaryAlertedTime = checkNotNull(summary).lastAudiblyAlertedMs return max(lastChildAlertedTime, summaryAlertedTime) } }
packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt +0 −2 Original line number Diff line number Diff line Loading @@ -75,7 +75,5 @@ class RowAppearanceCoordinator @Inject internal constructor( (mAutoExpandFirstNotification && entry == entryToExpand)) // Show/hide the feedback icon controller.setFeedbackIcon(mAssistantFeedbackController.getFeedbackIcon(entry)) // Show the "alerted" bell icon controller.setLastAudibleMs(entry.lastAudiblyAlertedMs) } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAlertTimeCoordinatorTest.kt 0 → 100644 +99 −0 Original line number Diff line number Diff line /* * 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.collection.coordinator import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.notification.collection.GroupEntryBuilder import com.android.systemui.statusbar.notification.collection.NotifPipeline import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener import com.android.systemui.statusbar.notification.collection.listbuilder.OnBeforeFinalizeFilterListener import com.android.systemui.statusbar.notification.collection.render.NotifRowController import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.withArgCaptor import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations.initMocks @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper class RowAlertTimeCoordinatorTest : SysuiTestCase() { private lateinit var coordinator: RowAlertTimeCoordinator private lateinit var beforeFinalizeFilterListener: OnBeforeFinalizeFilterListener private lateinit var afterRenderEntryListener: OnAfterRenderEntryListener @Mock private lateinit var pipeline: NotifPipeline @Before fun setUp() { initMocks(this) coordinator = RowAlertTimeCoordinator() coordinator.attach(pipeline) beforeFinalizeFilterListener = withArgCaptor { verify(pipeline).addOnBeforeFinalizeFilterListener(capture()) } afterRenderEntryListener = withArgCaptor { verify(pipeline).addOnAfterRenderEntryListener(capture()) } } @Test fun testSetLastAudiblyAlerted() { val entry1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(10).build() val entry2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(20).build() val summary = NotificationEntryBuilder().setLastAudiblyAlertedMs(5).build() val child1 = NotificationEntryBuilder().setLastAudiblyAlertedMs(0).build() val child2 = NotificationEntryBuilder().setLastAudiblyAlertedMs(8).build() val group = GroupEntryBuilder() .setKey("group") .setSummary(summary) .addChild(child1) .addChild(child2) .build() val entries = listOf(entry1, summary, child1, child2, entry2) beforeFinalizeFilterListener.onBeforeFinalizeFilter(listOf(entry1, group, entry2)) val actualTimesSet = entries.associateWith { val rowController = mock<NotifRowController>() afterRenderEntryListener.onAfterRenderEntry(it, rowController) withArgCaptor<Long> { verify(rowController).setLastAudibleMs(capture()) verifyNoMoreInteractions(rowController) } } val expectedTimesSet = mapOf( entry1 to 10L, entry2 to 20L, summary to 8L, child1 to 0L, child2 to 8L, ) assertThat(actualTimesSet).containsExactlyEntriesIn(expectedTimesSet) } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt +1 −7 Original line number Diff line number Diff line Loading @@ -76,7 +76,7 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { verify(pipeline).addOnAfterRenderEntryListener(capture()) } whenever(assistantFeedbackController.getFeedbackIcon(any())).thenReturn(FeedbackIcon(1, 2)) entry1 = NotificationEntryBuilder().setSection(section1).setLastAudiblyAlertedMs(17).build() entry1 = NotificationEntryBuilder().setSection(section1).build() entry2 = NotificationEntryBuilder().setSection(section2).build() } Loading @@ -102,12 +102,6 @@ class RowAppearanceCoordinatorTest : SysuiTestCase() { verify(controller2).setSystemExpanded(eq(false)) } @Test fun testSetLastAudiblyAlerted() { afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) verify(controller1).setLastAudibleMs(eq(17.toLong())) } @Test fun testSetFeedbackIcon() { afterRenderEntryListener.onAfterRenderEntry(entry1, controller1) Loading