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.permissioncontroller.permission.ui.wear.elements
18
19 import androidx.compose.foundation.interaction.MutableInteractionSource
20 import androidx.compose.foundation.layout.BoxScope
21 import androidx.compose.foundation.layout.Row
22 import androidx.compose.foundation.layout.RowScope
23 import androidx.compose.foundation.layout.fillMaxWidth
24 import androidx.compose.foundation.layout.size
25 import androidx.compose.foundation.shape.CircleShape
26 import androidx.compose.runtime.Composable
27 import androidx.compose.runtime.remember
28 import androidx.compose.ui.Modifier
29 import androidx.compose.ui.draw.clip
30 import androidx.compose.ui.graphics.Color
31 import androidx.compose.ui.graphics.compositeOver
32 import androidx.compose.ui.res.stringResource
33 import androidx.compose.ui.semantics.semantics
34 import androidx.compose.ui.semantics.stateDescription
35 import androidx.compose.ui.text.style.TextAlign
36 import androidx.compose.ui.text.style.TextOverflow
37 import androidx.wear.compose.material.ChipDefaults
38 import androidx.wear.compose.material.ContentAlpha
39 import androidx.wear.compose.material.MaterialTheme
40 import androidx.wear.compose.material.Text
41 import androidx.wear.compose.material.ToggleChip
42 import androidx.wear.compose.material.ToggleChipColors
43 import androidx.wear.compose.material.ToggleChipDefaults
44 import androidx.wear.compose.material.contentColorFor
45 import com.android.permissioncontroller.R
46
47 /**
48 * This component is an alternative to [ToggleChip], providing the following:
49 * - a convenient way of providing a label and a secondary label;
50 * - a convenient way of choosing the toggle control;
51 * - a convenient way of providing an icon and setting the icon to be mirrored in RTL mode;
52 */
53 @Composable
ToggleChipnull54 public fun ToggleChip(
55 checked: Boolean,
56 onCheckedChanged: (Boolean) -> Unit,
57 label: String,
58 labelMaxLine: Int? = null,
59 toggleControl: ToggleChipToggleControl,
60 modifier: Modifier = Modifier,
61 icon: Any? = null,
62 iconColor: Color = Color.Unspecified,
63 iconRtlMode: IconRtlMode = IconRtlMode.Default,
64 secondaryLabel: String? = null,
65 secondaryLabelMaxLine: Int? = null,
66 colors: ToggleChipColors = ToggleChipDefaults.toggleChipColors(),
67 enabled: Boolean = true,
68 interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
69 ) {
70 val hasSecondaryLabel = secondaryLabel != null
71
<lambda>null72 val labelParam: (@Composable RowScope.() -> Unit) = {
73 Text(
74 text = label,
75 modifier = Modifier.fillMaxWidth(),
76 textAlign = TextAlign.Start,
77 overflow = TextOverflow.Ellipsis,
78 maxLines = labelMaxLine ?: if (hasSecondaryLabel) 1 else 2,
79 style = MaterialTheme.typography.button
80 )
81 }
82
83 val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
<lambda>null84 secondaryLabel?.let {
85 {
86 Text(
87 text = secondaryLabel,
88 overflow = TextOverflow.Ellipsis,
89 maxLines = secondaryLabelMaxLine ?: 1,
90 style = MaterialTheme.typography.caption2
91 )
92 }
93 }
94
95 val toggleControlParam: (@Composable () -> Unit) = {
96 Icon(
97 imageVector =
98 when (toggleControl) {
99 ToggleChipToggleControl.Switch -> ToggleChipDefaults.switchIcon(checked)
100 ToggleChipToggleControl.Radio -> ToggleChipDefaults.radioIcon(checked)
101 ToggleChipToggleControl.Checkbox -> ToggleChipDefaults.checkboxIcon(checked)
102 },
103 contentDescription = null,
104 // This potentially be removed once this issue is addressed:
105 // https://issuetracker.google.com/issues/287087138
106 rtlMode =
107 if (toggleControl == ToggleChipToggleControl.Switch) {
108 IconRtlMode.Mirrored
109 } else {
110 IconRtlMode.Default
111 }
112 )
113 }
114
115 val iconParam: (@Composable BoxScope.() -> Unit)? =
<lambda>null116 icon?.let {
117 {
118 Row {
119 Icon(
120 icon = icon,
121 tint = iconColor,
122 contentDescription = null,
123 modifier = Modifier.size(ChipDefaults.IconSize).clip(CircleShape),
124 rtlMode = iconRtlMode
125 )
126 }
127 }
128 }
129
130 val stateDescriptionSemantics =
131 stringResource(
132 if (checked) {
133 R.string.on
134 } else {
135 R.string.off
136 }
137 )
138 ToggleChip(
139 checked = checked,
140 onCheckedChange = onCheckedChanged,
141 label = labelParam,
142 toggleControl = toggleControlParam,
143 modifier =
<lambda>null144 modifier.fillMaxWidth().semantics { stateDescription = stateDescriptionSemantics },
145 appIcon = iconParam,
146 secondaryLabel = secondaryLabelParam,
147 colors = colors,
148 enabled = enabled,
149 interactionSource = interactionSource
150 )
151 }
152
153 /**
154 * ToggleChipColors that disabled alpha is applied based on [ToggleChipDefaults.toggleChipColors()].
155 * It is used for a ToggleChip which would like to respond to click events, meanwhile it seems
156 * disabled.
157 */
158 @Composable
toggleChipDisabledColorsnull159 fun toggleChipDisabledColors(): ToggleChipColors {
160 val checkedStartBackgroundColor =
161 MaterialTheme.colors.surface.copy(alpha = 0f).compositeOver(MaterialTheme.colors.surface)
162 val checkedEndBackgroundColor =
163 MaterialTheme.colors.primary.copy(alpha = 0.5f).compositeOver(MaterialTheme.colors.surface)
164 val checkedContentColor = MaterialTheme.colors.onSurface
165 val checkedSecondaryContentColor = MaterialTheme.colors.onSurfaceVariant
166 val checkedToggleControlColor = MaterialTheme.colors.secondary
167 val uncheckedStartBackgroundColor = MaterialTheme.colors.surface
168 val uncheckedEndBackgroundColor = uncheckedStartBackgroundColor
169 val uncheckedContentColor = contentColorFor(checkedEndBackgroundColor)
170 val uncheckedSecondaryContentColor = uncheckedContentColor
171 val uncheckedToggleControlColor = uncheckedContentColor
172
173 return ToggleChipDefaults.toggleChipColors(
174 checkedStartBackgroundColor =
175 checkedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
176 checkedEndBackgroundColor = checkedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
177 checkedContentColor = checkedContentColor.copy(alpha = ContentAlpha.disabled),
178 checkedSecondaryContentColor =
179 checkedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
180 checkedToggleControlColor = checkedToggleControlColor.copy(alpha = ContentAlpha.disabled),
181 uncheckedStartBackgroundColor =
182 uncheckedStartBackgroundColor.copy(alpha = ContentAlpha.disabled),
183 uncheckedEndBackgroundColor =
184 uncheckedEndBackgroundColor.copy(alpha = ContentAlpha.disabled),
185 uncheckedContentColor = uncheckedContentColor.copy(alpha = ContentAlpha.disabled),
186 uncheckedSecondaryContentColor =
187 uncheckedSecondaryContentColor.copy(alpha = ContentAlpha.disabled),
188 uncheckedToggleControlColor =
189 uncheckedToggleControlColor.copy(alpha = ContentAlpha.disabled)
190 )
191 }
192
193 /**
194 * ToggleChipColors that theme background color is applied based on
195 * [ToggleChipDefaults.toggleChipColors()]. It is used for a ToggleChip having the same background
196 * color of the screen.
197 */
198 @Composable
toggleChipBackgroundColorsnull199 fun toggleChipBackgroundColors(): ToggleChipColors {
200 val checkedStartBackgroundColor =
201 MaterialTheme.colors.background
202 .copy(alpha = 0f)
203 .compositeOver(MaterialTheme.colors.background)
204 val checkedEndBackgroundColor =
205 MaterialTheme.colors.primary
206 .copy(alpha = 0.5f)
207 .compositeOver(MaterialTheme.colors.background)
208 val checkedContentColor = MaterialTheme.colors.onBackground
209 val checkedSecondaryContentColor = MaterialTheme.colors.onSurfaceVariant
210 val checkedToggleControlColor = MaterialTheme.colors.secondary
211 val uncheckedStartBackgroundColor = MaterialTheme.colors.background
212 val uncheckedEndBackgroundColor = uncheckedStartBackgroundColor
213 val uncheckedContentColor = contentColorFor(checkedEndBackgroundColor)
214 val uncheckedSecondaryContentColor = uncheckedContentColor
215 val uncheckedToggleControlColor = uncheckedContentColor
216
217 return ToggleChipDefaults.toggleChipColors(
218 checkedStartBackgroundColor = checkedStartBackgroundColor,
219 checkedEndBackgroundColor = checkedEndBackgroundColor,
220 checkedContentColor = checkedContentColor,
221 checkedSecondaryContentColor = checkedSecondaryContentColor,
222 checkedToggleControlColor = checkedToggleControlColor,
223 uncheckedStartBackgroundColor = uncheckedStartBackgroundColor,
224 uncheckedEndBackgroundColor = uncheckedEndBackgroundColor,
225 uncheckedContentColor = uncheckedContentColor,
226 uncheckedSecondaryContentColor = uncheckedSecondaryContentColor,
227 uncheckedToggleControlColor = uncheckedToggleControlColor
228 )
229 }
230