• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 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  *      https://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 package com.android.permissioncontroller.wear.permission.components.material3
17 
18 import androidx.compose.foundation.layout.BoxScope
19 import androidx.compose.foundation.layout.RowScope
20 import androidx.compose.foundation.layout.fillMaxWidth
21 import androidx.compose.runtime.Composable
22 import androidx.compose.ui.Modifier
23 import androidx.compose.ui.res.stringResource
24 import androidx.compose.ui.semantics.semantics
25 import androidx.compose.ui.semantics.stateDescription
26 import androidx.compose.ui.text.style.Hyphens
27 import androidx.wear.compose.material3.CheckboxButton
28 import androidx.wear.compose.material3.LocalTextConfiguration
29 import androidx.wear.compose.material3.LocalTextStyle
30 import androidx.wear.compose.material3.RadioButton
31 import androidx.wear.compose.material3.SwitchButton
32 import androidx.wear.compose.material3.Text
33 import com.android.permissioncontroller.wear.permission.components.R
34 import com.android.permissioncontroller.wear.permission.components.material2.ToggleChip
35 import com.android.permissioncontroller.wear.permission.components.theme.ResourceHelper
36 import com.android.permissioncontroller.wear.permission.components.theme.WearPermissionMaterialUIVersion
37 
38 /** Defines various toggle control types. */
39 enum class WearPermissionToggleControlType {
40     Switch,
41     Radio,
42     Checkbox,
43 }
44 
45 /**
46  * The custom component is a wrapper on different material3 toggle controls.
47  * 1. It provides an unified interface for RadioButton,CheckButton and SwitchButton.
48  * 2. It takes icon, primary, secondary label resources and construct them applying permission app
49  *    defaults
50  * 3. Applies custom semantics for based on the toggle control type
51  */
52 @Composable
WearPermissionToggleControlnull53 fun WearPermissionToggleControl(
54     toggleControl: WearPermissionToggleControlType,
55     label: String,
56     checked: Boolean,
57     onCheckedChanged: (Boolean) -> Unit,
58     modifier: Modifier = Modifier,
59     labelMaxLines: Int? = null,
60     materialUIVersion: WearPermissionMaterialUIVersion = ResourceHelper.materialUIVersionInSettings,
61     iconBuilder: WearPermissionIconBuilder? = null,
62     secondaryLabel: String? = null,
63     secondaryLabelMaxLines: Int? = null,
64     enabled: Boolean = true,
65     style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default,
66 ) {
67     if (materialUIVersion == WearPermissionMaterialUIVersion.MATERIAL2_5) {
68         ToggleChip(
69             toggleControl = toggleControl,
70             label = label,
71             labelMaxLine = labelMaxLines,
72             checked = checked,
73             onCheckedChanged = onCheckedChanged,
74             modifier = modifier,
75             icon = iconBuilder?.iconResource,
76             secondaryLabel = secondaryLabel,
77             secondaryLabelMaxLine = secondaryLabelMaxLines,
78             enabled = enabled,
79             colors = style.material2ToggleControlColors(),
80         )
81     } else {
82         WearPermissionToggleControlInternal(
83             label = label,
84             toggleControl = toggleControl,
85             checked = checked,
86             onCheckedChanged = onCheckedChanged,
87             modifier = modifier,
88             iconBuilder = iconBuilder,
89             labelMaxLines = labelMaxLines,
90             secondaryLabel = secondaryLabel,
91             secondaryLabelMaxLines = secondaryLabelMaxLines,
92             enabled = enabled,
93             style = style,
94         )
95     }
96 }
97 
98 @Composable
WearPermissionToggleControlInternalnull99 private fun WearPermissionToggleControlInternal(
100     label: String,
101     toggleControl: WearPermissionToggleControlType,
102     checked: Boolean,
103     onCheckedChanged: (Boolean) -> Unit,
104     modifier: Modifier = Modifier,
105     iconBuilder: WearPermissionIconBuilder? = null,
106     labelMaxLines: Int? = null,
107     secondaryLabel: String? = null,
108     secondaryLabelMaxLines: Int? = null,
109     enabled: Boolean = true,
110     style: WearPermissionToggleControlStyle = WearPermissionToggleControlStyle.Default,
111 ) {
112     val labelParam: (@Composable RowScope.() -> Unit) = {
113         Text(
114             text = label,
115             modifier = Modifier.fillMaxWidth(),
116             maxLines = labelMaxLines ?: LocalTextConfiguration.current.maxLines,
117             style = LocalTextStyle.current.copy(hyphens = Hyphens.Auto),
118         )
119     }
120 
121     val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
122         secondaryLabel?.let {
123             {
124                 Text(
125                     text = it,
126                     modifier = Modifier.fillMaxWidth(),
127                     maxLines = secondaryLabelMaxLines ?: LocalTextConfiguration.current.maxLines,
128                     style = LocalTextStyle.current.copy(hyphens = Hyphens.Auto),
129                 )
130             }
131         }
132 
133     val iconParam: (@Composable BoxScope.() -> Unit)? = iconBuilder?.let { { it.build() } }
134     val toggleControlStateDescription =
135         stringResource(
136             if (checked) {
137                 R.string.on
138             } else {
139                 R.string.off
140             }
141         )
142     val updatedModifier =
143         modifier.fillMaxWidth().semantics { stateDescription = toggleControlStateDescription }
144 
145     when (toggleControl) {
146         WearPermissionToggleControlType.Radio ->
147             RadioButton(
148                 selected = checked,
149                 onSelect = {
150                     // We do not want to call if it is already checked.
151                     // Radio button can't be toggled off
152                     if (!checked) {
153                         onCheckedChanged(true)
154                     }
155                 },
156                 modifier = updatedModifier,
157                 enabled = enabled,
158                 icon = iconParam,
159                 secondaryLabel = secondaryLabelParam,
160                 label = labelParam,
161                 colors = style.radioButtonColorScheme(),
162             )
163 
164         WearPermissionToggleControlType.Checkbox ->
165             CheckboxButton(
166                 checked = checked,
167                 onCheckedChange = onCheckedChanged,
168                 modifier = updatedModifier,
169                 enabled = enabled,
170                 icon = iconParam,
171                 secondaryLabel = secondaryLabelParam,
172                 label = labelParam,
173                 colors = style.checkboxColorScheme(),
174             )
175 
176         WearPermissionToggleControlType.Switch ->
177             SwitchButton(
178                 checked = checked,
179                 onCheckedChange = onCheckedChanged,
180                 modifier = updatedModifier,
181                 enabled = enabled,
182                 icon = iconParam,
183                 secondaryLabel = secondaryLabelParam,
184                 label = labelParam,
185                 colors = style.switchButtonColorScheme(),
186             )
187     }
188 }
189