Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save marlonlom/526f572dabd869dbe31283b0341bdbd1 to your computer and use it in GitHub Desktop.
Save marlonlom/526f572dabd869dbe31283b0341bdbd1 to your computer and use it in GitHub Desktop.
Gist for medium post related to androidx activity result api, moving the firebase auth to the new results api

Adapting FirebaseAuth for the new Activity Result API

Activity Result API is a new set of classes that enhances the way we use the recently deprecated methods startActivityForResult and onActivityResult, thats a usual way for interact with another apps using intents and a response code for checking the result data.

In this short post, i'm sharing to you how adapt the firebase auth flow using the Activity Result APIs

Activity Result APIs are available since AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02. Remember to use -ktx artifacts to get additional extensions that helps you write cleaner Kotlin code.

(app/build.gradle)

implementation 'androidx.activity:activity-ktx:1.2.0-alpha02'
implementation 'androidx.fragment:fragment-ktx:1.3.0-alpha02'

Wait, startActivityForResult() and onActivityResult() are deprecated?

Yes, they are, looking in the Android official documentation, it says that:

While the underlying startActivityForResult() and onActivityResult() APIs are available on the Activity class on all API levels, it is strongly recommended to use the Activity Result APIs introduced in AndroidX Activity 1.2.0-alpha02 and Fragment 1.3.0-alpha02.

In Android studio, when upgraded the dependencies mentioned earlier, i found in the javadoc of the method startActivityForResult and onActivityResult the following:

use #registerForActivityResult(ActivityResultContract, ActivityResultCallback) with the appropriate ActivityResultContract and handling the result in the ActivityResultCallback#onActivityResult(Object) callback.

With that, i thought that is time for migrate to the new Activity Result API.

What about FirebaseAuth

Firebase Authentication provides backend services, easy-to-use SDKs, and ready-made UI libraries to authenticate users to your app. It supports authentication using passwords, phone numbers, popular federated identity providers like Google, Facebook and Twitter, and more.

It includes the FirebaseUI Auth Library, that contains the UI needed for making the login and register flows much easier and integrating with popular authentication providers like Google or Facebook.

When using the library in code, we build an Intent using the builder method in the singleton class com.firebase.ui.auth.AuthUI:

AuthUI.getInstance().createSignInIntentBuilder()

With that, and checking the providers we need for authenticate with firebase, you can create and launch the sign-in intent, inside the intent building, you can set more properties, like theming, set the privacy terms, logo and smart lock capabilities.

// Choose authentication providers
val providers = arrayListOf(
        AuthUI.IdpConfig.EmailBuilder().build()
        /* others providers here*/
        )

// Create and launch sign-in intent
startActivityForResult(
        AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setAvailableProviders(providers)
                .setLogo(R.drawable.auth_logo)
                .setIsSmartLockEnabled(true)
                .build(),
        RC_SIGN_IN)

Later, for checking the response, you might use the onActivityResult method on a Fragment or Activity:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == RC_SIGN_IN) {
        val response = IdpResponse.fromResultIntent(data)
        if (resultCode == Activity.RESULT_OK) {
            // Successfully signed in
            val user = FirebaseAuth.getInstance().currentUser
            // ...
        } else {
            // Sign in failed. Check response.getError().getErrorCode() and handle the error.
        }
    }

Now, that's the oficcially documented way to do this, let's apply the new Activity Result API in this feature.

Migrating to the new Activity Results API

Lets take the snippet that creates the intent builder for tha sign-in flow, the idea here is keep the statement as it is, but, for the sake of the new API, we move that code block to a new Class that implements the abstract androidx.activity.result.contract.ActivityResultContract<I, O> class, which allows the implementation of two methods:

  1. override fun createIntent(context: Context, input: Int?): Intent: Returns the Intent we'll use when starting the Activity/Fragment.
  2. override fun parseResult(resultCode: Int, intent: Intent?): O?: Builds an output object based on the returned Intent and the result code.

When dealing with intents, in this class we provide the definition of that, also, the output response of that intent, or null for error checking.

Moving the firebase auth intent builder

Lets create the activiy result contract class:

<script src="https://gist.github.com/marlonlom/8649e0c8bf0d4734258325ce4ce0e64f.js"></script>

At this point, we ...

  • Created the concrete class for handling the firebase auth flow.
  • Moved the intent builder method and put inside the createIntent method.
  • Moved the IdpResponse.fromResultIntent(data) method and put inside the parseResult method.

Changing the activity result callback in Fragment/Activity

Now that we've created the authentication result contract, it's time to use it in the fragment/activity we need. in this example, we're assuming that we're using a WelcomeFragment, the layout wont be explained here, but, imagine that contains the onboarding flow and a button that executes the sign-in flow.

When applying the new results API, we need to understand the following methods that are included with the new androidx activity and fragment apis:

  1. registerForActivityResult: Register a request to start an activity for result, designated by the given ActivityResultContract.
  2. ActivityResultLauncher.launch(input): Executes the activity result contract using the provided response code.

With that in mind, we apply the changes to the WelcomeFragment:

<script src="https://gist.github.com/marlonlom/33125ec94881996fcb15b3b17f170d99.js"> </script>

At this point, we made the following changes to the fragment...

  • Created a ActivityResultLauncher value object for registering the contract defined before, also, for checking the callback related to the response handling.
  • With the launcher object created before, we change the startActivityForResult method call to authResultLauncher.launch(RC_SIGN_IN) call.

After updating the welcome fragment code for activity result handling, you can test it using the emulator and check that the sign-in flow is working fine.

Conclusion

After migrating the functionality for handling the activity result using the new APIs, I found that we can write more readable and optimized processing of custom intents like firebase auth sign-in flows, understanding the new classes for creating the contract, and the callbacks required for checking the activity results, it becomes more organized and composable-ish to make the calls to another apps, activities and features makes for gettings result type-safe. With Activity Result API, you can use another activity result contracts, for tasks such as dialing, permissions and picture taking, also, like in this post, you can create custom contracts.

...

Thanks for reading, Happy coding!! 😊

Further reading

  1. Getting a result from an activity, Official Android documentation
  2. Easily add sign-in to your Android app with FirebaseUI, Official Firebase documentation
@daya-pangestu
Copy link

how to manage different result code?

@marlonlom
Copy link
Author

@ninggahaken
i think is because of the definition of the result contract. what you can do is model and code the contract for delivering the result based on constraints of the runtime permissions.
i did that before, but, in the scenario of the denial ("never ask again" checkbox) i had some issues handling that denial case.

@PabloXyu
Copy link

PabloXyu commented May 5, 2021

why?

The rest of your code samples in the dot.md file is ok, but 2 of them are not visible... :(
marlonlom-activity-results-api-firebase-auth medium-SCREENSHOT_NEXT md

marlonlom-activity-results-api-firebase-auth medium-SCREENSHOT md

@davidmigloz
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment