1 /* 2 * Copyright (C) 2023 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.statusbar.data.repository 18 19 import com.android.systemui.dagger.SysUISingleton 20 import com.android.systemui.dagger.qualifiers.Application 21 import com.android.systemui.scene.shared.flag.SceneContainerFlag 22 import com.android.systemui.statusbar.NotificationRemoteInputManager 23 import com.android.systemui.statusbar.RemoteInputController 24 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow 25 import dagger.Binds 26 import dagger.Module 27 import javax.inject.Inject 28 import kotlinx.coroutines.CoroutineScope 29 import kotlinx.coroutines.channels.awaitClose 30 import kotlinx.coroutines.flow.Flow 31 import kotlinx.coroutines.flow.MutableStateFlow 32 import kotlinx.coroutines.flow.SharingStarted 33 import kotlinx.coroutines.flow.stateIn 34 35 /** 36 * Repository used for tracking the state of notification remote input (e.g. when the user presses 37 * "reply" on a notification and the keyboard opens). 38 */ 39 interface RemoteInputRepository { 40 /** Whether remote input is currently active for any notification. */ 41 val isRemoteInputActive: Flow<Boolean> 42 43 /** 44 * The bottom bound of the currently focused remote input notification row, or null if there 45 * isn't one. 46 */ 47 val remoteInputRowBottomBound: Flow<Float?> 48 setRemoteInputRowBottomBoundnull49 fun setRemoteInputRowBottomBound(bottom: Float?) 50 51 /** Close any active remote inputs */ 52 fun closeRemoteInputs() 53 } 54 55 @SysUISingleton 56 class RemoteInputRepositoryImpl 57 @Inject 58 constructor( 59 @Application applicationScope: CoroutineScope, 60 private val notificationRemoteInputManager: NotificationRemoteInputManager, 61 ) : RemoteInputRepository { 62 private val _isRemoteInputActive = conflatedCallbackFlow { 63 val callback = 64 object : RemoteInputController.Callback { 65 override fun onRemoteInputActive(active: Boolean) { 66 trySend(active) 67 } 68 } 69 trySend(notificationRemoteInputManager.isRemoteInputActive) 70 notificationRemoteInputManager.addControllerCallback(callback) 71 awaitClose { notificationRemoteInputManager.removeControllerCallback(callback) } 72 } 73 74 override val isRemoteInputActive = 75 if (SceneContainerFlag.isEnabled) { 76 _isRemoteInputActive.stateIn( 77 applicationScope, 78 SharingStarted.WhileSubscribed(), 79 notificationRemoteInputManager.isRemoteInputActive, 80 ) 81 } else { 82 _isRemoteInputActive 83 } 84 85 override val remoteInputRowBottomBound = MutableStateFlow<Float?>(null) 86 87 override fun setRemoteInputRowBottomBound(bottom: Float?) { 88 remoteInputRowBottomBound.value = bottom 89 } 90 91 override fun closeRemoteInputs() { 92 notificationRemoteInputManager.closeRemoteInputs() 93 } 94 } 95 96 @Module 97 interface RemoteInputRepositoryModule { bindImplnull98 @Binds fun bindImpl(impl: RemoteInputRepositoryImpl): RemoteInputRepository 99 } 100