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