From 1063346acbffeccd9507bea6526191716181ddc8 Mon Sep 17 00:00:00 2001 From: Andres Calizaya Date: Sun, 10 Nov 2024 23:22:58 -0300 Subject: [PATCH] Se agrega SignIn con google --- .../example/jetnews/ui/signin/SignInScreen.kt | 66 ++++++++++++------- .../ui/signin/SignInScreenViewModel.kt | 40 +++++++++-- .../example/jetnews/ui/signup/SignUpScreen.kt | 62 ++++------------- .../ui/signup/SignUpScreenViewModel.kt | 15 +---- 4 files changed, 88 insertions(+), 95 deletions(-) diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreen.kt index fdbd55da21..088f918984 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreen.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreen.kt @@ -1,5 +1,7 @@ package com.example.jetnews.ui.signin +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -23,9 +25,12 @@ import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation @@ -34,16 +39,17 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavHostController import androidx.navigation.compose.rememberNavController +import com.example.jetnews.R +import com.example.jetnews.model.authentication.AuthResult import com.example.jetnews.model.authentication.AuthUiState import com.example.jetnews.ui.JetnewsDestinations import com.example.jetnews.ui.ViewModelFactory import com.example.jetnews.ui.components.buttons.AuthButton -import com.example.jetnews.ui.components.buttons.FirebaseGoogleButton -import com.example.jetnews.ui.components.buttons.rememberFirebaseAuthLauncher +import com.example.jetnews.ui.components.buttons.SocialAuthButton import com.example.jetnews.ui.components.textfields.AuthTextField import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.auth.api.signin.GoogleSignInOptions import com.google.firebase.auth.GoogleAuthProvider +import kotlinx.coroutines.launch @Suppress("ktlint:standard:function-naming") @Composable @@ -53,6 +59,7 @@ fun SignInScreen( modifier: Modifier = Modifier, ) { val context = LocalContext.current + val scope = rememberCoroutineScope() // State val email by viewModel.email val password by viewModel.password @@ -67,16 +74,28 @@ fun SignInScreen( } // Firebase Auth launcher - val launcher = - rememberFirebaseAuthLauncher( - onAuthComplete = { account -> - val credential = GoogleAuthProvider.getCredential(account.idToken, null) - viewModel.handleGoogleCredential(credential) - }, - onAuthError = { exception -> - viewModel.handleError(exception.message ?: "Authentication failed") - }, - ) + // ActivityResultLauncher for Google Sign-In + val googleSignInLauncher = + rememberLauncherForActivityResult( + contract = ActivityResultContracts.StartActivityForResult(), + ) { result -> + val task = GoogleSignIn.getSignedInAccountFromIntent(result.data) + when (val accountResult = viewModel.handleSignInResult(task)) { + is AuthResult.Success -> { + val account = accountResult.data + val credential = GoogleAuthProvider.getCredential(account?.idToken, null) + scope.launch { + viewModel.handleGoogleCredential(credential) + } + } + is AuthResult.Error -> { + viewModel.handleError(accountResult.message) + } + else -> { + viewModel.handleError("Unknown error during Google Sign-In") + } + } + } Box( modifier = modifier.fillMaxSize(), @@ -169,18 +188,19 @@ fun SignInScreen( } // Social Sign In - FirebaseGoogleButton( + SocialAuthButton( + text = "Sign in with Google", onClick = { - val gso = - GoogleSignInOptions - .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestIdToken("") // No lo subi al repo, cualquier cosa pedirlo - .requestEmail() - .build() - val googleSignInClient = GoogleSignIn.getClient(context, gso) - launcher.launch(googleSignInClient.signInIntent) + viewModel.signInWithGoogle(googleSignInLauncher) + }, + icon = { + Icon( + painter = painterResource(id = R.drawable.ic_google), + contentDescription = null, + modifier = Modifier.size(24.dp), + tint = Color.Unspecified, + ) }, - enabled = uiState !is AuthUiState.Loading, ) // Sign Up Link diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreenViewModel.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreenViewModel.kt index d3d3821052..b1f655e9c1 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreenViewModel.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/signin/SignInScreenViewModel.kt @@ -1,7 +1,9 @@ package com.example.jetnews.ui.signin import android.content.Context +import android.content.Intent import android.util.Log +import androidx.activity.result.ActivityResultLauncher import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel @@ -9,6 +11,12 @@ import androidx.lifecycle.viewModelScope import com.example.jetnews.data.authentication.AuthRepository import com.example.jetnews.model.authentication.AuthResult import com.example.jetnews.model.authentication.AuthUiState +import com.google.android.gms.auth.api.signin.GoogleSignIn +import com.google.android.gms.auth.api.signin.GoogleSignInAccount +import com.google.android.gms.auth.api.signin.GoogleSignInClient +import com.google.android.gms.auth.api.signin.GoogleSignInOptions +import com.google.android.gms.common.api.ApiException +import com.google.android.gms.tasks.Task import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.FirebaseUser import kotlinx.coroutines.launch @@ -27,16 +35,34 @@ class SignInScreenViewModel( private val _uiState = mutableStateOf(AuthUiState.Initial) val uiState: State = _uiState + private val googleSignInClient: GoogleSignInClient by lazy { + val gso = + GoogleSignInOptions + .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestIdToken("") + .requestEmail() + .build() + GoogleSignIn.getClient(context, gso) + } + + fun signInWithGoogle(googleSignInLauncher: ActivityResultLauncher) { + val signInIntent = googleSignInClient.signInIntent + googleSignInLauncher.launch(signInIntent) + } + + fun handleSignInResult(task: Task): AuthResult = + try { + val account = task.getResult(ApiException::class.java) + AuthResult.Success(account) + } catch (e: ApiException) { + AuthResult.Error(e.message ?: "Google sign-in failed") + } + fun handleGoogleCredential(credential: AuthCredential) { viewModelScope.launch { _uiState.value = AuthUiState.Loading - try { - val result = repository.signInWithGoogle(credential) - handleAuthResult(result) - } catch (e: Exception) { - Log.e("GoogleSignInError", "Error during Google sign-in", e) - handleError(e.message ?: "Google sign-in failed") - } + val result = repository.signInWithGoogle(credential) + handleAuthResult(result) } } diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreen.kt index 340651536d..32019fa8b1 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreen.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreen.kt @@ -14,7 +14,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.PersonAdd -import androidx.compose.material3.Divider import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface @@ -39,12 +38,7 @@ import com.example.jetnews.model.authentication.AuthUiState import com.example.jetnews.ui.JetnewsDestinations import com.example.jetnews.ui.ViewModelFactory import com.example.jetnews.ui.components.buttons.AuthButton -import com.example.jetnews.ui.components.buttons.FirebaseGoogleButton -import com.example.jetnews.ui.components.buttons.rememberFirebaseAuthLauncher import com.example.jetnews.ui.components.textfields.AuthTextField -import com.google.android.gms.auth.api.signin.GoogleSignIn -import com.google.android.gms.auth.api.signin.GoogleSignInOptions -import com.google.firebase.auth.GoogleAuthProvider @Suppress("ktlint:standard:function-naming") @Composable @@ -69,18 +63,6 @@ fun SignUpScreen( } } - // Firebase Auth launcher - val launcher = - rememberFirebaseAuthLauncher( - onAuthComplete = { account -> - val credential = GoogleAuthProvider.getCredential(account.idToken, null) - viewModel.handleGoogleCredential(credential) - }, - onAuthError = { exception -> - viewModel.handleError(exception.message ?: "Authentication failed") - }, - ) - Box( modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center, @@ -89,19 +71,21 @@ fun SignUpScreen( modifier = Modifier .fillMaxWidth() - .padding(24.dp) + .padding(horizontal = 24.dp) .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(16.dp), + verticalArrangement = Arrangement.Center, ) { // Header Icon( imageVector = Icons.Rounded.PersonAdd, contentDescription = null, - modifier = Modifier.size(64.dp), + modifier = Modifier.size(80.dp), tint = MaterialTheme.colorScheme.primary, ) + Spacer(modifier = Modifier.height(16.dp)) + Text( text = "Create Account", style = MaterialTheme.typography.headlineMedium, @@ -114,7 +98,7 @@ fun SignUpScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) - Spacer(modifier = Modifier.height(8.dp)) + Spacer(modifier = Modifier.height(24.dp)) // Form AuthTextField( @@ -153,9 +137,12 @@ fun SignUpScreen( text = (uiState as AuthUiState.Error).message, color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(top = 8.dp), ) } + Spacer(modifier = Modifier.height(24.dp)) + // Sign Up Button AuthButton( text = "Create Account", @@ -163,37 +150,10 @@ fun SignUpScreen( isLoading = uiState is AuthUiState.Loading, ) - // OR Divider - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - ) { - Divider(modifier = Modifier.weight(1f)) - Text( - text = "OR", - modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colorScheme.onSurfaceVariant, - ) - Divider(modifier = Modifier.weight(1f)) - } - - // Firebase Google Button - FirebaseGoogleButton( - onClick = { - val gso = - GoogleSignInOptions - .Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) - .requestIdToken("") // No lo subi al repo, cualquier cosa pedirlo - .requestEmail() - .build() - val googleSignInClient = GoogleSignIn.getClient(context, gso) - launcher.launch(googleSignInClient.signInIntent) - }, - enabled = uiState !is AuthUiState.Loading, - ) + Spacer(modifier = Modifier.height(16.dp)) + // Sign In Link Row( - modifier = Modifier.padding(top = 8.dp), verticalAlignment = Alignment.CenterVertically, ) { Text( diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreenViewModel.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreenViewModel.kt index bfb55b667f..578401f8a3 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreenViewModel.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/signup/SignUpScreenViewModel.kt @@ -9,7 +9,6 @@ import androidx.lifecycle.viewModelScope import com.example.jetnews.data.authentication.AuthRepository import com.example.jetnews.model.authentication.AuthResult import com.example.jetnews.model.authentication.AuthUiState -import com.google.firebase.auth.AuthCredential import com.google.firebase.auth.FirebaseUser import kotlinx.coroutines.launch @@ -69,20 +68,8 @@ class SignUpScreenViewModel( } } - fun handleGoogleCredential(credential: AuthCredential) { - viewModelScope.launch { - _uiState.value = AuthUiState.Loading - try { - val result = repository.signInWithGoogle(credential) - handleAuthResult(result) - } catch (e: Exception) { - handleError(e.message ?: "Google sign in failed") - } - } - } - fun handleError(message: String) { - Log.e("SignInError", message) + Log.e("SignUpError", message) _uiState.value = AuthUiState.Error(message) }