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.dialog.domain.interactor 18 19 import android.annotation.SuppressLint 20 import android.media.AudioManager.RINGER_MODE_NORMAL 21 import android.media.AudioManager.RINGER_MODE_SILENT 22 import android.os.Handler 23 import com.android.systemui.dagger.qualifiers.Background 24 import com.android.systemui.plugins.VolumeDialogController 25 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPlugin 26 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogPluginScope 27 import com.android.systemui.volume.dialog.domain.model.VolumeDialogEventModel 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.channels.BufferOverflow 31 import kotlinx.coroutines.channels.ProducerScope 32 import kotlinx.coroutines.channels.awaitClose 33 import kotlinx.coroutines.flow.Flow 34 import kotlinx.coroutines.flow.SharingStarted 35 import kotlinx.coroutines.flow.buffer 36 import kotlinx.coroutines.flow.callbackFlow 37 import kotlinx.coroutines.flow.onStart 38 import kotlinx.coroutines.flow.shareIn 39 40 private const val BUFFER_CAPACITY = 16 41 42 /** 43 * Exposes [VolumeDialogController] callback events in the [event]. 44 * 45 * @see VolumeDialogController.Callbacks 46 */ 47 @VolumeDialogPluginScope 48 class VolumeDialogCallbacksInteractor 49 @Inject 50 constructor( 51 private val volumeDialogController: VolumeDialogController, 52 @VolumeDialogPlugin private val coroutineScope: CoroutineScope, 53 @Background private val bgHandler: Handler?, 54 ) { 55 56 @SuppressLint("SharedFlowCreation") // event-bus needed 57 val event: Flow<VolumeDialogEventModel> = <lambda>null58 callbackFlow { 59 val producer = VolumeDialogEventModelProducer(this) 60 volumeDialogController.addCallback(producer, bgHandler) 61 send(VolumeDialogEventModel.SubscribedToEvents) 62 awaitClose { volumeDialogController.removeCallback(producer) } 63 } 64 .buffer(capacity = BUFFER_CAPACITY, onBufferOverflow = BufferOverflow.DROP_OLDEST) 65 .shareIn(replay = 0, scope = coroutineScope, started = SharingStarted.Eagerly) <lambda>null66 .onStart { emit(VolumeDialogEventModel.SubscribedToEvents) } 67 68 private inner class VolumeDialogEventModelProducer( 69 private val scope: ProducerScope<VolumeDialogEventModel> 70 ) : VolumeDialogController.Callbacks { onShowRequestednull71 override fun onShowRequested(reason: Int, keyguardLocked: Boolean, lockTaskModeState: Int) { 72 scope.trySend( 73 VolumeDialogEventModel.ShowRequested( 74 reason = reason, 75 keyguardLocked = keyguardLocked, 76 lockTaskModeState = lockTaskModeState, 77 ) 78 ) 79 } 80 onDismissRequestednull81 override fun onDismissRequested(reason: Int) { 82 scope.trySend(VolumeDialogEventModel.DismissRequested(reason)) 83 } 84 onStateChangednull85 override fun onStateChanged(state: VolumeDialogController.State?) { 86 if (state != null) { 87 scope.trySend(VolumeDialogEventModel.StateChanged(state)) 88 } 89 } 90 onLayoutDirectionChangednull91 override fun onLayoutDirectionChanged(layoutDirection: Int) { 92 scope.trySend(VolumeDialogEventModel.LayoutDirectionChanged(layoutDirection)) 93 } 94 95 // Configuration change is never emitted by the VolumeDialogControllerImpl now. onConfigurationChangednull96 override fun onConfigurationChanged() = Unit 97 98 override fun onScreenOff() { 99 scope.trySend(VolumeDialogEventModel.ScreenOff) 100 } 101 onShowSafetyWarningnull102 override fun onShowSafetyWarning(flags: Int) { 103 scope.trySend(VolumeDialogEventModel.ShowSafetyWarning(flags)) 104 } 105 onAccessibilityModeChangednull106 override fun onAccessibilityModeChanged(showA11yStream: Boolean?) { 107 scope.trySend(VolumeDialogEventModel.AccessibilityModeChanged(showA11yStream == true)) 108 } 109 onShowCsdWarningnull110 override fun onShowCsdWarning(csdWarning: Int, durationMs: Int) { 111 scope.trySend( 112 VolumeDialogEventModel.ShowCsdWarning( 113 csdWarning = csdWarning, 114 durationMs = durationMs, 115 ) 116 ) 117 } 118 onVolumeChangedFromKeynull119 override fun onVolumeChangedFromKey() { 120 scope.trySend(VolumeDialogEventModel.VolumeChangedFromKey) 121 } 122 123 // This should've been handled in side the controller itself. onShowVibrateHintnull124 override fun onShowVibrateHint() { 125 volumeDialogController.setRingerMode(RINGER_MODE_SILENT, false) 126 } 127 128 // This should've been handled in side the controller itself. onShowSilentHintnull129 override fun onShowSilentHint() { 130 volumeDialogController.setRingerMode(RINGER_MODE_NORMAL, false) 131 } 132 133 // Captions button is remove from the Volume Dialog onCaptionComponentStateChangednull134 override fun onCaptionComponentStateChanged( 135 isComponentEnabled: Boolean, 136 fromTooltip: Boolean, 137 ) = Unit 138 139 // Captions button is remove from the Volume Dialog 140 override fun onCaptionEnabledStateChanged(isEnabled: Boolean, checkBeforeSwitch: Boolean) = 141 Unit 142 } 143 } 144