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 package com.android.credentialmanager.ui.components
17 
18 import androidx.wear.compose.material.MaterialTheme as WearMaterialTheme
19 import androidx.compose.foundation.layout.Row
20 import androidx.compose.material3.Icon
21 import android.graphics.drawable.Drawable
22 import androidx.compose.foundation.layout.fillMaxWidth
23 import androidx.compose.foundation.layout.Arrangement
24 import androidx.compose.foundation.layout.BoxScope
25 import androidx.compose.foundation.layout.RowScope
26 import androidx.compose.foundation.layout.size
27 import androidx.compose.material.icons.Icons
28 import androidx.compose.material.icons.outlined.Lock
29 import androidx.compose.material.icons.outlined.LockOpen
30 import androidx.compose.runtime.Composable
31 import androidx.compose.ui.Alignment
32 import androidx.compose.ui.Modifier
33 import androidx.compose.ui.res.stringResource
34 import androidx.compose.ui.tooling.preview.Preview
35 import androidx.compose.ui.unit.dp
36 import androidx.wear.compose.material.Chip
37 import androidx.core.graphics.drawable.toBitmap
38 import androidx.wear.compose.material.ChipColors
39 import androidx.compose.ui.graphics.asImageBitmap
40 import androidx.compose.ui.graphics.Color
41 import androidx.compose.ui.text.style.TextAlign
42 import androidx.wear.compose.material.ChipDefaults
43 import com.android.credentialmanager.R
44 import com.android.credentialmanager.common.ui.components.WearButtonText
45 import com.android.credentialmanager.common.ui.components.WearSecondaryLabel
46 import com.android.credentialmanager.model.get.AuthenticationEntryInfo
47 
48 /* Used as credential suggestion or user action chip. */
49 @Composable
CredentialsScreenChipnull50 fun CredentialsScreenChip(
51     primaryText: @Composable () -> Unit,
52     secondaryText: (@Composable () -> Unit)? = null,
53     onClick: () -> Unit,
54     icon: Drawable? = null,
55     isAuthenticationEntryLocked: Boolean? = null,
56     modifier: Modifier = Modifier,
57     colors: ChipColors = ChipDefaults.secondaryChipColors()
58 ) {
59     val labelParam: (@Composable RowScope.() -> Unit) =
60         {
61             var horizontalArrangement = Arrangement.Start
62             if (icon == null) {
63                 horizontalArrangement = Arrangement.Center
64             }
65             Row(horizontalArrangement = horizontalArrangement, modifier = modifier.fillMaxWidth()) {
66                 primaryText()
67             }
68         }
69 
70     val secondaryLabelParam: (@Composable RowScope.() -> Unit)? =
71         secondaryText?.let {
72             {
73                 Row {
74                     secondaryText()
75                     if (isAuthenticationEntryLocked != null) {
76                         if (isAuthenticationEntryLocked) {
77                             Icon(
78                                 Icons.Outlined.Lock,
79                                 contentDescription = null,
80                                 modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
81                                 tint = WearMaterialTheme.colors.onSurfaceVariant
82                             )
83                         } else {
84                             Icon(
85                                 Icons.Outlined.LockOpen,
86                                 contentDescription = null,
87                                 modifier = Modifier.size(12.dp).align(Alignment.CenterVertically),
88                                 tint = WearMaterialTheme.colors.onSurfaceVariant
89                             )
90                         }
91                     }
92                 }
93             }
94         }
95 
96     val iconParam: (@Composable BoxScope.() -> Unit)? =
97         icon?.toBitmap()?.asImageBitmap()?.let {
98             {
99                 Icon(
100                     bitmap = it,
101                     // Decorative purpose only.
102                     contentDescription = null,
103                     modifier = Modifier.size(24.dp),
104                     tint = Color.Unspecified
105                 )
106             }
107         }
108 
109     Chip(
110         label = labelParam,
111         onClick = onClick,
112         modifier = modifier.fillMaxWidth(),
113         secondaryLabel = secondaryLabelParam,
114         icon = iconParam,
115         colors = colors,
116         enabled = true,
117     )
118 }
119 
120 @Preview
121 @Composable
CredentialsScreenChipPreviewnull122 fun CredentialsScreenChipPreview() {
123     CredentialsScreenChip(
124         primaryText = {
125             WearButtonText(
126                 text = "Elisa Beckett",
127                 textAlign = TextAlign.Start,
128             )
129         },
130         onClick = { },
131         secondaryText = {
132             WearSecondaryLabel(
133                 text = "[email protected]",
134                 color = WearMaterialTheme.colors.onSurfaceVariant
135             )
136         },
137         icon = null,
138     )
139 }
140 
141 @Composable
SignInOptionsChipnull142 fun SignInOptionsChip(onClick: () -> Unit) {
143     CredentialsScreenChip(
144         primaryText = {
145             WearButtonText(
146                 text = stringResource(R.string.dialog_sign_in_options_button),
147                 textAlign = TextAlign.Center,
148                 maxLines = 2
149             )
150         },
151         onClick = onClick,
152     )
153 }
154 
155 @Preview
156 @Composable
SignInOptionsChipPreviewnull157 fun SignInOptionsChipPreview() {
158     SignInOptionsChip({})
159 }
160 
161 @Composable
ContinueChipnull162 fun ContinueChip(onClick: () -> Unit) {
163     CredentialsScreenChip(
164         onClick = onClick,
165         primaryText = {
166             WearButtonText(
167                 text = stringResource(R.string.dialog_continue_button),
168                 textAlign = TextAlign.Center,
169                 color = WearMaterialTheme.colors.surface,
170             )
171         },
172         colors = ChipDefaults.primaryChipColors(),
173     )
174 }
175 
176 @Preview
177 @Composable
ContinueChipPreviewnull178 fun ContinueChipPreview() {
179     ContinueChip({})
180 }
181 
182 @Composable
DismissChipnull183 fun DismissChip(onClick: () -> Unit) {
184     CredentialsScreenChip(
185         primaryText = {
186             WearButtonText(
187                 text = stringResource(R.string.dialog_dismiss_button),
188                 textAlign = TextAlign.Center,
189                 maxLines = 2
190             )
191         },
192         onClick = onClick,
193     )
194 }
195 @Composable
LockedProviderChipnull196 fun LockedProviderChip(
197     authenticationEntryInfo: AuthenticationEntryInfo,
198     secondaryMaxLines: Int = 1,
199     onClick: () -> Unit,
200 ) {
201     val secondaryLabel = stringResource(
202         if (authenticationEntryInfo.isUnlockedAndEmpty)
203             R.string.locked_credential_entry_label_subtext_no_sign_in
204         else R.string.locked_credential_entry_label_subtext_tap_to_unlock
205     )
206 
207     CredentialsScreenChip(
208         primaryText = {
209             WearButtonText(
210                 text = authenticationEntryInfo.title,
211                 textAlign = TextAlign.Start,
212                 maxLines = 2,
213             )
214         },
215         icon = authenticationEntryInfo.icon,
216         secondaryText = {
217             WearSecondaryLabel(
218                 text = secondaryLabel,
219                 color = WearMaterialTheme.colors.onSurfaceVariant,
220                 maxLines = secondaryMaxLines
221                 )
222         },
223         isAuthenticationEntryLocked = !authenticationEntryInfo.isUnlockedAndEmpty,
224         onClick = onClick,
225     )
226 }
227 
228 @Preview
229 @Composable
DismissChipPreviewnull230 fun DismissChipPreview() {
231     DismissChip({})
232 }
233 
234