1 /*
2  *  Copyright (C) 2022 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.data.quickaffordance
19 
20 import android.content.Context
21 import com.android.systemui.res.R
22 import com.android.systemui.animation.Expandable
23 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
24 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
25 import com.android.systemui.common.shared.model.ContentDescription
26 import com.android.systemui.common.shared.model.Icon
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
30 import com.android.systemui.statusbar.policy.FlashlightController
31 import javax.inject.Inject
32 import kotlinx.coroutines.channels.awaitClose
33 import kotlinx.coroutines.flow.Flow
34 
35 @SysUISingleton
36 class FlashlightQuickAffordanceConfig
37 @Inject
38 constructor(
39     @Application private val context: Context,
40     private val flashlightController: FlashlightController,
41 ) : KeyguardQuickAffordanceConfig {
42 
43     private sealed class FlashlightState {
44 
toLockScreenStatenull45         abstract fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState
46 
47         object On : FlashlightState() {
48             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
49                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
50                     Icon.Resource(
51                         R.drawable.qs_flashlight_icon_on,
52                         ContentDescription.Resource(R.string.quick_settings_flashlight_label)
53                     ),
54                     ActivationState.Active
55                 )
56         }
57 
58         object OffAvailable : FlashlightState() {
toLockScreenStatenull59             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
60                 KeyguardQuickAffordanceConfig.LockScreenState.Visible(
61                     Icon.Resource(
62                         R.drawable.qs_flashlight_icon_off,
63                         ContentDescription.Resource(R.string.quick_settings_flashlight_label)
64                     ),
65                     ActivationState.Inactive
66                 )
67         }
68 
69         object Unavailable : FlashlightState() {
70             override fun toLockScreenState(): KeyguardQuickAffordanceConfig.LockScreenState =
71                 KeyguardQuickAffordanceConfig.LockScreenState.Hidden
72         }
73     }
74 
75     override val key: String
76         get() = BuiltInKeyguardQuickAffordanceKeys.FLASHLIGHT
77 
pickerNamenull78     override fun pickerName(): String = context.getString(R.string.quick_settings_flashlight_label)
79 
80     override val pickerIconResourceId: Int
81         get() = R.drawable.ic_flashlight_off
82 
83     override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> =
84         conflatedCallbackFlow {
85             val flashlightCallback =
86                 object : FlashlightController.FlashlightListener {
87                     override fun onFlashlightChanged(enabled: Boolean) {
88                         trySendWithFailureLogging(
89                             if (enabled) {
90                                 FlashlightState.On.toLockScreenState()
91                             } else {
92                                 FlashlightState.OffAvailable.toLockScreenState()
93                             },
94                             TAG
95                         )
96                     }
97 
98                     override fun onFlashlightError() {
99                         trySendWithFailureLogging(
100                             FlashlightState.OffAvailable.toLockScreenState(),
101                             TAG
102                         )
103                     }
104 
105                     override fun onFlashlightAvailabilityChanged(available: Boolean) {
106                         trySendWithFailureLogging(
107                             if (!available) {
108                                 FlashlightState.Unavailable.toLockScreenState()
109                             } else {
110                                 if (flashlightController.isEnabled) {
111                                     FlashlightState.On.toLockScreenState()
112                                 } else {
113                                     FlashlightState.OffAvailable.toLockScreenState()
114                                 }
115                             },
116                             TAG
117                         )
118                     }
119                 }
120 
121             flashlightController.addCallback(flashlightCallback)
122 
123             awaitClose { flashlightController.removeCallback(flashlightCallback) }
124         }
125 
onTriggerednull126     override fun onTriggered(
127         expandable: Expandable?
128     ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
129         flashlightController.setFlashlight(
130             flashlightController.isAvailable && !flashlightController.isEnabled
131         )
132         return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
133     }
134 
getPickerScreenStatenull135     override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState =
136         if (flashlightController.isAvailable) {
137             KeyguardQuickAffordanceConfig.PickerScreenState.Default()
138         } else {
139             KeyguardQuickAffordanceConfig.PickerScreenState.UnavailableOnDevice
140         }
141 
142     companion object {
143         private const val TAG = "FlashlightQuickAffordanceConfig"
144     }
145 }
146