I’m having some trouble migrating from Turbo to Hotwire. I’m trying to use the native screen that manages GoogleSignIn, but it’s not calling the navigator.route(signInUrl), VisitOptions(action = REPLACE).
I’ve checked the docs and the demo multiple times, but I can’t seem to figure out what I’m missing. Any help would be greatly appreciated!
package com.maybeclub.maybeclub.fragments
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.CookieManager
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
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.maybeclub.maybeclub.BuildConfig.BASE_URL
import com.maybeclub.maybeclub.R
import dev.hotwire.core.turbo.visit.VisitOptions
import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink
import dev.hotwire.navigation.fragments.HotwireWebFragment
import dev.hotwire.core.turbo.visit.VisitAction.REPLACE
@HotwireDestinationDeepLink(uri = "hotwire://fragment/google_sign_in")
class GoogleSignInFragment : HotwireWebFragment() {
private lateinit var googleSignInClient: GoogleSignInClient
private lateinit var signInResultLauncher: ActivityResultLauncher<Intent>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
signInResultLauncher = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) { result ->
Log.i("GoogleSignIn", result.toString())
if (result.resultCode == Activity.RESULT_OK) {
val task = GoogleSignIn.getSignedInAccountFromIntent(result.data)
handleSignInResult(task)
}
if (result.resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(requireContext(), "Login cancelado", Toast.LENGTH_LONG).show()
// Log the Intent's extras if available
result.data?.extras?.let { bundle ->
for (key in bundle.keySet()) {
val value = bundle.get(key)
Log.i("GoogleSignInExtras", "Extra key: $key, value: $value")
}
}
}
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_google_sign_in, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.google_oauth_web_client_id)) // Google OAuth Web Client ID
.requestProfile()
.requestEmail()
.build()
googleSignInClient = GoogleSignIn.getClient(requireActivity(), gso)
view.findViewById<Button>(R.id.sign_in_button).setOnClickListener {
signIn()
}
val termsOfServiceTextView: TextView = view.findViewById(R.id.terms_of_service)
termsOfServiceTextView.setOnClickListener {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("${BASE_URL}/public_documents/terms_of_use"))
startActivity(browserIntent)
}
val privacyPolicyTextView: TextView = view.findViewById(R.id.privacy_policy)
privacyPolicyTextView.setOnClickListener {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("${BASE_URL}/public_documents/privacy_policy"))
startActivity(browserIntent)
}
}
private fun signIn() {
val signInIntent = googleSignInClient.signInIntent
signInResultLauncher.launch(signInIntent)
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
try {
val account = completedTask.getResult(ApiException::class.java)
// Signed in successfully, show authenticated UI.
CookieManager.getInstance().setCookie(BASE_URL, "id_token=${account.idToken}") {
val signInUrl = "${BASE_URL}/turbo_native/sessions/create"
navigator.route(signInUrl, VisitOptions(action = REPLACE))
}
} catch (e: ApiException) {
AlertDialog.Builder(requireContext()).apply {
setTitle("Falha na autenticação")
setMessage("Erro: ${e.statusCode}")
setPositiveButton("OK") { dialog, _ -> dialog.dismiss() }
show()
}
}
}
}