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.biometrics.domain.interactor 18 19 import android.content.Context 20 import android.content.res.Configuration 21 import com.android.systemui.biometrics.data.repository.DisplayStateRepository 22 import com.android.systemui.biometrics.shared.model.DisplayRotation 23 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 24 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 25 import com.android.systemui.dagger.qualifiers.Application 26 import com.android.systemui.dagger.qualifiers.Main 27 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider 28 import com.android.systemui.unfold.updates.FoldProvider 29 import java.util.concurrent.Executor 30 import javax.inject.Inject 31 import kotlinx.coroutines.CoroutineScope 32 import kotlinx.coroutines.channels.awaitClose 33 import kotlinx.coroutines.flow.Flow 34 import kotlinx.coroutines.flow.SharingStarted 35 import kotlinx.coroutines.flow.StateFlow 36 import kotlinx.coroutines.flow.stateIn 37 38 /** Aggregates display state information. */ 39 interface DisplayStateInteractor { 40 41 /** Whether the device is currently in rear display mode. */ 42 val isInRearDisplayMode: StateFlow<Boolean> 43 44 /** Whether the device is currently folded. */ 45 val isFolded: Flow<Boolean> 46 47 /** Current rotation of the display */ 48 val currentRotation: StateFlow<DisplayRotation> 49 50 /** Called on configuration changes, used to keep the display state in sync */ 51 fun onConfigurationChanged(newConfig: Configuration) 52 } 53 54 /** Encapsulates logic for interacting with the display state. */ 55 class DisplayStateInteractorImpl 56 @Inject 57 constructor( 58 @Application applicationScope: CoroutineScope, 59 @Application context: Context, 60 @Main mainExecutor: Executor, 61 displayStateRepository: DisplayStateRepository, 62 ) : DisplayStateInteractor { 63 private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context) 64 65 fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) { 66 screenSizeFoldProvider = foldProvider 67 } 68 69 override val isFolded: Flow<Boolean> = 70 conflatedCallbackFlow { 71 val sendFoldStateUpdate = { state: Boolean -> 72 trySendWithFailureLogging( 73 state, 74 TAG, 75 "Error sending fold state update to $state" 76 ) 77 } 78 79 val callback = 80 object : FoldProvider.FoldCallback { 81 override fun onFoldUpdated(isFolded: Boolean) { 82 sendFoldStateUpdate(isFolded) 83 } 84 } 85 86 sendFoldStateUpdate(false) 87 screenSizeFoldProvider.registerCallback(callback, mainExecutor) 88 awaitClose { screenSizeFoldProvider.unregisterCallback(callback) } 89 } 90 .stateIn( 91 applicationScope, 92 started = SharingStarted.Eagerly, 93 initialValue = false, 94 ) 95 96 override val isInRearDisplayMode: StateFlow<Boolean> = 97 displayStateRepository.isInRearDisplayMode 98 99 override val currentRotation: StateFlow<DisplayRotation> = 100 displayStateRepository.currentRotation 101 102 override fun onConfigurationChanged(newConfig: Configuration) { 103 screenSizeFoldProvider.onConfigurationChange(newConfig) 104 } 105 106 companion object { 107 private const val TAG = "DisplayStateInteractor" 108 } 109 } 110