skip to Main Content

I am new to Android development and so far I have been really keen to use Alex Mamo’s lecture to adapt my code and find best practices. I am trying to implement the one tap sign in button to my app. So far I was able to reproduce what Alex posted on his Github and works perfectly. But when it comes to my integration to my app, it’s a different story. I get no errors and very little signs that it is almost working, after 2 days of trying I come to a point where I don’t know what I am missing to make it work.

The 2 main differences from my stack to his are my AppModule.kt where I have added the logic to write on a different collection (that is my first thought, that could’ve blocked me)

object AppModule {
@Provides
fun provideContext(
    app: Application
): Context = app.applicationContext

@Provides
fun provideFirebaseAuth() = Firebase.auth

@Provides
fun provideFirebaseFirestore() = Firebase.firestore

@Provides
fun provideItemRef(
    db: FirebaseFirestore
) = db
    .collection(COLLECTION_NAME)

@Provides
fun provideItemRepository(
    itemsRef: CollectionReference
): BudgetRepository = BudgetRepositoryImpl(itemsRef)

@Provides
fun provideUseCases(
    repo: BudgetRepository
) =  UseCases(
    getItems = GetItems(repo)
)

@Provides
fun provideOneTapClient(
    context: Context
) = Identity.getSignInClient(context)

@Provides
@Named(SIGN_IN_REQUEST)
fun provideSignInRequest(
    app: Application
) = BeginSignInRequest.builder()
    .setGoogleIdTokenRequestOptions(
        BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
            .setSupported(true)
            .setServerClientId(app.getString(R.string.web_client_id))
            .setFilterByAuthorizedAccounts(true)
            .build())
    .setAutoSelectEnabled(true)
    .build()

@Provides
@Named(SIGN_UP_REQUEST)
fun provideSignUpRequest(
    app: Application
) = BeginSignInRequest.builder()
    .setGoogleIdTokenRequestOptions(
        BeginSignInRequest.GoogleIdTokenRequestOptions.builder()
            .setSupported(true)
            .setServerClientId(app.getString(R.string.web_client_id))
            .setFilterByAuthorizedAccounts(false)
            .build())
    .build()

@Provides
fun provideGoogleSignInOptions(
    app: Application
) = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
    .requestIdToken(app.getString(R.string.web_client_id))
    .requestEmail()
    .build()

@Provides
fun provideGoogleSignInClient(
    app: Application,
    options: GoogleSignInOptions
) = GoogleSignIn.getClient(app, options)

@Provides
fun provideAuthRepository(
    auth: FirebaseAuth,
    oneTapClient: SignInClient,
    @Named(SIGN_IN_REQUEST)
    signInRequest: BeginSignInRequest,
    @Named(SIGN_UP_REQUEST)
    signUpRequest: BeginSignInRequest,
    signInClient: GoogleSignInClient,
    db: FirebaseFirestore
): AuthRepository = AuthRepositoryImpl(
    auth = auth,
    oneTapClient = oneTapClient,
    signInRequest = signInRequest,
    signUpRequest = signUpRequest,
    signInClient = signInClient,
    db = db
)

}

And the fact that I don’t redirect to a profile page but to a dashboard. So this is it and the result is that sometimes I see the snackBar showing with progress bar but not going anywhere and sometimes not.
Screen Capture of Emulator

setContent {
        MyApp() {
            navController = rememberAnimatedNavController()
            BudgetNavigation(
                navController = navController
            )
            checkAuthState()
        }
    }
}
private fun checkAuthState() {
    if(viewModel.isUserAuthenticated) {
        navigateToDashboardScreen()
    }
}

isUserAuthenticated is the one not receiving the TRUE value, if I place a ! in front I will navigate to my destination.

Please ask me anything if you need me to clarify, I would really like to get it as I have passed now 3 days on this issue…

Thank you 🙂

2

Answers


  1. Chosen as BEST ANSWER

    I have found my issue and this is what solved it:

    @Provides
    fun provideBudgetRepository(
        db: FirebaseFirestore
    ): BudgetRepository = BudgetRepositoryImpl(
    )
    

    instead of:

    @Provides
    fun provideItemRef(
        db: FirebaseFirestore
    ) = db
        .collection(COLLECTION_NAME)
    
    @Provides
    fun provideItemRepository(
        itemsRef: CollectionReference
    ): BudgetRepository = BudgetRepositoryImpl(itemsRef)
    
    @Provides
    fun provideUseCases(
        repo: BudgetRepository
    ) =  UseCases(
        getItems = GetItems(repo)
    )
    

    it was the way I was reaching to Firebase through useCases in the MVVM architecture. To simplify I mimicked the way Firebase writes to Firestore once a User is logged in, thus removing the use of useCases in the architecture and using solely the viewModels to reach out to the repositories!

    Thanks everyone for trying to help! This was motivating and helped me look further!


  2. Hope it will be helpful

    internal class GoogleAuthActivityResultContract :
    ActivityResultContract<Unit, Task<GoogleSignInAccount>>() {
    
    override fun createIntent(context: Context, input: Unit): Intent {
        val googleSignInOptions = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
            .requestEmail()
            .requestIdToken(context.getString(R.string.default_web_client_id))
            .build()
    
        val client = GoogleSignIn.getClient(context, googleSignInOptions)
        return client.signInIntent
    }
    
    override fun parseResult(resultCode: Int, intent: Intent?): Task<GoogleSignInAccount> {
        return if (resultCode == Activity.RESULT_CANCELED) {
            Tasks.forCanceled()
        } else {
            GoogleSignIn.getSignedInAccountFromIntent(intent)
        }
    }}
    

    Important, that you have to put it on clickable

    val googleAuthLauncher = rememberLauncherForActivityResult(
                    GoogleAuthActivityResultContract(),
                    welcomeViewModel::onGoogleAuthResult
                )
    
     Button(stringResource(id = R.string.login_option_google), modifier= Modifier.clickable{googleAuthLauncher.launch()})
    

    and on your viewModel you can handle results as

      fun onGoogleAuthResult(task: Task<GoogleSignInAccount>) {
        if (task.isCanceled) return
        viewModelScope.launch {
            runCatching {
                val account = task.await()
                val authCredential = GoogleAuthProvider
                    .getCredential(account.idToken, null)
    
                val authResult = Firebase.auth.signInWithCredential(authCredential).await()
                val authUser = requireNotNull(authResult.user) { "User from auth result is empty" }
                val tokenResult = authUser.getIdToken(true).await()
                val token = requireNotNull(tokenResult.token) { "Token from firebase is empty" }
    
                authRepository.loginByGoogleToken(token)
            }
                .onSuccess {
                    Log.i("TAG", "Success")
                }
                .onFailureSafe {
                    it.printStackTrace()
                }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search