Android Jetpack Navigation: Handle Firebase Auth Signin via Shared MainActivity

When you implement Jetpack Navigation, you would have a single MainActivity with many fragments. You could implement shared code among all fragments in MainActivity, such as sign in using Firebase Authentication.

MainActivity

class MainActivity : AppCompatActivity() {    companion object {        const val REQUEST_SIGN_IN = 1    }    private val viewModel: MainViewModel by viewModels()    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {        super.onActivityResult(requestCode, resultCode, data)        if (requestCode == REQUEST_SIGN_IN) {            val response = IdpResponse.fromResultIntent(data)            val signInRequest = viewModel.requestSignInEvent.value?.peek()            if (resultCode == RESULT_OK) {                val user = FirebaseAuth.getInstance().currentUser                if (user != null) {                    // TODO: init UserPrivate isAnonymouse=false, save email, etc.                    Timber.d("name=${user.displayName}, email=${user.email}, uid=${user.uid}")                    viewModel.initUser(user)                }                signInRequest?.also { request ->                    if (request.liveData != null) {                        request.liveData.value = Event(Resource(data = request.requestCode))                    }                }            }            else {                response?.also { response ->                    // when the signin credential already signup for this app                    // as existing user cannot be linked to existing anonymous user                    if (response.error?.errorCode == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {                        Timber.w("REQUEST_SIGN_IN conflict detected")                        val currentAnonymousUser = FirebaseAuth.getInstance().currentUser                        // TODO: save anonymous data                        val newCredential = response.credentialForLinking                        if (newCredential != null) {                            FirebaseAuth.getInstance().signInWithCredential(newCredential)                                .addOnSuccessListener {                                    val newUser = FirebaseAuth.getInstance().currentUser                                    // TODO: migrate/merge anonymous data to existing user                                    signInRequest?.also { request ->                                        if (request.liveData != null) {                                            request.liveData.value =                                                Event(Resource(data = request.requestCode))                                        }                                    }                                }                        }                    }                    else {                        // TODO: toast                        Timber.e(response.error,"REQUEST_SIGN_IN error")                        signInRequest?.also { request ->                            if (request.liveData != null) {                                request.liveData.value =                                    Event(Resource(exception = response.error!!))                            }                        }                    }                }            }        }    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.main)        // init nav controller for jetpack navigation        // listen to sign in request from fragment        viewModel.requestSignInEvent.observe(this, Observer { event ->            event.getIfPending()?.let {                startActivityForResult(viewModel.getAuthIntent(), REQUEST_SIGN_IN)            }        })    }}

NOTE: Refer Event class.

MainViewModel

class Request(val requestCode: Int = 0, val liveData: MutableLiveData<Event<Resource<Int>>>? = null)class MainViewModel : ViewModel() {    internal val requestSignInEvent = MutableLiveData<Event<Request>>()    fun getAuthIntent(): Intent {        val currentUser = FirebaseAuth.getInstance().currentUser        val providers = if (currentUser == null) {            arrayListOf(                AuthUI.IdpConfig.GoogleBuilder().build(),                // uthUI.IdpConfig.EmailBuilder().build(),                AuthUI.IdpConfig.AnonymousBuilder().build()            )        } else {            arrayListOf(                AuthUI.IdpConfig.GoogleBuilder().build()            )        }        return AuthUI.getInstance()            .createSignInIntentBuilder()            .setIsSmartLockEnabled(false)            .setAvailableProviders(providers)            .enableAnonymousUsersAutoUpgrade()            .build()    }    fun initUser(user: FirebaseUser) {        // TODO: create or update user in database    }}

Fragment

class HomeFragment : Fragment() {    protected val auth: FirebaseAuth by lazy { FirebaseAuth.getInstance()  }    protected val mainViewModel: MainViewModel by activityViewModels()    override fun onActivityCreated(savedInstanceState: Bundle?) {        super.onActivityCreated(savedInstanceState)        if (auth.currentUser == null) {            val liveData = viewModel.signInEvent            mainViewModel.requestSignInEvent.value = Event(Request(liveData = liveData))            // callback upon successful signin            liveData.observe(viewLifecycleOwner, Observer { event ->                event.getIfPending()?.also { resource ->                    if (resource.isSuccessful) {                        init()                    }                    else {                        Toast.makeText(requireContext(), resource.error().toString(), Toast.LENGTH_LONG).show()                    }                }            })        }        else {            init()        }    }    private fun init() {        // TODO: init data and UI    }}
class HomeViewModel : ViewModel() {    internal val signInEvent = MutableLiveData<Event<Resource<Int>>>()}

❤️ Is this article helpful?

Buy me a coffee ☕ or support my work via PayPal to keep this space 🖖 and ad-free.

Do send some 💖 to @d_luaz or share this article.

✨ By Desmond Lua

A dream boy who enjoys making apps, travelling and making youtube videos. Follow me on @d_luaz

👶 Apps I built

Travelopy - discover travel places in Malaysia, Singapore, Taiwan, Japan.