summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt20
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt15
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt)45
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt37
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt24
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt6
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt12
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt139
-rw-r--r--packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt (renamed from packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt)16
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt4
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt6
13 files changed, 283 insertions, 72 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
index f729c04fb849..6851997ac323 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/AudioRepository.kt
@@ -21,10 +21,12 @@ import android.media.AudioManager
import android.media.AudioManager.OnCommunicationDeviceChangedListener
import androidx.concurrent.futures.DirectExecutor
import com.android.internal.util.ConcurrentUtils
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
+import com.android.settingslib.volume.shared.model.StreamAudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -33,6 +35,7 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -72,7 +75,7 @@ interface AudioRepository {
}
class AudioRepositoryImpl(
- private val audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ private val audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val audioManager: AudioManager,
private val backgroundCoroutineContext: CoroutineContext,
private val coroutineScope: CoroutineScope,
@@ -89,8 +92,8 @@ class AudioRepositoryImpl(
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), audioManager.mode)
override val ringerMode: StateFlow<RingerMode> =
- audioManagerIntentsReceiver.intents
- .filter { AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION == it.action }
+ audioManagerEventsReceiver.events
+ .filterIsInstance(AudioManagerEvent.InternalRingerModeChanged::class)
.map { RingerMode(audioManager.ringerModeInternal) }
.flowOn(backgroundCoroutineContext)
.stateIn(
@@ -120,7 +123,14 @@ class AudioRepositoryImpl(
)
override suspend fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> {
- return audioManagerIntentsReceiver.intents
+ return audioManagerEventsReceiver.events
+ .filter {
+ if (it is StreamAudioManagerEvent) {
+ it.audioStream == audioStream
+ } else {
+ true
+ }
+ }
.map { getCurrentAudioStream(audioStream) }
.flowOn(backgroundCoroutineContext)
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index aa9ae76c66c4..298dd71e555e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -15,13 +15,13 @@
*/
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.MediaRouter2Manager
import android.media.RoutingSessionInfo
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
@@ -29,7 +29,7 @@ import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapNotNull
@@ -54,7 +54,7 @@ interface LocalMediaRepository {
}
class LocalMediaRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val localMediaManager: LocalMediaManager,
private val mediaRouter2Manager: MediaRouter2Manager,
coroutineScope: CoroutineScope,
@@ -62,9 +62,9 @@ class LocalMediaRepositoryImpl(
) : LocalMediaRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
private val mediaDevicesUpdates: Flow<DevicesUpdate> =
callbackFlow {
val callback =
@@ -109,6 +109,7 @@ class LocalMediaRepositoryImpl(
override val currentConnectedDevice: StateFlow<MediaDevice?> =
merge(devicesChanges, mediaDevicesUpdates)
.map { localMediaManager.currentConnectedDevice }
+ .onStart { emit(localMediaManager.currentConnectedDevice) }
.stateIn(
coroutineScope,
SharingStarted.WhileSubscribed(),
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
index 6925c71fc68f..7c231d1fad4e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerRepository.kt
@@ -16,21 +16,19 @@
package com.android.settingslib.volume.data.repository
-import android.content.Intent
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaSessionManager
-import android.media.session.PlaybackState
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.bluetooth.headsetAudioModeChanges
import com.android.settingslib.media.session.activeMediaChanges
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onStart
@@ -44,7 +42,7 @@ interface MediaControllerRepository {
}
class MediaControllerRepositoryImpl(
- audioManagerIntentsReceiver: AudioManagerIntentsReceiver,
+ audioManagerEventsReceiver: AudioManagerEventsReceiver,
private val mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
coroutineScope: CoroutineScope,
@@ -52,9 +50,9 @@ class MediaControllerRepositoryImpl(
) : MediaControllerRepository {
private val devicesChanges =
- audioManagerIntentsReceiver.intents.filter {
- AudioManager.STREAM_DEVICES_CHANGED_ACTION == it.action
- }
+ audioManagerEventsReceiver.events.filterIsInstance(
+ AudioManagerEvent.StreamDevicesChanged::class
+ )
override val activeLocalMediaController: StateFlow<MediaController?> =
combine(
@@ -63,7 +61,7 @@ class MediaControllerRepositoryImpl(
},
localBluetoothManager?.headsetAudioModeChanges?.onStart { emit(Unit) }
?: flowOf(null),
- devicesChanges.onStart { emit(Intent()) },
+ devicesChanges.onStart { emit(AudioManagerEvent.StreamDevicesChanged) },
) { controllers, _, _ ->
controllers?.let(::findLocalMediaController)
}
@@ -98,9 +96,4 @@ class MediaControllerRepositoryImpl(
}
return localController
}
-
- private companion object {
- val inactivePlaybackStates =
- setOf(PlaybackState.STATE_STOPPED, PlaybackState.STATE_NONE, PlaybackState.STATE_ERROR)
- }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
index 9fa4c86cdea1..13ed9a802318 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiver.kt
@@ -21,6 +21,9 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.media.AudioManager
+import android.util.Log
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.SharedFlow
@@ -28,19 +31,20 @@ import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
-/** Exposes [AudioManager] intents as a observable shared flow. */
-interface AudioManagerIntentsReceiver {
+/** Exposes [AudioManager] events as a observable shared flow. */
+interface AudioManagerEventsReceiver {
- val intents: SharedFlow<Intent>
+ val events: SharedFlow<AudioManagerEvent>
}
-class AudioManagerIntentsReceiverImpl(
+class AudioManagerEventsReceiverImpl(
private val context: Context,
coroutineScope: CoroutineScope,
-) : AudioManagerIntentsReceiver {
+) : AudioManagerEventsReceiver {
private val allActions: Collection<String>
get() =
@@ -52,7 +56,7 @@ class AudioManagerIntentsReceiverImpl(
AudioManager.STREAM_DEVICES_CHANGED_ACTION,
)
- override val intents: SharedFlow<Intent> =
+ override val events: SharedFlow<AudioManagerEvent> =
callbackFlow {
val receiver =
object : BroadcastReceiver() {
@@ -73,5 +77,34 @@ class AudioManagerIntentsReceiverImpl(
}
.filterNotNull()
.filter { intent -> allActions.contains(intent.action) }
+ .mapNotNull { it.toAudioManagerEvent() }
.shareIn(coroutineScope, SharingStarted.WhileSubscribed())
+
+ private fun Intent.toAudioManagerEvent(): AudioManagerEvent? {
+ when (action) {
+ AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION ->
+ return AudioManagerEvent.InternalRingerModeChanged
+ AudioManager.STREAM_DEVICES_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamDevicesChanged
+ AudioManager.MASTER_MUTE_CHANGED_ACTION ->
+ return AudioManagerEvent.StreamMasterMuteChanged
+ }
+
+ val audioStreamType: Int =
+ getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.ERROR)
+ if (audioStreamType == AudioManager.ERROR) {
+ Log.e(
+ "AudioManagerIntentsReceiver",
+ "Intent doesn't have AudioManager.EXTRA_VOLUME_STREAM_TYPE extra",
+ )
+ return null
+ }
+ val audioStream = AudioStream(audioStreamType)
+ return when (action) {
+ AudioManager.STREAM_MUTE_CHANGED_ACTION ->
+ AudioManagerEvent.StreamMuteChanged(audioStream)
+ AudioManager.VOLUME_CHANGED_ACTION -> AudioManagerEvent.StreamVolumeChanged(audioStream)
+ else -> null
+ }
+ }
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
new file mode 100644
index 000000000000..e19896bc5e87
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/shared/model/AudioManagerEvent.kt
@@ -0,0 +1,37 @@
+/*
+ * 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.settingslib.volume.shared.model
+
+/** Model events happening with the [android.media.AudioManager]. */
+sealed interface AudioManagerEvent {
+
+ data class StreamMuteChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data class StreamVolumeChanged(override val audioStream: AudioStream) : StreamAudioManagerEvent
+
+ data object StreamMasterMuteChanged : AudioManagerEvent
+
+ data object InternalRingerModeChanged : AudioManagerEvent
+
+ data object StreamDevicesChanged : AudioManagerEvent
+}
+
+/** [AudioManagerEvent] that happens for a specific [AudioStream]. */
+sealed interface StreamAudioManagerEvent : AudioManagerEvent {
+
+ val audioStream: AudioStream
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
index 48b04db5b50b..9ddf876be68e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/AudioRepositoryTest.kt
@@ -20,7 +20,8 @@ import android.media.AudioDeviceInfo
import android.media.AudioManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.android.settingslib.volume.shared.model.AudioStream
import com.android.settingslib.volume.shared.model.AudioStreamModel
import com.android.settingslib.volume.shared.model.RingerMode
@@ -46,7 +47,6 @@ import org.mockito.MockitoAnnotations
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@Suppress("UnspecifiedRegisterReceiverFlag")
@RunWith(AndroidJUnit4::class)
class AudioRepositoryTest {
@@ -59,7 +59,7 @@ class AudioRepositoryTest {
@Mock private lateinit var audioManager: AudioManager
@Mock private lateinit var communicationDevice: AudioDeviceInfo
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val volumeByStream: MutableMap<Int, Int> = mutableMapOf()
private val isAffectedByRingerModeByStream: MutableMap<Int, Boolean> = mutableMapOf()
private val isMuteByStream: MutableMap<Int, Boolean> = mutableMapOf()
@@ -77,12 +77,14 @@ class AudioRepositoryTest {
`when`(audioManager.getStreamMaxVolume(anyInt())).thenReturn(MAX_VOLUME)
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_NORMAL)
`when`(audioManager.setStreamVolume(anyInt(), anyInt(), anyInt())).then {
- volumeByStream[it.arguments[0] as Int] = it.arguments[1] as Int
- triggerIntent(AudioManager.ACTION_VOLUME_CHANGED)
+ val streamType = it.arguments[1] as Int
+ volumeByStream[it.arguments[0] as Int] = streamType
+ triggerEvent(AudioManagerEvent.StreamVolumeChanged(AudioStream(streamType)))
}
`when`(audioManager.adjustStreamVolume(anyInt(), anyInt(), anyInt())).then {
- isMuteByStream[it.arguments[0] as Int] = it.arguments[2] == AudioManager.ADJUST_MUTE
- triggerIntent(AudioManager.STREAM_MUTE_CHANGED_ACTION)
+ val streamType = it.arguments[0] as Int
+ isMuteByStream[streamType] = it.arguments[2] == AudioManager.ADJUST_MUTE
+ triggerEvent(AudioManagerEvent.StreamMuteChanged(AudioStream(streamType)))
}
`when`(audioManager.getStreamVolume(anyInt())).thenAnswer {
volumeByStream.getOrDefault(it.arguments[0] as Int, 0)
@@ -96,7 +98,7 @@ class AudioRepositoryTest {
underTest =
AudioRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
audioManager,
testScope.testScheduler,
testScope.backgroundScope,
@@ -125,7 +127,7 @@ class AudioRepositoryTest {
runCurrent()
`when`(audioManager.ringerModeInternal).thenReturn(AudioManager.RINGER_MODE_SILENT)
- triggerIntent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION)
+ triggerEvent(AudioManagerEvent.InternalRingerModeChanged)
runCurrent()
assertThat(modes)
@@ -267,8 +269,8 @@ class AudioRepositoryTest {
modeListenerCaptor.value.onModeChanged(mode)
}
- private fun triggerIntent(action: String) {
- testScope.launch { intentsReceiver.triggerIntent(action) }
+ private fun triggerEvent(event: AudioManagerEvent) {
+ testScope.launch { eventsReceiver.triggerEvent(event) }
}
private companion object {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
index dc9ea10a1074..2d12dae36ff1 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/LocalMediaRepositoryImplTest.kt
@@ -23,7 +23,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.volume.data.model.RoutingSession
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -58,7 +58,7 @@ class LocalMediaRepositoryImplTest {
@Captor
private lateinit var deviceCallbackCaptor: ArgumentCaptor<LocalMediaManager.DeviceCallback>
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private val testScope = TestScope()
private lateinit var underTest: LocalMediaRepository
@@ -69,7 +69,7 @@ class LocalMediaRepositoryImplTest {
underTest =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManager,
mediaRouter2Manager,
testScope.backgroundScope,
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
index 7bd43d2cf8ab..f3d17141334e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/data/repository/MediaControllerRepositoryImplTest.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.volume.data.repository
-import android.media.AudioManager
import android.media.session.MediaController
import android.media.session.MediaController.PlaybackInfo
import android.media.session.MediaSessionManager
@@ -26,7 +25,8 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.bluetooth.BluetoothCallback
import com.android.settingslib.bluetooth.BluetoothEventManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
-import com.android.settingslib.volume.shared.FakeAudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.FakeAudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
@@ -66,7 +66,7 @@ class MediaControllerRepositoryImplTest {
@Mock private lateinit var localPlaybackInfo: PlaybackInfo
private val testScope = TestScope()
- private val intentsReceiver = FakeAudioManagerIntentsReceiver()
+ private val eventsReceiver = FakeAudioManagerEventsReceiver()
private lateinit var underTest: MediaControllerRepository
@@ -94,7 +94,7 @@ class MediaControllerRepositoryImplTest {
underTest =
MediaControllerRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
mediaSessionManager,
localBluetoothManager,
testScope.backgroundScope,
@@ -121,7 +121,7 @@ class MediaControllerRepositoryImplTest {
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
@@ -146,7 +146,7 @@ class MediaControllerRepositoryImplTest {
.launchIn(backgroundScope)
runCurrent()
- intentsReceiver.triggerIntent(AudioManager.STREAM_DEVICES_CHANGED_ACTION)
+ eventsReceiver.triggerEvent(AudioManagerEvent.StreamDevicesChanged)
triggerOnAudioModeChanged()
runCurrent()
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
new file mode 100644
index 000000000000..35ee8287d52f
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/AudioManagerEventsReceiverTest.kt
@@ -0,0 +1,139 @@
+/*
+ * 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.settingslib.volume.shared
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.media.AudioManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
+import com.android.settingslib.volume.shared.model.AudioStream
+import com.google.common.truth.Expect
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@Suppress("UnspecifiedRegisterReceiverFlag")
+@RunWith(AndroidJUnit4::class)
+class AudioManagerEventsReceiverTest {
+
+ @JvmField @Rule val expect = Expect.create()
+ private val testScope = TestScope()
+
+ @Mock private lateinit var context: Context
+ @Captor private lateinit var receiverCaptor: ArgumentCaptor<BroadcastReceiver>
+
+ private lateinit var underTest: AudioManagerEventsReceiver
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+
+ underTest = AudioManagerEventsReceiverImpl(context, testScope.backgroundScope)
+ }
+
+ @Test
+ fun validIntent_translatedToEvent() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(
+ Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(
+ Intent(AudioManager.VOLUME_CHANGED_ACTION).apply {
+ putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, AudioManager.STREAM_SYSTEM)
+ }
+ )
+ triggerIntent(Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.STREAM_DEVICES_CHANGED_ACTION))
+ runCurrent()
+
+ expect
+ .that(events)
+ .containsExactly(
+ AudioManagerEvent.StreamMuteChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamVolumeChanged(
+ AudioStream(AudioManager.STREAM_SYSTEM),
+ ),
+ AudioManagerEvent.StreamMasterMuteChanged,
+ AudioManagerEvent.InternalRingerModeChanged,
+ AudioManagerEvent.StreamDevicesChanged,
+ )
+ }
+ }
+
+ @Test
+ fun streamAudioManagerEvent_withoutAudioStream_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(Intent(AudioManager.STREAM_MUTE_CHANGED_ACTION))
+ triggerIntent(Intent(AudioManager.VOLUME_CHANGED_ACTION))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ @Test
+ fun invalidIntents_areSkipped() {
+ testScope.runTest {
+ val events = mutableListOf<AudioManagerEvent>()
+ underTest.events.onEach { events.add(it) }.launchIn(backgroundScope)
+ runCurrent()
+
+ triggerIntent(null)
+ triggerIntent(Intent())
+ triggerIntent(Intent("invalid_action"))
+ runCurrent()
+
+ expect.that(events).isEmpty()
+ }
+ }
+
+ private fun triggerIntent(intent: Intent?) {
+ verify(context).registerReceiver(receiverCaptor.capture(), any())
+ receiverCaptor.value.onReceive(context, intent)
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
index 530690a5faa9..b742df7afc46 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerIntentsReceiver.kt
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/volume/shared/FakeAudioManagerEventsReceiver.kt
@@ -16,21 +16,17 @@
package com.android.settingslib.volume.shared
-import android.content.Intent
+import com.android.settingslib.volume.shared.model.AudioManagerEvent
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.asSharedFlow
-class FakeAudioManagerIntentsReceiver : AudioManagerIntentsReceiver {
+class FakeAudioManagerEventsReceiver : AudioManagerEventsReceiver {
- private val mutableIntents = MutableSharedFlow<Intent>()
- override val intents: SharedFlow<Intent> = mutableIntents.asSharedFlow()
+ private val mutableIntents = MutableSharedFlow<AudioManagerEvent>()
+ override val events: SharedFlow<AudioManagerEvent> = mutableIntents.asSharedFlow()
- suspend fun triggerIntent(intent: Intent) {
- mutableIntents.emit(intent)
- }
-
- suspend fun triggerIntent(action: String) {
- triggerIntent(Intent(action))
+ suspend fun triggerEvent(event: AudioManagerEvent) {
+ mutableIntents.emit(event)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
index 67d6a7f5d735..f6fd519ed723 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/AudioModule.kt
@@ -27,8 +27,8 @@ import com.android.settingslib.statusbar.notification.data.repository.Notificati
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioRepositoryImpl
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiverImpl
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiverImpl
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import dagger.Module
@@ -46,11 +46,11 @@ interface AudioModule {
fun provideAudioManagerIntentsReceiver(
@Application context: Context,
@Application coroutineScope: CoroutineScope,
- ): AudioManagerIntentsReceiver = AudioManagerIntentsReceiverImpl(context, coroutineScope)
+ ): AudioManagerEventsReceiver = AudioManagerEventsReceiverImpl(context, coroutineScope)
@Provides
fun provideAudioRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
audioManager: AudioManager,
@Background coroutineContext: CoroutineContext,
@Application coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
index 9f99e9778ef2..bf9963d13959 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt
@@ -20,7 +20,7 @@ import android.media.session.MediaSessionManager
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.volume.data.repository.MediaControllerRepository
import com.android.settingslib.volume.data.repository.MediaControllerRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
@@ -45,7 +45,7 @@ interface MediaDevicesModule {
@Provides
@SysUISingleton
fun provideMediaDeviceSessionRepository(
- intentsReceiver: AudioManagerIntentsReceiver,
+ intentsReceiver: AudioManagerEventsReceiver,
mediaSessionManager: MediaSessionManager,
localBluetoothManager: LocalBluetoothManager?,
@Application coroutineScope: CoroutineScope,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index d8cd128af0c5..11b4690e59ee 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -18,7 +18,7 @@ package com.android.systemui.volume.panel.component.mediaoutput.data.repository
import android.media.MediaRouter2Manager
import com.android.settingslib.volume.data.repository.LocalMediaRepository
import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
-import com.android.settingslib.volume.shared.AudioManagerIntentsReceiver
+import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.media.controls.util.LocalMediaManagerFactory
@@ -34,7 +34,7 @@ interface LocalMediaRepositoryFactory {
class LocalMediaRepositoryFactoryImpl
@Inject
constructor(
- private val intentsReceiver: AudioManagerIntentsReceiver,
+ private val eventsReceiver: AudioManagerEventsReceiver,
private val mediaRouter2Manager: MediaRouter2Manager,
private val localMediaManagerFactory: LocalMediaManagerFactory,
@Application private val coroutineScope: CoroutineScope,
@@ -43,7 +43,7 @@ constructor(
override fun create(packageName: String?): LocalMediaRepository =
LocalMediaRepositoryImpl(
- intentsReceiver,
+ eventsReceiver,
localMediaManagerFactory.create(packageName),
mediaRouter2Manager,
coroutineScope,