• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.dialog.sliders.domain.interactor
18 
19 import android.content.pm.PackageManager
20 import android.media.AudioManager
21 import android.media.AudioSystem
22 import com.android.settingslib.flags.Flags
23 import com.android.systemui.volume.VolumeDialogControllerImpl
24 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
25 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
26 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
27 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStateModel
28 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
29 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
30 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSlidersModel
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineScope
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.SharingStarted
35 import kotlinx.coroutines.flow.filter
36 import kotlinx.coroutines.flow.filterNotNull
37 import kotlinx.coroutines.flow.map
38 import kotlinx.coroutines.flow.runningReduce
39 import kotlinx.coroutines.flow.stateIn
40 
41 private const val DEFAULT_STREAM = AudioManager.STREAM_MUSIC
42 
43 /** Provides a state for the Sliders section of the Volume Dialog. */
44 @VolumeDialogScope
45 class VolumeDialogSlidersInteractor
46 @Inject
47 constructor(
48     volumeDialogStateInteractor: VolumeDialogStateInteractor,
49     private val packageManager: PackageManager,
50     @VolumeDialog private val coroutineScope: CoroutineScope,
51 ) {
52 
53     private val streamsSorter = StreamsSorter()
54     val sliders: Flow<VolumeDialogSlidersModel> =
55         volumeDialogStateInteractor.volumeDialogState
56             .filter { it.streamModels.isNotEmpty() }
57             .map { stateModel ->
58                 val sliderTypes =
59                     stateModel.streamModels.values
60                         .filter { streamModel -> shouldShowSliders(stateModel, streamModel) }
61                         .sortedWith(streamsSorter)
62                         .map { model -> model.toType() }
63                 LinkedHashSet(sliderTypes)
64             }
65             .runningReduce { sliderTypes, newSliderTypes ->
66                 sliderTypes.apply { addAll(newSliderTypes) }
67             }
68             .map { sliderTypes ->
69                 VolumeDialogSlidersModel(
70                     slider = sliderTypes.first(),
71                     floatingSliders = sliderTypes.drop(1),
72                 )
73             }
74             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
75             .filterNotNull()
76 
77     private fun shouldShowSliders(
78         stateModel: VolumeDialogStateModel,
79         streamModel: VolumeDialogStreamModel,
80     ): Boolean {
81         if (streamModel.isActive) {
82             return true
83         }
84 
85         if (!packageManager.isTv()) {
86             if (streamModel.stream == AudioSystem.STREAM_ACCESSIBILITY) {
87                 return stateModel.shouldShowA11ySlider
88             }
89 
90             // Always show the stream for audio sharing if it exists.
91             if (
92                 (Flags.volumeDialogAudioSharingFix() || Flags.audioSharingDeveloperOption()) &&
93                     streamModel.stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST
94             ) {
95                 return true
96             }
97 
98             return streamModel.stream == DEFAULT_STREAM || streamModel.isDynamic
99         }
100 
101         return false
102     }
103 
104     private fun VolumeDialogStreamModel.toType(): VolumeDialogSliderType {
105         return when {
106             stream == VolumeDialogControllerImpl.DYNAMIC_STREAM_BROADCAST ->
107                 VolumeDialogSliderType.AudioSharingStream(stream)
108             stream >= VolumeDialogControllerImpl.DYNAMIC_STREAM_REMOTE_START_INDEX ->
109                 VolumeDialogSliderType.RemoteMediaStream(stream)
110             else -> VolumeDialogSliderType.Stream(stream)
111         }
112     }
113 
114     private class StreamsSorter : Comparator<VolumeDialogStreamModel> {
115 
116         /**
117          * This list reflects the order of the sorted collection. Elements that satisfy predicates
118          * at the beginning of this list will be earlier in the sorted collection.
119          */
120         private val priorityPredicates: List<(VolumeDialogStreamModel) -> Boolean> =
121             listOf(
122                 { it.isActive },
123                 { it.stream == AudioManager.STREAM_MUSIC },
124                 { it.stream == AudioManager.STREAM_ACCESSIBILITY },
125                 { it.stream == AudioManager.STREAM_RING },
126                 { it.stream == AudioManager.STREAM_NOTIFICATION },
127                 { it.stream == AudioManager.STREAM_VOICE_CALL },
128                 { it.stream == AudioManager.STREAM_SYSTEM },
129                 { it.isDynamic },
130             )
131 
132         override fun compare(lhs: VolumeDialogStreamModel, rhs: VolumeDialogStreamModel): Int {
133             return lhs.getPriority() - rhs.getPriority()
134         }
135 
136         private fun VolumeDialogStreamModel.getPriority(): Int {
137             val index = priorityPredicates.indexOfFirst { it(this) }
138             return if (index >= 0) {
139                 index
140             } else {
141                 stream
142             }
143         }
144     }
145 }
146 
PackageManagernull147 private fun PackageManager.isTv(): Boolean = hasSystemFeature(PackageManager.FEATURE_LEANBACK)
148