Loading packages/SystemUI/aconfig/systemui.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -892,3 +892,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "media_controls_user_initiated_dismiss" namespace: "systemui" description: "Only dismiss media notifications when the control was removed by the user." bug: "335875159" metadata { purpose: PURPOSE_BUGFIX } } packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ class CommunalMediaRepositoryImplTest : SysuiTestCase() { // Change to media unavailable and notify the listener. whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false) mediaDataListenerCaptor.value.onMediaDataRemoved("key") mediaDataListenerCaptor.value.onMediaDataRemoved("key", false) runCurrent() // Media active now returns false. Loading packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -27,12 +27,16 @@ import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.bluetooth.mockBroadcastDialogController import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.data.repository.mediaDataRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.util.mediaInstanceId import com.android.systemui.media.mediaOutputDialogManager Loading Loading @@ -211,6 +215,21 @@ class MediaControlInteractorTest : SysuiTestCase() { ) } @Test fun removeMediaControl() { val listener = mock<MediaDataProcessor.Listener>() kosmos.mediaDataProcessor.addInternalListener(listener) var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST) kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData) underTest.removeMediaControl(null, instanceId, 0L) kosmos.fakeExecutor.advanceClockToNext() kosmos.fakeExecutor.runAllReady() verify(listener).onMediaDataRemoved(eq(KEY), eq(true)) } companion object { private const val USER_ID = 0 private const val KEY = "key" Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt 0 → 100644 +117 −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 import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.service.notification.NotificationListenerService import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.dump.dumpManager import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.mockNotifCollection import com.android.systemui.statusbar.notification.collection.notifPipeline import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class NotificationMediaManagerTest : SysuiTestCase() { private val KEY = "KEY" private val kosmos = testKosmos() private val visibilityProvider = kosmos.notificationVisibilityProvider private val notifPipeline = kosmos.notifPipeline private val notifCollection = kosmos.mockNotifCollection private val dumpManager = kosmos.dumpManager private val mediaDataManager = mock<MediaDataManager>() private val backgroundExecutor = FakeExecutor(FakeSystemClock()) private var listenerCaptor = argumentCaptor<MediaDataManager.Listener>() private lateinit var notificationMediaManager: NotificationMediaManager @Before fun setup() { notificationMediaManager = NotificationMediaManager( context, visibilityProvider, notifPipeline, notifCollection, mediaDataManager, dumpManager, backgroundExecutor, ) verify(mediaDataManager).addListener(listenerCaptor.capture()) } @Test @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_userInitiated_dismissNotif() { val notifEntryCaptor = argumentCaptor<NotificationEntry>() val notifEntry = mock<NotificationEntry>() whenever(notifEntry.key).thenReturn(KEY) whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking()) whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry)) listenerCaptor.lastValue.onMediaDataRemoved(KEY, true) verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any()) assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY) } @Test @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() { listenerCaptor.lastValue.onMediaDataRemoved(KEY, false) verify(notifCollection, never()).dismissNotification(any(), any()) } @Test @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() { val notifEntryCaptor = argumentCaptor<NotificationEntry>() val notifEntry = mock<NotificationEntry>() whenever(notifEntry.key).thenReturn(KEY) whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking()) whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry)) listenerCaptor.lastValue.onMediaDataRemoved(KEY, false) verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any()) assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY) } } packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ constructor( updateMediaModel(data) } override fun onMediaDataRemoved(key: String) { override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { updateMediaModel() } } Loading Loading
packages/SystemUI/aconfig/systemui.aconfig +11 −1 Original line number Diff line number Diff line Loading @@ -892,3 +892,13 @@ flag { purpose: PURPOSE_BUGFIX } } flag { name: "media_controls_user_initiated_dismiss" namespace: "systemui" description: "Only dismiss media notifications when the control was removed by the user." bug: "335875159" metadata { purpose: PURPOSE_BUGFIX } }
packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalMediaRepositoryImplTest.kt +1 −1 Original line number Diff line number Diff line Loading @@ -114,7 +114,7 @@ class CommunalMediaRepositoryImplTest : SysuiTestCase() { // Change to media unavailable and notify the listener. whenever(mediaDataManager.hasActiveMediaOrRecommendation()).thenReturn(false) mediaDataListenerCaptor.value.onMediaDataRemoved("key") mediaDataListenerCaptor.value.onMediaDataRemoved("key", false) runCurrent() // Media active now returns false. Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/domain/interactor/MediaControlInteractorTest.kt +19 −0 Original line number Diff line number Diff line Loading @@ -27,12 +27,16 @@ import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.animation.Expandable import com.android.systemui.bluetooth.mockBroadcastDialogController import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.media.controls.data.repository.mediaDataRepository import com.android.systemui.media.controls.domain.pipeline.MediaDataFilterImpl import com.android.systemui.media.controls.domain.pipeline.MediaDataProcessor import com.android.systemui.media.controls.domain.pipeline.interactor.MediaControlInteractor import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor import com.android.systemui.media.controls.domain.pipeline.mediaDataFilter import com.android.systemui.media.controls.domain.pipeline.mediaDataProcessor import com.android.systemui.media.controls.shared.model.MediaData import com.android.systemui.media.controls.util.mediaInstanceId import com.android.systemui.media.mediaOutputDialogManager Loading Loading @@ -211,6 +215,21 @@ class MediaControlInteractorTest : SysuiTestCase() { ) } @Test fun removeMediaControl() { val listener = mock<MediaDataProcessor.Listener>() kosmos.mediaDataProcessor.addInternalListener(listener) var mediaData = MediaData(userId = USER_ID, instanceId = instanceId, artist = ARTIST) kosmos.mediaDataRepository.addMediaEntry(KEY, mediaData) underTest.removeMediaControl(null, instanceId, 0L) kosmos.fakeExecutor.advanceClockToNext() kosmos.fakeExecutor.runAllReady() verify(listener).onMediaDataRemoved(eq(KEY), eq(true)) } companion object { private const val USER_ID = 0 private const val KEY = "key" Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt 0 → 100644 +117 −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 import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.service.notification.NotificationListenerService import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.dump.dumpManager import com.android.systemui.media.controls.domain.pipeline.MediaDataManager import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.mockNotifCollection import com.android.systemui.statusbar.notification.collection.notifPipeline import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider import com.android.systemui.testKosmos import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @SmallTest @RunWith(AndroidJUnit4::class) class NotificationMediaManagerTest : SysuiTestCase() { private val KEY = "KEY" private val kosmos = testKosmos() private val visibilityProvider = kosmos.notificationVisibilityProvider private val notifPipeline = kosmos.notifPipeline private val notifCollection = kosmos.mockNotifCollection private val dumpManager = kosmos.dumpManager private val mediaDataManager = mock<MediaDataManager>() private val backgroundExecutor = FakeExecutor(FakeSystemClock()) private var listenerCaptor = argumentCaptor<MediaDataManager.Listener>() private lateinit var notificationMediaManager: NotificationMediaManager @Before fun setup() { notificationMediaManager = NotificationMediaManager( context, visibilityProvider, notifPipeline, notifCollection, mediaDataManager, dumpManager, backgroundExecutor, ) verify(mediaDataManager).addListener(listenerCaptor.capture()) } @Test @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_userInitiated_dismissNotif() { val notifEntryCaptor = argumentCaptor<NotificationEntry>() val notifEntry = mock<NotificationEntry>() whenever(notifEntry.key).thenReturn(KEY) whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking()) whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry)) listenerCaptor.lastValue.onMediaDataRemoved(KEY, true) verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any()) assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY) } @Test @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() { listenerCaptor.lastValue.onMediaDataRemoved(KEY, false) verify(notifCollection, never()).dismissNotification(any(), any()) } @Test @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS) fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() { val notifEntryCaptor = argumentCaptor<NotificationEntry>() val notifEntry = mock<NotificationEntry>() whenever(notifEntry.key).thenReturn(KEY) whenever(notifEntry.ranking).thenReturn(NotificationListenerService.Ranking()) whenever(notifPipeline.allNotifs).thenReturn(listOf(notifEntry)) listenerCaptor.lastValue.onMediaDataRemoved(KEY, false) verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any()) assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY) } }
packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ constructor( updateMediaModel(data) } override fun onMediaDataRemoved(key: String) { override fun onMediaDataRemoved(key: String, userInitiated: Boolean) { updateMediaModel() } } Loading