1 /*
2  * 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.qs.tiles.viewmodel
18 
19 import android.content.res.Resources
20 import android.content.res.Resources.Theme
21 import android.service.quicksettings.Tile
22 import android.view.View
23 import android.widget.Switch
24 import com.android.systemui.common.shared.model.Icon
25 import kotlin.reflect.KClass
26 
27 /**
28  * Represents current a state of the tile to be displayed in on the view. Consider using
29  * [QSTileState.build] for better state creation experience and preset default values for certain
30  * fields.
31  *
32  * @param iconRes For when we want to have Loaded icon, but still keep a reference to the resource
33  *   id. A use case would be for tests that have to compare animated drawables.
34  *
35  * // TODO(b/http://b/299909989): Clean up legacy mappings after the transition
36  */
37 data class QSTileState(
38     val icon: () -> Icon?,
39     val iconRes: Int?,
40     val label: CharSequence,
41     val activationState: ActivationState,
42     val secondaryLabel: CharSequence?,
43     val supportedActions: Set<UserAction>,
44     val contentDescription: CharSequence?,
45     val stateDescription: CharSequence?,
46     val sideViewIcon: SideViewIcon,
47     val enabledState: EnabledState,
48     val expandedAccessibilityClassName: String?,
49 ) {
50 
51     companion object {
52 
buildnull53         fun build(
54             resources: Resources,
55             theme: Theme,
56             config: QSTileUIConfig,
57             build: Builder.() -> Unit
58         ): QSTileState {
59             val iconDrawable = resources.getDrawable(config.iconRes, theme)
60             return build(
61                 { Icon.Loaded(iconDrawable, null) },
62                 resources.getString(config.labelRes),
63                 build,
64             )
65         }
66 
buildnull67         fun build(icon: () -> Icon?, label: CharSequence, build: Builder.() -> Unit): QSTileState =
68             Builder(icon, label).apply(build).build()
69     }
70 
71     enum class ActivationState(val legacyState: Int) {
72         // An unavailable state indicates that for some reason this tile is not currently available
73         // to the user, and will have no click action. The tile's icon will be tinted differently to
74         // reflect this state.
75         UNAVAILABLE(Tile.STATE_UNAVAILABLE),
76         // This represents a tile that is currently active. (e.g. wifi is connected, bluetooth is
77         // on, cast is casting). This is the default state.
78         ACTIVE(Tile.STATE_ACTIVE),
79         // This represents a tile that is currently in a disabled state but is still interactable. A
80         // disabled state indicates that the tile is not currently active (e.g. wifi disconnected or
81         // bluetooth disabled), but is still interactable by the user to modify this state.
82         INACTIVE(Tile.STATE_INACTIVE);
83 
84         companion object {
85             fun valueOf(legacyState: Int): ActivationState =
86                 when (legacyState) {
87                     Tile.STATE_ACTIVE -> ACTIVE
88                     Tile.STATE_INACTIVE -> INACTIVE
89                     else -> UNAVAILABLE
90                 }
91         }
92     }
93 
94     /**
95      * Enabled tile behaves as usual where is disabled one is frozen and inactive in its current
96      * [ActivationState].
97      */
98     enum class EnabledState {
99         ENABLED,
100         DISABLED,
101     }
102 
103     enum class UserAction {
104         CLICK,
105         LONG_CLICK,
106     }
107 
108     sealed interface SideViewIcon {
109         data class Custom(val icon: Icon) : SideViewIcon
110         data object Chevron : SideViewIcon
111         data object None : SideViewIcon
112     }
113 
114     class Builder(
115         var icon: () -> Icon?,
116         var label: CharSequence,
117     ) {
118         var iconRes: Int? = null
119         var activationState: ActivationState = ActivationState.INACTIVE
120         var secondaryLabel: CharSequence? = null
121         var supportedActions: Set<UserAction> = setOf(UserAction.CLICK)
122         var contentDescription: CharSequence? = null
123         var stateDescription: CharSequence? = null
124         var sideViewIcon: SideViewIcon = SideViewIcon.None
125         var enabledState: EnabledState = EnabledState.ENABLED
126         var expandedAccessibilityClass: KClass<out View>? = Switch::class
127 
buildnull128         fun build(): QSTileState =
129             QSTileState(
130                 icon,
131                 iconRes,
132                 label,
133                 activationState,
134                 secondaryLabel,
135                 supportedActions,
136                 contentDescription,
137                 stateDescription,
138                 sideViewIcon,
139                 enabledState,
140                 expandedAccessibilityClass?.qualifiedName,
141             )
142     }
143 }
144