1616
1717package com.google.credentialmanager.sample
1818
19+ import android.app.Activity
1920import androidx.compose.foundation.layout.*
2021import androidx.compose.foundation.shape.RoundedCornerShape
2122import androidx.compose.foundation.text.KeyboardOptions
@@ -24,6 +25,7 @@ import androidx.compose.runtime.*
2425import androidx.compose.ui.Alignment
2526import androidx.compose.ui.Modifier
2627import androidx.compose.ui.platform.LocalContext
28+ import androidx.compose.ui.res.stringResource
2729import androidx.compose.ui.text.font.FontWeight
2830import androidx.compose.ui.text.input.KeyboardType
2931import androidx.compose.ui.text.input.PasswordVisualTransformation
@@ -82,122 +84,152 @@ fun SignUpScreen(navController: NavController) {
8284 horizontalAlignment = Alignment .CenterHorizontally ,
8385 verticalArrangement = Arrangement .spacedBy(10 .dp)
8486 ) {
85- Text (
86- text = " Create New Account" ,
87- fontSize = 24 .sp,
88- fontWeight = FontWeight .Bold ,
89- modifier = Modifier .padding(top = 20 .dp)
90- )
91-
92- OutlinedTextField (
93- value = username,
94- onValueChange = viewModel::onUsernameChange,
95- label = { Text (" Enter Username" ) },
96- singleLine = true ,
97- isError = usernameError != null ,
98- modifier = Modifier .fillMaxWidth()
99- )
100- if (usernameError != null ) {
101- Text (usernameError!! , color = MaterialTheme .colorScheme.error, fontSize = 12 .sp)
102- }
103-
87+ SignUpTitle ()
88+ UsernameInput (username, viewModel::onUsernameChange, usernameError)
10489 if (isPasswordInputVisible) {
105- OutlinedTextField (
106- value = password,
107- onValueChange = viewModel::onPasswordChange,
108- label = { Text (" Enter Password" ) },
109- singleLine = true ,
110- visualTransformation = PasswordVisualTransformation (),
111- keyboardOptions = KeyboardOptions (keyboardType = KeyboardType .Password ),
112- isError = passwordError != null ,
113- modifier = Modifier .fillMaxWidth()
114- )
115- if (passwordError != null ) {
116- Text (passwordError!! , color = MaterialTheme .colorScheme.error, fontSize = 12 .sp)
117- }
90+ PasswordInput (password, viewModel::onPasswordChange, passwordError)
11891 }
119-
120- if (isLoading) {
121- Row (
122- verticalAlignment = Alignment .CenterVertically ,
123- modifier = Modifier .padding(vertical = 8 .dp)
124- ) {
125- CircularProgressIndicator (modifier = Modifier .size(24 .dp))
126- Spacer (modifier = Modifier .width(8 .dp))
127- Text (" operation in progress..." )
128- }
129- }
130- if (passkeyCreationError != null ) {
131- Text (
132- passkeyCreationError!! ,
133- color = MaterialTheme .colorScheme.error,
134- fontSize = 12 .sp,
135- textAlign = TextAlign .Center ,
136- modifier = Modifier .padding(vertical = 4 .dp)
137- )
138- }
139- if (passwordCreationError != null ) {
140- Text (
141- passwordCreationError!! ,
142- color = MaterialTheme .colorScheme.error,
143- fontSize = 12 .sp,
144- textAlign = TextAlign .Center ,
145- modifier = Modifier .padding(vertical = 4 .dp)
146- )
147- }
148-
92+ LoadingIndicator (isLoading)
93+ ErrorMessages (passkeyCreationError, passwordCreationError)
14994 Spacer (modifier = Modifier .height(0 .dp))
150-
95+ PasskeySignUp (isLoading, viewModel, activity)
15196 Text (
152- text = " Sign in to your account easily and securely with a passkey. Note: Your biometric data is only stored on your devices and will never be shared with anyone. " ,
97+ text = stringResource( R .string.or_divider) ,
15398 textAlign = TextAlign .Center ,
154- fontSize = 12 .sp,
155- modifier = Modifier .padding(bottom = 8 .dp)
99+ modifier = Modifier .padding(vertical = 8 .dp)
156100 )
101+ PasswordSignUp (isLoading, isPasswordInputVisible, viewModel, activity)
102+ }
103+ }
104+ }
157105
158- Button (
159- onClick = {
106+ @Composable
107+ private fun SignUpTitle () {
108+ Text (
109+ text = stringResource(R .string.create_new_account),
110+ fontSize = 24 .sp,
111+ fontWeight = FontWeight .Bold ,
112+ modifier = Modifier .padding(top = 20 .dp)
113+ )
114+ }
160115
161- viewModel.signUpWithPasskey {
162- createCredential(activity, it) as CreatePublicKeyCredentialResponse
163- }
164- },
165- shape = RoundedCornerShape (4 .dp),
166- enabled = ! isLoading,
167- modifier = Modifier .fillMaxWidth()
168- ) {
169- Text (" Sign Up with passkey" )
170- }
116+ @OptIn(ExperimentalMaterial3Api ::class )
117+ @Composable
118+ private fun UsernameInput (username : String , onUsernameChange : (String ) -> Unit , usernameError : String? ) {
119+ OutlinedTextField (
120+ value = username,
121+ onValueChange = onUsernameChange,
122+ label = { Text (stringResource(R .string.enter_username)) },
123+ singleLine = true ,
124+ isError = usernameError != null ,
125+ modifier = Modifier .fillMaxWidth()
126+ )
127+ if (usernameError != null ) {
128+ Text (usernameError, color = MaterialTheme .colorScheme.error, fontSize = 12 .sp)
129+ }
130+ }
171131
172- Text (
173- text = " -------------------- OR --------------------" ,
174- textAlign = TextAlign .Center ,
175- modifier = Modifier .padding(vertical = 8 .dp)
176- )
132+ @OptIn(ExperimentalMaterial3Api ::class )
133+ @Composable
134+ private fun PasswordInput (password : String , onPasswordChange : (String ) -> Unit , passwordError : String? ) {
135+ OutlinedTextField (
136+ value = password,
137+ onValueChange = onPasswordChange,
138+ label = { Text (stringResource(R .string.enter_password)) },
139+ singleLine = true ,
140+ visualTransformation = PasswordVisualTransformation (),
141+ keyboardOptions = KeyboardOptions (keyboardType = KeyboardType .Password ),
142+ isError = passwordError != null ,
143+ modifier = Modifier .fillMaxWidth()
144+ )
145+ if (passwordError != null ) {
146+ Text (passwordError, color = MaterialTheme .colorScheme.error, fontSize = 12 .sp)
147+ }
148+ }
177149
178- Text (
179- text = " Sign up to your account with a password. Your password will be saved securely with your password provider." ,
180- textAlign = TextAlign .Center ,
181- fontSize = 12 .sp,
182- modifier = Modifier .padding(bottom = 8 .dp)
183- )
150+ @Composable
151+ private fun LoadingIndicator (isLoading : Boolean ) {
152+ if (isLoading) {
153+ Row (
154+ verticalAlignment = Alignment .CenterVertically ,
155+ modifier = Modifier .padding(vertical = 8 .dp)
156+ ) {
157+ CircularProgressIndicator (modifier = Modifier .size(24 .dp))
158+ Spacer (modifier = Modifier .width(8 .dp))
159+ Text (stringResource(R .string.operation_in_progress))
160+ }
161+ }
162+ }
184163
185- Button (
186- onClick = {
187- viewModel.signUpWithPassword {
188- createCredential(activity, it)
189- }
190- },
191- shape = RoundedCornerShape (4 .dp),
192- enabled = ! isLoading,
193- modifier = Modifier .fillMaxWidth()
194- ) {
195- Text (if (isPasswordInputVisible) " Sign up with Password" else " Sign up with a password instead" )
164+ @Composable
165+ private fun ErrorMessages (passkeyCreationError : String? , passwordCreationError : String? ) {
166+ if (passkeyCreationError != null ) {
167+ Text (
168+ passkeyCreationError,
169+ color = MaterialTheme .colorScheme.error,
170+ fontSize = 12 .sp,
171+ textAlign = TextAlign .Center ,
172+ modifier = Modifier .padding(vertical = 4 .dp)
173+ )
174+ }
175+ if (passwordCreationError != null ) {
176+ Text (
177+ passwordCreationError,
178+ color = MaterialTheme .colorScheme.error,
179+ fontSize = 12 .sp,
180+ textAlign = TextAlign .Center ,
181+ modifier = Modifier .padding(vertical = 4 .dp)
182+ )
183+ }
184+ }
185+
186+ @Composable
187+ private fun PasskeySignUp (isLoading : Boolean , viewModel : SignUpViewModel , activity : Activity ) {
188+ Text (
189+ text = stringResource(R .string.passkey_sign_up_description),
190+ textAlign = TextAlign .Center ,
191+ fontSize = 12 .sp,
192+ modifier = Modifier .padding(bottom = 8 .dp)
193+ )
194+
195+ Button (
196+ onClick = {
197+ viewModel.signUpWithPasskey {
198+ createCredential(activity, it) as CreatePublicKeyCredentialResponse
196199 }
197- }
200+ },
201+ shape = RoundedCornerShape (4 .dp),
202+ enabled = ! isLoading,
203+ modifier = Modifier .fillMaxWidth()
204+ ) {
205+ Text (stringResource(R .string.sign_up_with_passkey))
198206 }
199207}
200208
209+ @Composable
210+ private fun PasswordSignUp (isLoading : Boolean , isPasswordInputVisible : Boolean , viewModel : SignUpViewModel , activity : Activity ) {
211+ Text (
212+ text = stringResource(R .string.password_sign_up_description),
213+ textAlign = TextAlign .Center ,
214+ fontSize = 12 .sp,
215+ modifier = Modifier .padding(bottom = 8 .dp)
216+ )
217+
218+ Button (
219+ onClick = {
220+ viewModel.signUpWithPassword {
221+ createCredential(activity, it)
222+ }
223+ },
224+ shape = RoundedCornerShape (4 .dp),
225+ enabled = ! isLoading,
226+ modifier = Modifier .fillMaxWidth()
227+ ) {
228+ Text (if (isPasswordInputVisible) stringResource(R .string.sign_up_with_password) else stringResource(R .string.sign_up_with_password_instead))
229+ }
230+ }
231+
232+
201233@Preview(showBackground = true )
202234@Composable
203235fun SignUpScreenPreview () {
0 commit comments