diff --git a/JetNews/app/src/main/java/com/example/jetnews/JetnewsApplication.kt b/JetNews/app/src/main/java/com/example/jetnews/JetnewsApplication.kt index f248317c44..2c1b958c0e 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/JetnewsApplication.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/JetnewsApplication.kt @@ -17,12 +17,14 @@ package com.example.jetnews import android.app.Application +import com.google.firebase.analytics.FirebaseAnalytics import com.example.jetnews.data.AppContainer import com.example.jetnews.data.AppContainerImpl class JetnewsApplication : Application() { companion object { const val JETNEWS_APP_URI = "https://developer.android.com/jetnews" + lateinit var firebaseAnalytics: FirebaseAnalytics } // AppContainer instance used by the rest of classes to obtain dependencies @@ -31,5 +33,6 @@ class JetnewsApplication : Application() { override fun onCreate() { super.onCreate() container = AppContainerImpl(this) + firebaseAnalytics = FirebaseAnalytics.getInstance(this) } } diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt index a8c619dc6b..79be91e837 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt @@ -66,6 +66,10 @@ import com.example.jetnews.ui.utils.BookmarkButton import com.example.jetnews.ui.utils.FavoriteButton import com.example.jetnews.ui.utils.ShareButton import com.example.jetnews.ui.utils.TextSettingsButton +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.analytics.logEvent +import com.google.firebase.ktx.Firebase import com.google.firebase.crashlytics.FirebaseCrashlytics import kotlinx.coroutines.runBlocking @@ -90,6 +94,7 @@ fun ArticleScreen( modifier: Modifier = Modifier, lazyListState: LazyListState = rememberLazyListState() ) { + val analytics = Firebase.analytics // Inicializa FirebaseAnalytics var showUnimplementedActionDialog by rememberSaveable { mutableStateOf(false) } if (showUnimplementedActionDialog) { FunctionalityNotAvailablePopup { showUnimplementedActionDialog = false } @@ -120,8 +125,20 @@ fun ArticleScreen( showUnimplementedActionDialog = true triggerCrash("FavoriteButton") }) - BookmarkButton(isBookmarked = isFavorite, onClick = onToggleFavorite) - ShareButton(onClick = { sharePost(post, context) }) + BookmarkButton(isBookmarked = isFavorite, onClick = { + onToggleFavorite() + // Evento personalizado de GOOGLE ANALYTICS para el botón de marcador + analytics.logEvent("bookmark_event") { + param("is_bookmarked", isFavorite.toString()) + } + }) + ShareButton(onClick = { + sharePost(post, context) + // Evento personalizado de GOOGLE ANALYTICS para el botón de compartir + analytics.logEvent("share_event") { + param("post_title", post.title) + } + }) TextSettingsButton(onClick = { showUnimplementedActionDialog = true triggerCrash("TextSettingsButton") diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt index 521034b9f8..bec8b0a298 100644 --- a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt +++ b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt @@ -76,7 +76,11 @@ import com.example.jetnews.data.Result import com.example.jetnews.data.interests.InterestSection import com.example.jetnews.data.interests.TopicSelection import com.example.jetnews.data.interests.impl.FakeInterestsRepository +import com.google.firebase.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.ktx.Firebase import com.example.jetnews.ui.theme.JetnewsTheme +import com.google.firebase.analytics.logEvent import kotlin.math.max import kotlinx.coroutines.runBlocking @@ -263,6 +267,8 @@ private fun TabWithTopics( selectedTopics: Set, onTopicSelect: (String) -> Unit ) { + val analytics = Firebase.analytics // Inicialización de Firebase Analytics + InterestsAdaptiveContentLayout( topPadding = 16.dp, modifier = tabContainerModifier.verticalScroll(rememberScrollState()) @@ -271,7 +277,13 @@ private fun TabWithTopics( TopicItem( itemTitle = topic, selected = selectedTopics.contains(topic), - onToggle = { onTopicSelect(topic) }, + onToggle = { + onTopicSelect(topic) + // Evento personalizado de Analytics + analytics.logEvent("select_interest_topic") { + param("interest_name", topic) + } + }, ) } } @@ -290,6 +302,8 @@ private fun TabWithSections( selectedTopics: Set, onTopicSelect: (TopicSelection) -> Unit ) { + val analytics = Firebase.analytics // Inicialización de Firebase Analytics + Column(tabContainerModifier.verticalScroll(rememberScrollState())) { sections.forEach { (section, topics) -> Text( @@ -304,7 +318,14 @@ private fun TabWithSections( TopicItem( itemTitle = topic, selected = selectedTopics.contains(TopicSelection(section, topic)), - onToggle = { onTopicSelect(TopicSelection(section, topic)) }, + onToggle = { + onTopicSelect(TopicSelection(section, topic)) + // Evento personalizado de Analytics + analytics.logEvent("select_interest_section_topic") { + param("section_name", section) + param("topic_name", topic) + } + }, ) } } 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 088f918984..afae5fb36f 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,7 +1,5 @@ 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 @@ -25,12 +23,9 @@ 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 @@ -39,17 +34,20 @@ 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.SocialAuthButton +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.analytics.FirebaseAnalytics +import com.google.firebase.analytics.ktx.analytics +import com.google.firebase.analytics.logEvent +import com.google.firebase.ktx.Firebase import com.google.firebase.auth.GoogleAuthProvider -import kotlinx.coroutines.launch @Suppress("ktlint:standard:function-naming") @Composable @@ -59,7 +57,7 @@ fun SignInScreen( modifier: Modifier = Modifier, ) { val context = LocalContext.current - val scope = rememberCoroutineScope() + val analytics = Firebase.analytics // Inicialización de Firebase Analytics // State val email by viewModel.email val password by viewModel.password @@ -67,6 +65,10 @@ fun SignInScreen( LaunchedEffect(uiState) { if (uiState is AuthUiState.Success) { + // Registrar evento de inicio de sesión en Analytics cuando el usuario inicia sesión con éxito + analytics.logEvent("user_sign_in") { + param("sign_in_method", "email") // Aquí podrías especificar "email" u otro método + } navHostController.navigate(JetnewsDestinations.HOME_ROUTE) { popUpTo(JetnewsDestinations.SIGNIN_ROUTE) { inclusive = true } } @@ -74,28 +76,21 @@ fun SignInScreen( } // Firebase Auth launcher - // 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) + val launcher = + rememberFirebaseAuthLauncher( + onAuthComplete = { account -> + val credential = GoogleAuthProvider.getCredential(account.idToken, null) + viewModel.handleGoogleCredential(credential) + + // Registrar evento de inicio de sesión con Google en Analytics + analytics.logEvent("user_sign_in") { + param("sign_in_method", "google") } - else -> { - viewModel.handleError("Unknown error during Google Sign-In") - } - } - } + }, + onAuthError = { exception -> + viewModel.handleError(exception.message ?: "Authentication failed") + }, + ) Box( modifier = modifier.fillMaxSize(), @@ -103,10 +98,10 @@ fun SignInScreen( ) { Column( modifier = - Modifier - .fillMaxWidth() - .padding(24.dp) - .verticalScroll(rememberScrollState()), + Modifier + .fillMaxWidth() + .padding(24.dp) + .verticalScroll(rememberScrollState()), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(16.dp), ) { @@ -169,7 +164,13 @@ fun SignInScreen( // Sign In Button AuthButton( text = "Sign In", - onClick = viewModel::onSignInClick, + onClick = { + viewModel.onSignInClick() + // Registrar evento de intento de inicio de sesión + analytics.logEvent("attempt_user_sign_in") { + param("sign_in_method", "email") + } + }, isLoading = uiState is AuthUiState.Loading, ) @@ -188,19 +189,18 @@ fun SignInScreen( } // Social Sign In - SocialAuthButton( - text = "Sign in with Google", + FirebaseGoogleButton( onClick = { - viewModel.signInWithGoogle(googleSignInLauncher) - }, - icon = { - Icon( - painter = painterResource(id = R.drawable.ic_google), - contentDescription = null, - modifier = Modifier.size(24.dp), - tint = Color.Unspecified, - ) + 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, ) // Sign Up Link