• 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 package com.android.systemui.keyguard.ui.viewmodel
18 
19 import android.content.Context
20 import android.content.res.Configuration
21 import android.util.Log
22 import com.android.systemui.customization.R as customR
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.dagger.qualifiers.Application
25 import com.android.systemui.keyguard.domain.interactor.KeyguardSmartspaceInteractor
26 import com.android.systemui.res.R
27 import com.android.systemui.shade.domain.interactor.ShadeModeInteractor
28 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.flow.SharingStarted
32 import kotlinx.coroutines.flow.StateFlow
33 import kotlinx.coroutines.flow.combine
34 import kotlinx.coroutines.flow.map
35 import kotlinx.coroutines.flow.stateIn
36 
37 @SysUISingleton
38 class KeyguardSmartspaceViewModel
39 @Inject
40 constructor(
41     @Application applicationScope: CoroutineScope,
42     smartspaceController: LockscreenSmartspaceController,
43     keyguardClockViewModel: KeyguardClockViewModel,
44     smartspaceInteractor: KeyguardSmartspaceInteractor,
45     shadeModeInteractor: ShadeModeInteractor,
46 ) {
47     /** Whether the smartspace section is available in the build. */
48     val isSmartspaceEnabled: Boolean = smartspaceController.isEnabled
49     /** Whether the weather area is available in the build. */
50     private val isWeatherEnabled: StateFlow<Boolean> = smartspaceInteractor.isWeatherEnabled
51 
52     /** Whether the data and weather areas are decoupled in the build. */
53     val isDateWeatherDecoupled: Boolean = smartspaceController.isDateWeatherDecoupled
54 
55     /** Whether the date area should be visible. */
56     val isDateVisible: StateFlow<Boolean> =
57         keyguardClockViewModel.hasCustomWeatherDataDisplay
58             .map { !it }
59             .stateIn(
60                 scope = applicationScope,
61                 started = SharingStarted.WhileSubscribed(),
62                 initialValue = !keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
63             )
64 
65     /** Whether the weather area should be visible. */
66     val isWeatherVisible: StateFlow<Boolean> =
67         combine(isWeatherEnabled, keyguardClockViewModel.hasCustomWeatherDataDisplay) {
68                 isWeatherEnabled,
69                 clockIncludesCustomWeatherDisplay ->
70                 isWeatherVisible(
71                     clockIncludesCustomWeatherDisplay = clockIncludesCustomWeatherDisplay,
72                     isWeatherEnabled = isWeatherEnabled,
73                 )
74             }
75             .stateIn(
76                 scope = applicationScope,
77                 started = SharingStarted.WhileSubscribed(),
78                 initialValue =
79                     isWeatherVisible(
80                         clockIncludesCustomWeatherDisplay =
81                             keyguardClockViewModel.hasCustomWeatherDataDisplay.value,
82                         isWeatherEnabled = isWeatherEnabled.value,
83                     ),
84             )
85 
86     private fun isWeatherVisible(
87         clockIncludesCustomWeatherDisplay: Boolean,
88         isWeatherEnabled: Boolean,
89     ): Boolean {
90         return !clockIncludesCustomWeatherDisplay && isWeatherEnabled
91     }
92 
93     /* trigger clock and smartspace constraints change when smartspace appears */
94     val bcSmartspaceVisibility: StateFlow<Int> = smartspaceInteractor.bcSmartspaceVisibility
95 
96     val isShadeLayoutWide: StateFlow<Boolean> = shadeModeInteractor.isShadeLayoutWide
97 
98     companion object {
99         private const val TAG = "KeyguardSmartspaceVM"
100 
101         fun dateWeatherBelowSmallClock(
102             configuration: Configuration,
103             customDateWeather: Boolean = false,
104         ): Boolean {
105             return if (
106                 com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout() &&
107                     !customDateWeather
108             ) {
109                 // font size to display size
110                 // These values come from changing the font size and display size on a non-foldable.
111                 // Visually looked at which configs cause the date/weather to push off of the screen
112                 val breakingPairs =
113                     listOf(
114                         0.85f to 320, // tiny font size but large display size
115                         1f to 346,
116                         1.15f to 346,
117                         1.5f to 376,
118                         1.8f to 411, // large font size but tiny display size
119                     )
120                 val screenWidthDp = configuration.screenWidthDp
121                 val fontScale = configuration.fontScale
122                 var fallBelow = false
123                 for ((font, width) in breakingPairs) {
124                     if (fontScale >= font && screenWidthDp <= width) {
125                         fallBelow = true
126                         break
127                     }
128                 }
129                 Log.d(TAG, "Width: $screenWidthDp, Font: $fontScale, BelowClock: $fallBelow")
130                 return fallBelow
131             } else {
132                 true
133             }
134         }
135 
136         fun getDateWeatherStartMargin(context: Context): Int {
137             return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_start) +
138                 context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
139         }
140 
141         fun getDateWeatherEndMargin(context: Context): Int {
142             return context.resources.getDimensionPixelSize(R.dimen.below_clock_padding_end) +
143                 context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
144         }
145 
146         fun getSmartspaceHorizontalMargin(context: Context): Int {
147             return context.resources.getDimensionPixelSize(R.dimen.smartspace_padding_horizontal) +
148                 context.resources.getDimensionPixelSize(customR.dimen.status_view_margin_horizontal)
149         }
150     }
151 }
152