1 /*
<lambda>null2  * 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.display.data.repository.DisplayRepository
28 import com.android.systemui.unfold.compat.ScreenSizeFoldProvider
29 import com.android.systemui.unfold.updates.FoldProvider
30 import java.util.concurrent.Executor
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineScope
33 import kotlinx.coroutines.channels.awaitClose
34 import kotlinx.coroutines.flow.Flow
35 import kotlinx.coroutines.flow.SharingStarted
36 import kotlinx.coroutines.flow.StateFlow
37 import kotlinx.coroutines.flow.stateIn
38 
39 /** Aggregates display state information. */
40 interface DisplayStateInteractor {
41     /** Whether the default display is currently off. */
42     val isDefaultDisplayOff: Flow<Boolean>
43 
44     /** Whether the device is currently in rear display mode. */
45     val isInRearDisplayMode: StateFlow<Boolean>
46 
47     /** Whether the device is currently folded. */
48     val isFolded: Flow<Boolean>
49 
50     /** Current rotation of the display */
51     val currentRotation: StateFlow<DisplayRotation>
52 
53     /** Display change event indicating a change to the given displayId has occurred. */
54     val displayChanges: Flow<Int>
55 
56     /**
57      * If true, the direction rotation is applied to get to an application's requested orientation
58      * is reversed. Normally, the model is that landscape is clockwise from portrait; thus on a
59      * portrait device an app requesting landscape will cause a clockwise rotation, and on a
60      * landscape device an app requesting portrait will cause a counter-clockwise rotation. Setting
61      * true here reverses that logic. See go/natural-orientation for context.
62      */
63     val isReverseDefaultRotation: Boolean
64 
65     /** Called on configuration changes, used to keep the display state in sync */
66     fun onConfigurationChanged(newConfig: Configuration)
67 
68     /** Provides whether the current display is large screen */
69     val isLargeScreen: StateFlow<Boolean>
70 }
71 
72 /** Encapsulates logic for interacting with the display state. */
73 class DisplayStateInteractorImpl
74 @Inject
75 constructor(
76     @Application applicationScope: CoroutineScope,
77     @Application context: Context,
78     @Main mainExecutor: Executor,
79     displayStateRepository: DisplayStateRepository,
80     displayRepository: DisplayRepository,
81 ) : DisplayStateInteractor {
82     private var screenSizeFoldProvider: ScreenSizeFoldProvider = ScreenSizeFoldProvider(context)
83 
setScreenSizeFoldProvidernull84     fun setScreenSizeFoldProvider(foldProvider: ScreenSizeFoldProvider) {
85         screenSizeFoldProvider = foldProvider
86     }
87 
88     override val displayChanges = displayRepository.displayChangeEvent
89 
90     override val isFolded: Flow<Boolean> =
<lambda>null91         conflatedCallbackFlow {
92                 val sendFoldStateUpdate = { state: Boolean ->
93                     trySendWithFailureLogging(
94                         state,
95                         TAG,
96                         "Error sending fold state update to $state"
97                     )
98                 }
99 
100                 val callback =
101                     object : FoldProvider.FoldCallback {
102                         override fun onFoldUpdated(isFolded: Boolean) {
103                             sendFoldStateUpdate(isFolded)
104                         }
105                     }
106 
107                 sendFoldStateUpdate(false)
108                 screenSizeFoldProvider.registerCallback(callback, mainExecutor)
109                 awaitClose { screenSizeFoldProvider.unregisterCallback(callback) }
110             }
111             .stateIn(
112                 applicationScope,
113                 started = SharingStarted.Eagerly,
114                 initialValue = false,
115             )
116 
117     override val isInRearDisplayMode: StateFlow<Boolean> =
118         displayStateRepository.isInRearDisplayMode
119 
120     override val currentRotation: StateFlow<DisplayRotation> =
121         displayStateRepository.currentRotation
122 
123     override val isReverseDefaultRotation: Boolean = displayStateRepository.isReverseDefaultRotation
124 
onConfigurationChangednull125     override fun onConfigurationChanged(newConfig: Configuration) {
126         screenSizeFoldProvider.onConfigurationChange(newConfig)
127     }
128 
129     override val isDefaultDisplayOff = displayRepository.defaultDisplayOff
130 
131     override val isLargeScreen: StateFlow<Boolean> = displayStateRepository.isLargeScreen
132 
133     companion object {
134         private const val TAG = "DisplayStateInteractor"
135     }
136 }
137