1 /*
2 * Copyright (C) 2024 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.brightness.ui.viewmodel
18
19 import android.content.Context
20 import androidx.annotation.DrawableRes
21 import androidx.annotation.FloatRange
22 import androidx.annotation.StringRes
23 import androidx.compose.runtime.getValue
24 import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor
25 import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor
26 import com.android.systemui.brightness.shared.model.GammaBrightness
27 import com.android.systemui.classifier.Classifier
28 import com.android.systemui.classifier.domain.interactor.FalsingInteractor
29 import com.android.systemui.common.shared.model.Icon
30 import com.android.systemui.common.shared.model.asIcon
31 import com.android.systemui.graphics.ImageLoader
32 import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel
33 import com.android.systemui.lifecycle.ExclusiveActivatable
34 import com.android.systemui.lifecycle.Hydrator
35 import com.android.systemui.res.R
36 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
37 import com.android.systemui.settings.brightness.ui.BrightnessWarningToast
38 import com.android.systemui.utils.PolicyRestriction
39 import dagger.assisted.Assisted
40 import dagger.assisted.AssistedFactory
41 import dagger.assisted.AssistedInject
42
43 /**
44 * View Model for a brightness slider.
45 *
46 * If this brightness slider supports mirroring (show on top of current activity while dragging),
47 * then:
48 * * [showMirror] will be true while dragging
49 * * [BrightnessMirrorShowingInteractor.isShowing] will track if the mirror should show (for (other
50 * parts of SystemUI to act accordingly).
51 */
52 class BrightnessSliderViewModel
53 @AssistedInject
54 constructor(
55 private val screenBrightnessInteractor: ScreenBrightnessInteractor,
56 private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor,
57 val hapticsViewModelFactory: SliderHapticsViewModel.Factory,
58 private val brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
59 private val falsingInteractor: FalsingInteractor,
60 @Assisted private val supportsMirroring: Boolean,
61 private val brightnessWarningToast: BrightnessWarningToast,
62 private val imageLoader: ImageLoader,
63 ) : ExclusiveActivatable() {
64
65 private val hydrator = Hydrator("BrightnessSliderViewModel.hydrator")
66
67 val currentBrightness by
68 hydrator.hydratedStateOf(
69 "currentBrightness",
70 initialValue,
71 screenBrightnessInteractor.gammaBrightness,
72 )
73
74 val maxBrightness = screenBrightnessInteractor.maxGammaBrightness
75 val minBrightness = screenBrightnessInteractor.minGammaBrightness
76
77 val policyRestriction = brightnessPolicyEnforcementInteractor.brightnessPolicyRestriction
78
showPolicyRestrictionDialognull79 fun showPolicyRestrictionDialog(restriction: PolicyRestriction.Restricted) {
80 brightnessPolicyEnforcementInteractor.startAdminSupportDetailsDialog(restriction)
81 }
82
83 val brightnessOverriddenByWindow = screenBrightnessInteractor.brightnessOverriddenByWindow
84
showToastnull85 fun showToast(viewContext: Context, @StringRes resId: Int) {
86 if (brightnessWarningToast.isToastActive()) {
87 return
88 }
89 brightnessWarningToast.show(viewContext, resId)
90 }
91
emitBrightnessTouchForFalsingnull92 fun emitBrightnessTouchForFalsing() {
93 falsingInteractor.isFalseTouch(Classifier.BRIGHTNESS_SLIDER)
94 }
95
loadImagenull96 suspend fun loadImage(@DrawableRes resId: Int, context: Context): Icon.Loaded {
97 return imageLoader
98 .loadDrawable(
99 android.graphics.drawable.Icon.createWithResource(context, resId),
100 maxHeight = 200,
101 maxWidth = 200,
102 )!!
103 .asIcon(null, resId)
104 }
105
106 /**
107 * As a brightness slider is dragged, the corresponding events should be sent using this method.
108 */
onDragnull109 suspend fun onDrag(drag: Drag) {
110 when (drag) {
111 is Drag.Dragging -> screenBrightnessInteractor.setTemporaryBrightness(drag.brightness)
112 is Drag.Stopped -> screenBrightnessInteractor.setBrightness(drag.brightness)
113 }
114 }
115
setIsDraggingnull116 fun setIsDragging(dragging: Boolean) {
117 brightnessMirrorShowingInteractor.setMirrorShowing(dragging && supportsMirroring)
118 }
119
120 val showMirror by
121 hydrator.hydratedStateOf("showMirror", brightnessMirrorShowingInteractor.isShowing)
122
onActivatednull123 override suspend fun onActivated(): Nothing {
124 hydrator.activate()
125 }
126
127 @AssistedFactory
128 interface Factory {
createnull129 fun create(supportsMirroring: Boolean): BrightnessSliderViewModel
130 }
131
132 companion object {
133 val initialValue = GammaBrightness(-1)
134
135 private val icons =
136 BrightnessIcons(
137 brightnessLow = R.drawable.ic_brightness_low,
138 brightnessMid = R.drawable.ic_brightness_medium,
139 brightnessHigh = R.drawable.ic_brightness_full,
140 )
141
142 @DrawableRes
143 fun getIconForPercentage(@FloatRange(0.0, 100.0) percentage: Float): Int {
144 return when {
145 percentage <= 20f -> icons.brightnessLow
146 percentage >= 80f -> icons.brightnessHigh
147 else -> icons.brightnessMid
148 }
149 }
150 }
151 }
152
BrightnessSliderViewModelnull153 fun BrightnessSliderViewModel.Factory.create() = create(supportsMirroring = true)
154
155 /** Represents a drag event in a brightness slider. */
156 sealed interface Drag {
157 val brightness: GammaBrightness
158
159 @JvmInline value class Dragging(override val brightness: GammaBrightness) : Drag
160
161 @JvmInline value class Stopped(override val brightness: GammaBrightness) : Drag
162 }
163
164 private data class BrightnessIcons(
165 @DrawableRes val brightnessLow: Int,
166 @DrawableRes val brightnessMid: Int,
167 @DrawableRes val brightnessHigh: Int,
168 )
169