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 android.graphics.drawable.Drawable
20 import androidx.annotation.StringRes
21 import androidx.compose.foundation.layout.BoxScope
22 import androidx.compose.foundation.layout.PaddingValues
23 import androidx.compose.foundation.layout.Row
24 import androidx.compose.foundation.layout.RowScope
25 import androidx.compose.foundation.layout.fillMaxWidth
26 import androidx.compose.foundation.layout.size
27 import androidx.compose.foundation.shape.CircleShape
28 import androidx.compose.foundation.shape.RoundedCornerShape
29 import androidx.compose.runtime.Composable
30 import androidx.compose.ui.Modifier
31 import androidx.compose.ui.draw.clip
32 import androidx.compose.ui.graphics.Color
33 import androidx.compose.ui.graphics.vector.ImageVector
34 import androidx.compose.ui.res.painterResource
35 import androidx.compose.ui.res.stringResource
36 import androidx.compose.ui.text.font.FontWeight
37 import androidx.compose.ui.text.style.TextAlign
38 import androidx.compose.ui.text.style.TextOverflow
39 import androidx.compose.ui.unit.dp
40 import androidx.wear.compose.material.Chip
41 import androidx.wear.compose.material.ChipColors
42 import androidx.wear.compose.material.ChipDefaults
43 import androidx.wear.compose.material.ContentAlpha
44 import androidx.wear.compose.material.Icon
45 import androidx.wear.compose.material.MaterialTheme
46 import androidx.wear.compose.material.Text
47 import androidx.wear.compose.material.contentColorFor
48 
49 /**
50  * This component is an alternative to [Chip], providing the following:
51  * - a convenient way of providing a label and a secondary label;
52  * - a convenient way of providing an icon, and choosing their size based on the sizes recommended
53  *   by the Wear guidelines;
54  */
55 @Composable
Chipnull56 public fun Chip(
57     label: String,
58     labelMaxLines: Int? = null,
59     onClick: () -> Unit,
60     modifier: Modifier = Modifier,
61     secondaryLabel: String? = null,
62     secondaryLabelMaxLines: Int? = null,
63     icon: Any? = null,
64     iconContentDescription: String? = null,
65     largeIcon: Boolean = false,
66     textColor: Color = MaterialTheme.colors.onSurface,
67     iconColor: Color = Color.Unspecified,
68     colors: ChipColors = chipDefaultColors(),
69     enabled: Boolean = true
70 ) {
71     val iconParam: (@Composable BoxScope.() -> Unit)? =
72         icon?.let {
73             {
74                 val iconSize =
75                     if (largeIcon) {
76                         ChipDefaults.LargeIconSize
77                     } else {
78                         ChipDefaults.IconSize
79                     }
80 
81                 Row {
82                     val iconModifier = Modifier.size(iconSize).clip(CircleShape)
83                     when (icon) {
84                         is ImageVector ->
85                             Icon(
86                                 imageVector = icon,
87                                 tint = iconColor,
88                                 contentDescription = iconContentDescription,
89                                 modifier = iconModifier
90                             )
91                         is Int ->
92                             Icon(
93                                 painter = painterResource(id = icon),
94                                 tint = iconColor,
95                                 contentDescription = iconContentDescription,
96                                 modifier = iconModifier
97                             )
98                         is Drawable ->
99                             Icon(
100                                 painter = rememberDrawablePainter(icon),
101                                 tint = iconColor,
102                                 contentDescription = iconContentDescription,
103                                 modifier = iconModifier
104                             )
105                         else -> {}
106                     }
107                 }
108             }
109         }
110 
111     Chip(
112         label = label,
113         labelMaxLines = labelMaxLines,
114         onClick = onClick,
115         modifier = modifier,
116         secondaryLabel = secondaryLabel,
117         secondaryLabelMaxLines = secondaryLabelMaxLines,
118         icon = iconParam,
119         largeIcon = largeIcon,
120         textColor = textColor,
121         colors = colors,
122         enabled = enabled
123     )
124 }
125 
126 /**
127  * This component is an alternative to [Chip], providing the following:
128  * - a convenient way of providing a label and a secondary label;
129  * - a convenient way of providing an icon, and choosing their size based on the sizes recommended
130  *   by the Wear guidelines;
131  */
132 @Composable
Chipnull133 public fun Chip(
134     @StringRes labelId: Int,
135     labelMaxLines: Int? = null,
136     onClick: () -> Unit,
137     modifier: Modifier = Modifier,
138     @StringRes secondaryLabel: Int? = null,
139     secondaryLabelMaxLines: Int? = null,
140     icon: Any? = null,
141     largeIcon: Boolean = false,
142     textColor: Color = MaterialTheme.colors.onSurface,
143     iconColor: Color = Color.Unspecified,
144     colors: ChipColors = chipDefaultColors(),
145     enabled: Boolean = true
146 ) {
147     Chip(
148         label = stringResource(id = labelId),
149         labelMaxLines = labelMaxLines,
150         onClick = onClick,
151         modifier = modifier,
152         secondaryLabel = secondaryLabel?.let { stringResource(id = it) },
153         secondaryLabelMaxLines = secondaryLabelMaxLines,
154         icon = icon,
155         largeIcon = largeIcon,
156         textColor = textColor,
157         iconColor = iconColor,
158         colors = colors,
159         enabled = enabled
160     )
161 }
162 
163 /**
164  * This component is an alternative to [Chip], providing the following:
165  * - a convenient way of providing a label and a secondary label;
166  */
167 // Setting the color as per
168 // https://source.corp.google.com/piper///depot/google3/java/com/google/android/clockwork/common/wearable/wearmaterial/button/res/color/wear_button_secondary_text_stateful.xml?q=wear_button_secondary_text_stateful
169 @Composable
Chipnull170 public fun Chip(
171     label: String,
172     labelMaxLines: Int? = null,
173     onClick: () -> Unit,
174     modifier: Modifier = Modifier,
175     secondaryLabel: String? = null,
176     secondaryLabelMaxLines: Int? = null,
177     icon: (@Composable BoxScope.() -> Unit)? = null,
178     largeIcon: Boolean = false,
179     textColor: Color = MaterialTheme.colors.onSurface,
180     secondaryTextColor: Color = MaterialTheme.colors.primary,
181     colors: ChipColors = chipDefaultColors(),
182     enabled: Boolean = true
183 ) {
184     val hasSecondaryLabel = secondaryLabel != null
185     val hasIcon = icon != null
186 
187     val labelParam: (@Composable RowScope.() -> Unit) = {
188         Text(
189             text = label,
190             color = textColor,
191             modifier = Modifier.fillMaxWidth(),
192             textAlign = if (hasSecondaryLabel || hasIcon) TextAlign.Start else TextAlign.Center,
193             overflow = TextOverflow.Ellipsis,
194             maxLines = labelMaxLines ?: if (hasSecondaryLabel) 1 else 2,
195             style = MaterialTheme.typography.button.copy(fontWeight = FontWeight.W600)
196         )
197     }
198 
199     val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
200         secondaryLabel?.let {
201             {
202                 Text(
203                     text = secondaryLabel,
204                     color = secondaryTextColor,
205                     overflow = TextOverflow.Ellipsis,
206                     maxLines = secondaryLabelMaxLines ?: 1,
207                     style = MaterialTheme.typography.caption2
208                 )
209             }
210         }
211 
212     val contentPadding =
213         if (largeIcon) {
214             val verticalPadding = ChipDefaults.ChipVerticalPadding
215             PaddingValues(
216                 start = 10.dp,
217                 top = verticalPadding,
218                 end = ChipDefaults.ChipHorizontalPadding,
219                 bottom = verticalPadding
220             )
221         } else {
222             ChipDefaults.ContentPadding
223         }
224 
225     Chip(
226         label = labelParam,
227         onClick = onClick,
228         modifier = modifier.fillMaxWidth(),
229         secondaryLabel = secondaryLabelParam,
230         icon = icon,
231         colors = colors,
232         enabled = enabled,
233         contentPadding = contentPadding,
234         shape = RoundedCornerShape(26.dp)
235     )
236 }
237 
238 /** Default colors of a Chip. */
chipDefaultColorsnull239 @Composable fun chipDefaultColors(): ChipColors = ChipDefaults.secondaryChipColors()
240 
241 /**
242  * ChipColors that disabled alpha is applied based on [ChipDefaults.secondaryChipColors()]. It is
243  * used for a Chip which would like to respond to click events, meanwhile it seems disabled.
244  */
245 @Composable
246 fun chipDisabledColors(): ChipColors {
247     val backgroundColor = MaterialTheme.colors.surface
248     val contentColor = contentColorFor(backgroundColor)
249     val secondaryContentColor = contentColor
250     val iconColor = contentColor
251 
252     return ChipDefaults.chipColors(
253         backgroundColor = backgroundColor.copy(alpha = ContentAlpha.disabled),
254         contentColor = contentColor.copy(alpha = ContentAlpha.disabled),
255         secondaryContentColor = secondaryContentColor.copy(alpha = ContentAlpha.disabled),
256         iconColor = iconColor.copy(alpha = ContentAlpha.disabled)
257     )
258 }
259