1 /*
2  * Copyright (C) 2022 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.media.muteawait
18 
19 import android.content.Context
20 import android.graphics.drawable.Drawable
21 import android.media.AudioAttributes.USAGE_MEDIA
22 import android.media.AudioDeviceAttributes
23 import android.media.AudioManager
24 import com.android.settingslib.media.DeviceIconUtil
25 import com.android.settingslib.media.LocalMediaManager
26 import com.android.systemui.dagger.qualifiers.Main
27 import java.util.concurrent.Executor
28 
29 /**
30  * A class responsible for keeping track of devices that have muted audio playback until the device
31  * is connected. The device connection expected to happen imminently, so we'd like to display the
32  * device name in the media player. When the about-to-connect device changes, [localMediaManager]
33  * will be notified.
34  *
35  * See [AudioManager.muteAwaitConnection] and b/206614671 for more details.
36  */
37 class MediaMuteAwaitConnectionManager constructor(
38     @Main private val mainExecutor: Executor,
39     private val localMediaManager: LocalMediaManager,
40     private val context: Context,
41     private val deviceIconUtil: DeviceIconUtil,
42     private val logger: MediaMuteAwaitLogger
43 ) {
44     var currentMutedDevice: AudioDeviceAttributes? = null
45 
46     val audioManager: AudioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
47 
48     val muteAwaitConnectionChangeListener = object : AudioManager.MuteAwaitConnectionCallback() {
onMutedUntilConnectionnull49         override fun onMutedUntilConnection(device: AudioDeviceAttributes, mutedUsages: IntArray) {
50             logger.logMutedDeviceAdded(device.address, device.name, mutedUsages.hasMedia())
51             if (mutedUsages.hasMedia()) {
52                 // There should only be one device that's mutedUntilConnection at a time, so we can
53                 // safely override any previous value.
54                 currentMutedDevice = device
55                 localMediaManager.dispatchAboutToConnectDeviceAdded(
56                     device.address, device.name, device.getIcon()
57                 )
58             }
59         }
60 
onUnmutedEventnull61         override fun onUnmutedEvent(
62             @UnmuteEvent unmuteEvent: Int,
63             device: AudioDeviceAttributes,
64             mutedUsages: IntArray
65         ) {
66             val isMostRecentDevice = currentMutedDevice == device
67             logger.logMutedDeviceRemoved(
68                 device.address, device.name, mutedUsages.hasMedia(), isMostRecentDevice
69             )
70             if (isMostRecentDevice && mutedUsages.hasMedia()) {
71                 currentMutedDevice = null
72                 localMediaManager.dispatchAboutToConnectDeviceRemoved()
73             }
74         }
75     }
76 
77     /** Start listening for mute await events. */
startListeningnull78     fun startListening() {
79         audioManager.registerMuteAwaitConnectionCallback(
80                 mainExecutor, muteAwaitConnectionChangeListener
81         )
82         val currentDevice = audioManager.mutingExpectedDevice
83         if (currentDevice != null) {
84             currentMutedDevice = currentDevice
85             localMediaManager.dispatchAboutToConnectDeviceAdded(
86                 currentDevice.address, currentDevice.name, currentDevice.getIcon()
87             )
88         }
89     }
90 
91     /** Stop listening for mute await events. */
stopListeningnull92     fun stopListening() {
93         audioManager.unregisterMuteAwaitConnectionCallback(muteAwaitConnectionChangeListener)
94     }
95 
AudioDeviceAttributesnull96     private fun AudioDeviceAttributes.getIcon(): Drawable {
97         return deviceIconUtil.getIconFromAudioDeviceType(this.type)
98     }
99 
IntArraynull100     private fun IntArray.hasMedia() = USAGE_MEDIA in this
101 }
102