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.settingslib.volume.domain.interactor
18 
19 import android.media.AudioManager
20 import com.android.settingslib.statusbar.notification.domain.interactor.NotificationsSoundPolicyInteractor
21 import com.android.settingslib.volume.data.repository.AudioRepository
22 import com.android.settingslib.volume.shared.model.AudioStream
23 import com.android.settingslib.volume.shared.model.AudioStreamModel
24 import com.android.settingslib.volume.shared.model.RingerMode
25 import kotlinx.coroutines.flow.Flow
26 import kotlinx.coroutines.flow.StateFlow
27 import kotlinx.coroutines.flow.combine
28 import kotlinx.coroutines.flow.first
29 import kotlinx.coroutines.flow.map
30 
31 /** Provides audio stream state and an ability to change it */
32 class AudioVolumeInteractor(
33     private val audioRepository: AudioRepository,
34     private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
35 ) {
36 
37     /** State of the [AudioStream]. */
38     fun getAudioStream(audioStream: AudioStream): Flow<AudioStreamModel> =
39         combine(
40             audioRepository.getAudioStream(audioStream),
41             audioRepository.ringerMode,
42             notificationsSoundPolicyInteractor.isZenMuted(audioStream)
43         ) { streamModel: AudioStreamModel, ringerMode: RingerMode, isZenMuted: Boolean ->
44             streamModel.copy(volume = processVolume(streamModel, ringerMode, isZenMuted))
45         }
46 
47     val ringerMode: StateFlow<RingerMode>
48         get() = audioRepository.ringerMode
49 
50     suspend fun setVolume(audioStream: AudioStream, volume: Int) {
51         val streamModel = getAudioStream(audioStream).first()
52         val oldVolume = streamModel.volume
53         if (volume != oldVolume) {
54             audioRepository.setVolume(audioStream, volume)
55             when {
56                 volume == streamModel.minVolume -> setMuted(audioStream, true)
57                 oldVolume == streamModel.minVolume && volume > streamModel.minVolume ->
58                     setMuted(audioStream, false)
59             }
60         }
61     }
62 
63     suspend fun setMuted(audioStream: AudioStream, isMuted: Boolean) {
64         val streamModel = getAudioStream(audioStream).first()
65         if (!streamModel.isAffectedByMute) {
66             return
67         }
68         if (audioStream.value == AudioManager.STREAM_RING) {
69             val mode =
70                 if (isMuted) AudioManager.RINGER_MODE_VIBRATE else AudioManager.RINGER_MODE_NORMAL
71             audioRepository.setRingerMode(audioStream, RingerMode(mode))
72         }
73         val mutedChanged = audioRepository.setMuted(audioStream, isMuted)
74         if (mutedChanged && !isMuted) {
75             with(getAudioStream(audioStream).first()) {
76                 if (volume == minVolume) {
77                     // Slightly increase volume when user un-mutes the stream that is lowered
78                     // down to its minimum
79                     setVolume(audioStream, volume + 1)
80                 }
81             }
82         }
83     }
84 
85     /** Checks if the volume can be changed via the UI. */
86     fun canChangeVolume(audioStream: AudioStream): Flow<Boolean> {
87         return if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
88             combine(
89                 notificationsSoundPolicyInteractor.isZenMuted(audioStream),
90                 getAudioStream(AudioStream(AudioManager.STREAM_RING)).map { it.isMuted },
91             ) { isZenMuted, isRingMuted ->
92                 !isZenMuted && !isRingMuted
93             }
94         } else {
95             notificationsSoundPolicyInteractor.isZenMuted(audioStream).map { !it }
96         }
97     }
98 
99     private suspend fun processVolume(
100         audioStreamModel: AudioStreamModel,
101         ringerMode: RingerMode,
102         isZenMuted: Boolean,
103     ): Int {
104         if (isZenMuted) {
105             return audioRepository.getLastAudibleVolume(audioStreamModel.audioStream)
106         }
107         val isNotificationOrRing =
108             audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
109                 audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION
110         if (isNotificationOrRing && ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) {
111             // For ringer-mode affected streams, show volume as zero when ringer mode is vibrate
112             if (
113                 audioStreamModel.audioStream.value == AudioManager.STREAM_RING ||
114                     (audioStreamModel.audioStream.value == AudioManager.STREAM_NOTIFICATION &&
115                         audioStreamModel.isMuted)
116             ) {
117                 return audioStreamModel.minVolume
118             }
119         } else if (audioStreamModel.isMuted) {
120             return audioStreamModel.minVolume
121         }
122         return audioStreamModel.volume
123     }
124 }
125