summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Anton Potapov <apotapov@google.com> 2025-01-08 17:10:01 +0000
committer Anton Potapov <apotapov@google.com> 2025-01-09 15:50:19 +0000
commit0986cd433357c8a0511d1b3a49922ead1eeef86c (patch)
tree679278257f78e3c1e74e5104c6d1e971ea56a9ee
parentd1f16038471078f636d76f6d09ee5fa8d2babb70 (diff)
Check if the stream can be disabled by the zen mode
This change reworks onStart `{ volumeDialogController.getState() }` invocation because in this case the new state is emitted before the `VolumeDialogCallbacksInteractor` is ready to process the upcoming values. Flag: com.android.systemui.volume_redesign Fixes: 388510417 Fixes: 388508798 Fixes: 388455787 Fixes: 388454510 Test: atest VolumeDialogSliderInteractorTest Test: atest initialEvent_isSubscribedToEvents Change-Id: Id92a54a75f0f95fa01ea9df32bf1e16c0df17393
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt47
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt122
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt18
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt2
8 files changed, 164 insertions, 48 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
new file mode 100644
index 000000000000..fec186e862be
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractorTest.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.dialog.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeDialogCallbacksInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos().apply { useUnconfinedTestDispatcher() }
+
+ private val underTest: VolumeDialogCallbacksInteractor by lazy {
+ kosmos.volumeDialogCallbacksInteractor
+ }
+
+ @Test
+ fun initialEvent_isSubscribedToEvents() =
+ kosmos.runTest {
+ val event by collectLastValue(underTest.event)
+ assertThat(event).isInstanceOf(VolumeDialogEventModel.SubscribedToEvents::class.java)
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
index bfafdab003aa..1001c245cc95 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorTest.kt
@@ -16,75 +16,115 @@
package com.android.systemui.volume.dialog.sliders.domain.interactor
+import android.media.AudioManager
+import android.service.notification.ZenPolicy
import android.testing.TestableLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.VolumeDialogController
import com.android.systemui.plugins.fakeVolumeDialogController
+import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
import com.android.systemui.testKosmos
+import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@TestableLooper.RunWithLooper
class VolumeDialogSliderInteractorTest : SysuiTestCase() {
- private val kosmos = testKosmos()
-
- private lateinit var underTest: VolumeDialogSliderInteractor
+ private val kosmos =
+ testKosmos().apply {
+ useUnconfinedTestDispatcher()
+ zenModeRepository.addMode(
+ TestModeBuilder()
+ .setName("Blocks media, Active")
+ .setZenPolicy(ZenPolicy.Builder().allowMedia(false).build())
+ .setActive(true)
+ .build()
+ )
+ }
- @Before
- fun setUp() {
- underTest = kosmos.volumeDialogSliderInteractor
+ private val underTest: VolumeDialogSliderInteractor by lazy {
+ kosmos.volumeDialogSliderInteractor
}
@Test
fun settingStreamVolume_setsActiveStream() =
- with(kosmos) {
- testScope.runTest {
- runCurrent()
- // initialize the stream model
- fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
+ kosmos.runTest {
+ // initialize the stream model
+ fakeVolumeDialogController.setStreamVolume(volumeDialogSliderType.audioStream, 0)
- val sliderModel by collectLastValue(underTest.slider)
- underTest.setStreamVolume(1)
- runCurrent()
+ val sliderModel by collectLastValue(underTest.slider)
+ underTest.setStreamVolume(1)
- assertThat(sliderModel!!.isActive).isTrue()
- }
+ assertThat(sliderModel!!.isActive).isTrue()
}
@Test
fun streamVolumeIs_minMaxAreEnforced() =
- with(kosmos) {
- testScope.runTest {
- runCurrent()
- fakeVolumeDialogController.updateState {
- states.put(
- volumeDialogSliderType.audioStream,
- VolumeDialogController.StreamState().apply {
- levelMin = 0
- level = 2
- levelMax = 1
- },
- )
- }
-
- val sliderModel by collectLastValue(underTest.slider)
- runCurrent()
-
- assertThat(sliderModel!!.level).isEqualTo(1)
+ kosmos.runTest {
+ fakeVolumeDialogController.updateState {
+ states.put(
+ volumeDialogSliderType.audioStream,
+ VolumeDialogController.StreamState().apply {
+ levelMin = 0
+ level = 2
+ levelMax = 1
+ },
+ )
}
+
+ val sliderModel by collectLastValue(underTest.slider)
+
+ assertThat(sliderModel!!.level).isEqualTo(1)
+ }
+
+ @Test
+ fun streamCantBeBlockedByZenMode_isDisabledByZenMode_false() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.Stream(AudioManager.STREAM_VOICE_CALL)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun remoteMediaStream_zenModeRestrictive_IsNotDisabledByZenMode() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.RemoteMediaStream(0)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun audioSharingStream_zenModeRestrictive_IsNotDisabledByZenMode() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.AudioSharingStream(0)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isFalse()
+ }
+
+ @Test
+ fun streamBlockedByZenMode_isDisabledByZenMode_true() =
+ kosmos.runTest {
+ volumeDialogSliderType = VolumeDialogSliderType.Stream(AudioManager.STREAM_MUSIC)
+
+ val isDisabledByZenMode by collectLastValue(underTest.isDisabledByZenMode)
+
+ assertThat(isDisabledByZenMode).isTrue()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index 3b0c8a6b46f8..b52c1075e9f0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -25,6 +25,7 @@ import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope
import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
@@ -54,9 +55,10 @@ constructor(
callbackFlow {
val producer = VolumeDialogEventModelProducer(this)
volumeDialogController.addCallback(producer, bgHandler)
+ send(VolumeDialogEventModel.SubscribedToEvents)
awaitClose { volumeDialogController.removeCallback(producer) }
}
- .buffer(BUFFER_CAPACITY)
+ .buffer(capacity = BUFFER_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST)
.shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.WhileSubscribed())
private class VolumeDialogEventModelProducer(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
index 51e79242daaf..26d2414acec1 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogStateInteractor.kt
@@ -31,7 +31,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.flow.onStart
/**
* Exposes [VolumeDialogController.getState] in the [volumeDialogState].
@@ -65,12 +64,14 @@ constructor(
is VolumeDialogEventModel.ShowSafetyWarning -> {
setSafetyWarning(VolumeDialogSafetyWarningModel.Visible(event.flags))
}
+ is VolumeDialogEventModel.SubscribedToEvents -> {
+ volumeDialogController.getState()
+ }
else -> {
// do nothing
}
}
}
- .onStart { volumeDialogController.getState() }
.launchIn(coroutineScope)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
index 80e423838251..9793d2be6b98 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/model/VolumeDialogEventModel.kt
@@ -52,4 +52,11 @@ sealed interface VolumeDialogEventModel {
VolumeDialogEventModel
data object VolumeChangedFromKey : VolumeDialogEventModel
+
+ /**
+ * Signals that the
+ * [com.android.systemui.volume.dialog.domain.interactor.VolumeDialogCallbacksInteractor] is
+ * ready to process the events.
+ */
+ data object SubscribedToEvents : VolumeDialogEventModel
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 3988acbea7c2..b86252ddd1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -47,7 +47,7 @@ constructor(
) {
val isDisabledByZenMode: Flow<Boolean> =
- if (sliderType is VolumeDialogSliderType.Stream) {
+ if (zenModeInteractor.canBeBlockedByZenMode(sliderType)) {
zenModeInteractor.activeModesBlockingStream(AudioStream(sliderType.audioStream)).map {
it.mainMode != null
}
@@ -75,3 +75,8 @@ constructor(
}
}
}
+
+private fun ZenModeInteractor.canBeBlockedByZenMode(sliderType: VolumeDialogSliderType): Boolean {
+ return sliderType is VolumeDialogSliderType.Stream &&
+ canBeBlockedByZenMode(AudioStream(sliderType.audioStream))
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index daf4c8275d20..71fe22ba4b01 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -26,6 +26,7 @@ import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.RingerMode
import com.android.systemui.res.R
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
+import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -49,10 +50,10 @@ constructor(
isRoutedToBluetooth: Boolean,
): Flow<Drawable> {
return combine(
- zenModeInteractor.activeModesBlockingStream(AudioStream(stream)),
+ zenModeInteractor.activeModesBlockingStream(stream),
ringerModeForStream(stream),
) { activeModesBlockingStream, ringerMode ->
- if (activeModesBlockingStream.mainMode?.icon != null) {
+ if (activeModesBlockingStream?.mainMode?.icon != null) {
return@combine activeModesBlockingStream.mainMode.icon.drawable
} else {
context.getDrawable(
@@ -141,3 +142,16 @@ constructor(
}
}
}
+
+private fun ZenModeInteractor.activeModesBlockingStream(stream: Int): Flow<ActiveZenModes?> {
+ return if (AudioStream.supportedStreamTypes.contains(stream)) {
+ val audioStream = AudioStream(stream)
+ if (canBeBlockedByZenMode(audioStream)) {
+ activeModesBlockingStream(audioStream)
+ } else {
+ flowOf(null)
+ }
+ } else {
+ flowOf(null)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
index 43eb93e4dd53..9d73ae3f176f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/plugins/FakeVolumeDialogController.kt
@@ -197,7 +197,7 @@ class FakeVolumeDialogController(private val audioManager: AudioManager) : Volum
}
}
-private inline fun CopyOnWriteArraySet<VolumeDialogController.Callbacks>.sendEvent(
+private inline fun Collection<VolumeDialogController.Callbacks>.sendEvent(
event: (callback: VolumeDialogController.Callbacks) -> Unit
) {
for (callback in this) {