1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
18 
19 import android.media.MediaMetadata
20 import android.media.session.MediaController
21 import android.media.session.MediaSession
22 import android.media.session.PlaybackState
23 import android.os.Bundle
24 import android.os.Handler
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.dagger.qualifiers.Background
27 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
28 import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaControllerChangeModel
29 import javax.inject.Inject
30 import kotlinx.coroutines.channels.ProducerScope
31 import kotlinx.coroutines.channels.awaitClose
32 import kotlinx.coroutines.flow.Flow
33 import kotlinx.coroutines.launch
34 
35 interface MediaControllerInteractor {
36 
37     /** [MediaController.Callback] flow representation. */
stateChangesnull38     fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel>
39 }
40 
41 @SysUISingleton
42 class MediaControllerInteractorImpl
43 @Inject
44 constructor(
45     @Background private val backgroundHandler: Handler,
46 ) : MediaControllerInteractor {
47 
48     override fun stateChanges(mediaController: MediaController): Flow<MediaControllerChangeModel> {
49         return conflatedCallbackFlow {
50             val callback = MediaControllerCallbackProducer(this)
51             mediaController.registerCallback(callback, backgroundHandler)
52             awaitClose { mediaController.unregisterCallback(callback) }
53         }
54     }
55 }
56 
57 private class MediaControllerCallbackProducer(
58     private val producingScope: ProducerScope<MediaControllerChangeModel>
59 ) : MediaController.Callback() {
60 
onSessionDestroyednull61     override fun onSessionDestroyed() {
62         send(MediaControllerChangeModel.SessionDestroyed)
63     }
64 
onSessionEventnull65     override fun onSessionEvent(event: String, extras: Bundle?) {
66         send(MediaControllerChangeModel.SessionEvent(event, extras))
67     }
68 
onPlaybackStateChangednull69     override fun onPlaybackStateChanged(state: PlaybackState?) {
70         send(MediaControllerChangeModel.PlaybackStateChanged(state))
71     }
72 
onMetadataChangednull73     override fun onMetadataChanged(metadata: MediaMetadata?) {
74         send(MediaControllerChangeModel.MetadataChanged(metadata))
75     }
76 
onQueueChangednull77     override fun onQueueChanged(queue: MutableList<MediaSession.QueueItem>?) {
78         send(MediaControllerChangeModel.QueueChanged(queue))
79     }
80 
onQueueTitleChangednull81     override fun onQueueTitleChanged(title: CharSequence?) {
82         send(MediaControllerChangeModel.QueueTitleChanged(title))
83     }
84 
onExtrasChangednull85     override fun onExtrasChanged(extras: Bundle?) {
86         send(MediaControllerChangeModel.ExtrasChanged(extras))
87     }
88 
onAudioInfoChangednull89     override fun onAudioInfoChanged(info: MediaController.PlaybackInfo) {
90         send(MediaControllerChangeModel.AudioInfoChanged(info))
91     }
92 
sendnull93     private fun send(change: MediaControllerChangeModel) {
94         producingScope.launch { producingScope.send(change) }
95     }
96 }
97