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 
18 package com.android.systemui.keyguard.ui.viewmodel
19 
20 import android.content.Context
21 import com.android.settingslib.Utils
22 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor
23 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
24 import com.android.systemui.dagger.SysUISingleton
25 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
26 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
27 import com.android.systemui.keyguard.shared.model.KeyguardState
28 import com.android.systemui.keyguard.ui.view.DeviceEntryIconView
29 import com.android.systemui.res.R
30 import javax.inject.Inject
31 import kotlin.math.roundToInt
32 import kotlinx.coroutines.ExperimentalCoroutinesApi
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.combine
35 import kotlinx.coroutines.flow.distinctUntilChanged
36 import kotlinx.coroutines.flow.flatMapLatest
37 import kotlinx.coroutines.flow.flowOf
38 import kotlinx.coroutines.flow.map
39 import kotlinx.coroutines.flow.onStart
40 
41 /** Models the UI state for the device entry icon foreground view (displayed icon). */
42 @ExperimentalCoroutinesApi
43 @SysUISingleton
44 class DeviceEntryForegroundViewModel
45 @Inject
46 constructor(
47     val context: Context,
48     configurationInteractor: ConfigurationInteractor,
49     deviceEntryUdfpsInteractor: DeviceEntryUdfpsInteractor,
50     transitionInteractor: KeyguardTransitionInteractor,
51     deviceEntryIconViewModel: DeviceEntryIconViewModel,
52     udfpsOverlayInteractor: UdfpsOverlayInteractor,
53 ) {
54     private val isShowingAodOrDozing: Flow<Boolean> =
55         combine(
56             transitionInteractor.startedKeyguardState,
57             transitionInteractor.transitionValue(KeyguardState.DOZING),
58         ) { startedKeyguardState, dozingTransitionValue ->
59             startedKeyguardState == KeyguardState.AOD || dozingTransitionValue == 1f
60         }
61 
62     private fun getColor(usingBackgroundProtection: Boolean): Int {
63         return if (usingBackgroundProtection) {
64             Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
65         } else {
66             Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColorAccent)
67         }
68     }
69 
70     // While dozing, the display can show the AOD UI; show the AOD udfps when dozing
71     private val useAodIconVariant: Flow<Boolean> =
72         deviceEntryUdfpsInteractor.isUdfpsEnrolledAndEnabled.flatMapLatest { udfspEnrolled ->
73             if (udfspEnrolled) {
74                 isShowingAodOrDozing.distinctUntilChanged()
75             } else {
76                 flowOf(false)
77             }
78         }
79 
80     private val color: Flow<Int> =
81         useAodIconVariant
82             .flatMapLatest { useAodVariant ->
83                 if (useAodVariant) {
84                     flowOf(android.graphics.Color.WHITE)
85                 } else {
86                     deviceEntryIconViewModel.useBackgroundProtection.flatMapLatest { useBgProtection
87                         ->
88                         configurationInteractor.onAnyConfigurationChange
89                             .map { getColor(useBgProtection) }
90                             .onStart { emit(getColor(useBgProtection)) }
91                     }
92                 }
93             }
94             .distinctUntilChanged()
95 
96     private val padding: Flow<Int> =
97         deviceEntryUdfpsInteractor.isUdfpsSupported.flatMapLatest { udfpsSupported ->
98             if (udfpsSupported) {
99                 udfpsOverlayInteractor.iconPadding
100             } else {
101                 configurationInteractor.scaleForResolution.map { scale ->
102                     (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
103                         .roundToInt()
104                 }
105             }
106         }
107 
108     val viewModel: Flow<ForegroundIconViewModel> =
109         combine(
110             deviceEntryIconViewModel.iconType,
111             useAodIconVariant,
112             color,
113             padding,
114         ) { iconType, useAodVariant, color, padding ->
115             ForegroundIconViewModel(
116                 type = iconType,
117                 useAodVariant = useAodVariant,
118                 tint = color,
119                 padding = padding,
120             )
121         }
122 
123     data class ForegroundIconViewModel(
124         val type: DeviceEntryIconView.IconType,
125         val useAodVariant: Boolean,
126         val tint: Int,
127         val padding: Int,
128     )
129 }
130