Loading packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt +2 −8 Original line number Diff line number Diff line Loading @@ -16,17 +16,16 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.os.Handler 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.coroutines.collectLastValue import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.volume.localMediaController import com.android.systemui.volume.mediaControllerRepository import com.android.systemui.volume.mediaDeviceSessionInteractor import com.android.systemui.volume.mediaOutputInteractor import com.android.systemui.volume.panel.shared.model.filterData import com.android.systemui.volume.remoteMediaController Loading Loading @@ -55,12 +54,7 @@ class MediaDeviceSessionInteractorTest : SysuiTestCase() { listOf(localMediaController, remoteMediaController) ) underTest = MediaDeviceSessionInteractor( testScope.testScheduler, Handler(TestableLooper.get(kosmos.testCase).looper), mediaControllerRepository, ) underTest = mediaDeviceSessionInteractor } } Loading packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt +7 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl import dagger.Binds import dagger.Module import dagger.Provides Loading @@ -41,6 +43,11 @@ interface MediaDevicesModule { impl: LocalMediaRepositoryFactoryImpl ): LocalMediaRepositoryFactory @Binds fun bindMediaControllerInteractor( impl: MediaControllerInteractorImpl ): MediaControllerInteractor companion object { @Provides Loading packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt→packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt +34 −38 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.settingslib.volume.data.repository package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.media.MediaMetadata import android.media.session.MediaController Loading @@ -22,79 +22,75 @@ import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle import android.os.Handler import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel import javax.inject.Inject import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch interface MediaControllerInteractor { /** [MediaController.Callback] flow representation. */ fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> { return callbackFlow { val callback = MediaControllerCallbackProducer(this) registerCallback(callback, handler) awaitClose { unregisterCallback(callback) } fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> } } /** Models particular change event received by [MediaController.Callback]. */ sealed interface MediaControllerChange { data object SessionDestroyed : MediaControllerChange data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange @SysUISingleton class MediaControllerInteractorImpl @Inject constructor( @Background private val backgroundHandler: Handler, ) : MediaControllerInteractor { data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) : MediaControllerChange data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> { return conflatedCallbackFlow { val callback = MediaControllerCallbackProducer(this) mediaController.registerCallback(callback, backgroundHandler) awaitClose { mediaController.unregisterCallback(callback) } } } } private class MediaControllerCallbackProducer( private val producingScope: ProducerScope<MediaControllerChange> private val producingScope: ProducerScope<MediaControllerChangeModel> ) : MediaController.Callback() { override fun onSessionDestroyed() { send(MediaControllerChange.SessionDestroyed) send(MediaControllerChangeModel.SessionDestroyed) } override fun onSessionEvent(event: String, extras: Bundle?) { send(MediaControllerChange.SessionEvent(event, extras)) send(MediaControllerChangeModel.SessionEvent(event, extras)) } override fun onPlaybackStateChanged(state: PlaybackState?) { send(MediaControllerChange.PlaybackStateChanged(state)) send(MediaControllerChangeModel.PlaybackStateChanged(state)) } override fun onMetadataChanged(metadata: MediaMetadata?) { send(MediaControllerChange.MetadataChanged(metadata)) send(MediaControllerChangeModel.MetadataChanged(metadata)) } override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) { send(MediaControllerChange.QueueChanged(queue)) send(MediaControllerChangeModel.QueueChanged(queue)) } override fun onQueueTitleChanged(title: CharSequence?) { send(MediaControllerChange.QueueTitleChanged(title)) send(MediaControllerChangeModel.QueueTitleChanged(title)) } override fun onExtrasChanged(extras: Bundle?) { send(MediaControllerChange.ExtrasChanged(extras)) send(MediaControllerChangeModel.ExtrasChanged(extras)) } override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) { send(MediaControllerChange.AudioInfoChanged(info)) send(MediaControllerChangeModel.AudioInfoChanged(info)) } private fun send(change: MediaControllerChange) { private fun send(change: MediaControllerChangeModel) { producingScope.launch { producingScope.send(change) } } } packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt +10 −11 Original line number Diff line number Diff line Loading @@ -18,11 +18,9 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.media.session.MediaController import android.media.session.PlaybackState import android.os.Handler import com.android.settingslib.volume.data.repository.MediaControllerChange import com.android.settingslib.volume.data.repository.MediaControllerRepository import com.android.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import javax.inject.Inject Loading @@ -45,38 +43,39 @@ class MediaDeviceSessionInteractor @Inject constructor( @Background private val backgroundCoroutineContext: CoroutineContext, @Background private val backgroundHandler: Handler, private val mediaControllerInteractor: MediaControllerInteractor, private val mediaControllerRepository: MediaControllerRepository, ) { /** [PlaybackState] changes for the [MediaDeviceSession]. */ fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> { return stateChanges(session) { emit(MediaControllerChange.PlaybackStateChanged(it.playbackState)) emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState)) } .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class) .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class) .map { it.state } } /** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */ fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> { return stateChanges(session) { emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo)) emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo)) } .filterIsInstance(MediaControllerChange.AudioInfoChanged::class) .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class) .map { it.info } } private fun stateChanges( session: MediaDeviceSession, onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit, ): Flow<MediaControllerChange?> = onStart: suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit, ): Flow<MediaControllerChangeModel?> = mediaControllerRepository.activeSessions .flatMapLatest { controllers -> val controller: MediaController = findControllerForSession(controllers, session) ?: return@flatMapLatest flowOf(null) controller.stateChanges(backgroundHandler).onStart { onStart(controller) } mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) } } .flowOn(backgroundCoroutineContext) Loading packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt +5 −4 Original line number Diff line number Diff line Loading @@ -19,12 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.content.pm.PackageManager import android.media.VolumeProvider import android.media.session.MediaController import android.os.Handler import android.util.Log import com.android.settingslib.media.MediaDevice import com.android.settingslib.volume.data.repository.LocalMediaRepository import com.android.settingslib.volume.data.repository.MediaControllerRepository import com.android.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions Loading Loading @@ -61,7 +59,7 @@ constructor( @VolumePanelScope private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, mediaControllerRepository: MediaControllerRepository, @Background private val backgroundHandler: Handler, private val mediaControllerInteractor: MediaControllerInteractor, ) { private val activeMediaControllers: Flow<MediaControllers> = Loading Loading @@ -194,7 +192,10 @@ constructor( return flowOf(null) } return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) } return mediaControllerInteractor .stateChanges(this) .map { this } .onStart { emit(this@stateChanges) } } private data class MediaControllers( Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractorTest.kt +2 −8 Original line number Diff line number Diff line Loading @@ -16,17 +16,16 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.os.Handler 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.coroutines.collectLastValue import com.android.systemui.kosmos.testCase import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos import com.android.systemui.volume.localMediaController import com.android.systemui.volume.mediaControllerRepository import com.android.systemui.volume.mediaDeviceSessionInteractor import com.android.systemui.volume.mediaOutputInteractor import com.android.systemui.volume.panel.shared.model.filterData import com.android.systemui.volume.remoteMediaController Loading Loading @@ -55,12 +54,7 @@ class MediaDeviceSessionInteractorTest : SysuiTestCase() { listOf(localMediaController, remoteMediaController) ) underTest = MediaDeviceSessionInteractor( testScope.testScheduler, Handler(TestableLooper.get(kosmos.testCase).looper), mediaControllerRepository, ) underTest = mediaDeviceSessionInteractor } } Loading
packages/SystemUI/src/com/android/systemui/volume/dagger/MediaDevicesModule.kt +7 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactoryImpl import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractor import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaControllerInteractorImpl import dagger.Binds import dagger.Module import dagger.Provides Loading @@ -41,6 +43,11 @@ interface MediaDevicesModule { impl: LocalMediaRepositoryFactoryImpl ): LocalMediaRepositoryFactory @Binds fun bindMediaControllerInteractor( impl: MediaControllerInteractorImpl ): MediaControllerInteractor companion object { @Provides Loading
packages/SettingsLib/src/com/android/settingslib/volume/data/repository/MediaControllerExt.kt→packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaControllerInteractor.kt +34 −38 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.settingslib.volume.data.repository package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor import android.media.MediaMetadata import android.media.session.MediaController Loading @@ -22,79 +22,75 @@ import android.media.session.MediaSession import android.media.session.PlaybackState import android.os.Bundle import android.os.Handler import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel import javax.inject.Inject import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.launch interface MediaControllerInteractor { /** [MediaController.Callback] flow representation. */ fun MediaController.stateChanges(handler: Handler): Flow<MediaControllerChange> { return callbackFlow { val callback = MediaControllerCallbackProducer(this) registerCallback(callback, handler) awaitClose { unregisterCallback(callback) } fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> } } /** Models particular change event received by [MediaController.Callback]. */ sealed interface MediaControllerChange { data object SessionDestroyed : MediaControllerChange data class SessionEvent(val event: String, val extras: Bundle?) : MediaControllerChange data class PlaybackStateChanged(val state: PlaybackState?) : MediaControllerChange data class MetadataChanged(val metadata: MediaMetadata?) : MediaControllerChange @SysUISingleton class MediaControllerInteractorImpl @Inject constructor( @Background private val backgroundHandler: Handler, ) : MediaControllerInteractor { data class QueueChanged(val queue: MutableList<MediaSession.QueueItem>?) : MediaControllerChange data class QueueTitleChanged(val title: CharSequence?) : MediaControllerChange data class ExtrasChanged(val extras: Bundle?) : MediaControllerChange data class AudioInfoChanged(val info: MediaController.PlaybackInfo?) : MediaControllerChange override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> { return conflatedCallbackFlow { val callback = MediaControllerCallbackProducer(this) mediaController.registerCallback(callback, backgroundHandler) awaitClose { mediaController.unregisterCallback(callback) } } } } private class MediaControllerCallbackProducer( private val producingScope: ProducerScope<MediaControllerChange> private val producingScope: ProducerScope<MediaControllerChangeModel> ) : MediaController.Callback() { override fun onSessionDestroyed() { send(MediaControllerChange.SessionDestroyed) send(MediaControllerChangeModel.SessionDestroyed) } override fun onSessionEvent(event: String, extras: Bundle?) { send(MediaControllerChange.SessionEvent(event, extras)) send(MediaControllerChangeModel.SessionEvent(event, extras)) } override fun onPlaybackStateChanged(state: PlaybackState?) { send(MediaControllerChange.PlaybackStateChanged(state)) send(MediaControllerChangeModel.PlaybackStateChanged(state)) } override fun onMetadataChanged(metadata: MediaMetadata?) { send(MediaControllerChange.MetadataChanged(metadata)) send(MediaControllerChangeModel.MetadataChanged(metadata)) } override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) { send(MediaControllerChange.QueueChanged(queue)) send(MediaControllerChangeModel.QueueChanged(queue)) } override fun onQueueTitleChanged(title: CharSequence?) { send(MediaControllerChange.QueueTitleChanged(title)) send(MediaControllerChangeModel.QueueTitleChanged(title)) } override fun onExtrasChanged(extras: Bundle?) { send(MediaControllerChange.ExtrasChanged(extras)) send(MediaControllerChangeModel.ExtrasChanged(extras)) } override fun onAudioInfoChanged(info: MediaController.PlaybackInfo?) { send(MediaControllerChange.AudioInfoChanged(info)) send(MediaControllerChangeModel.AudioInfoChanged(info)) } private fun send(change: MediaControllerChange) { private fun send(change: MediaControllerChangeModel) { producingScope.launch { producingScope.send(change) } } }
packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaDeviceSessionInteractor.kt +10 −11 Original line number Diff line number Diff line Loading @@ -18,11 +18,9 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.media.session.MediaController import android.media.session.PlaybackState import android.os.Handler import com.android.settingslib.volume.data.repository.MediaControllerChange import com.android.settingslib.volume.data.repository.MediaControllerRepository import com.android.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import javax.inject.Inject Loading @@ -45,38 +43,39 @@ class MediaDeviceSessionInteractor @Inject constructor( @Background private val backgroundCoroutineContext: CoroutineContext, @Background private val backgroundHandler: Handler, private val mediaControllerInteractor: MediaControllerInteractor, private val mediaControllerRepository: MediaControllerRepository, ) { /** [PlaybackState] changes for the [MediaDeviceSession]. */ fun playbackState(session: MediaDeviceSession): Flow<PlaybackState?> { return stateChanges(session) { emit(MediaControllerChange.PlaybackStateChanged(it.playbackState)) emit(MediaControllerChangeModel.PlaybackStateChanged(it.playbackState)) } .filterIsInstance(MediaControllerChange.PlaybackStateChanged::class) .filterIsInstance(MediaControllerChangeModel.PlaybackStateChanged::class) .map { it.state } } /** [MediaController.PlaybackInfo] changes for the [MediaDeviceSession]. */ fun playbackInfo(session: MediaDeviceSession): Flow<MediaController.PlaybackInfo?> { return stateChanges(session) { emit(MediaControllerChange.AudioInfoChanged(it.playbackInfo)) emit(MediaControllerChangeModel.AudioInfoChanged(it.playbackInfo)) } .filterIsInstance(MediaControllerChange.AudioInfoChanged::class) .filterIsInstance(MediaControllerChangeModel.AudioInfoChanged::class) .map { it.info } } private fun stateChanges( session: MediaDeviceSession, onStart: suspend FlowCollector<MediaControllerChange>.(controller: MediaController) -> Unit, ): Flow<MediaControllerChange?> = onStart: suspend FlowCollector<MediaControllerChangeModel>.(controller: MediaController) -> Unit, ): Flow<MediaControllerChangeModel?> = mediaControllerRepository.activeSessions .flatMapLatest { controllers -> val controller: MediaController = findControllerForSession(controllers, session) ?: return@flatMapLatest flowOf(null) controller.stateChanges(backgroundHandler).onStart { onStart(controller) } mediaControllerInteractor.stateChanges(controller).onStart { onStart(controller) } } .flowOn(backgroundCoroutineContext) Loading
packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt +5 −4 Original line number Diff line number Diff line Loading @@ -19,12 +19,10 @@ package com.android.systemui.volume.panel.component.mediaoutput.domain.interacto import android.content.pm.PackageManager import android.media.VolumeProvider import android.media.session.MediaController import android.os.Handler import android.util.Log import com.android.settingslib.media.MediaDevice import com.android.settingslib.volume.data.repository.LocalMediaRepository import com.android.settingslib.volume.data.repository.MediaControllerRepository import com.android.settingslib.volume.data.repository.stateChanges import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaDeviceSessions Loading Loading @@ -61,7 +59,7 @@ constructor( @VolumePanelScope private val coroutineScope: CoroutineScope, @Background private val backgroundCoroutineContext: CoroutineContext, mediaControllerRepository: MediaControllerRepository, @Background private val backgroundHandler: Handler, private val mediaControllerInteractor: MediaControllerInteractor, ) { private val activeMediaControllers: Flow<MediaControllers> = Loading Loading @@ -194,7 +192,10 @@ constructor( return flowOf(null) } return stateChanges(backgroundHandler).map { this }.onStart { emit(this@stateChanges) } return mediaControllerInteractor .stateChanges(this) .map { this } .onStart { emit(this@stateChanges) } } private data class MediaControllers( Loading