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>>>()}