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