Skip to content

AuthUI First-party support for Activity Result APIs #1918

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

import com.firebase.ui.auth.AuthUI;
import com.firebase.ui.auth.ErrorCodes;
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
import com.firebase.uidemo.R;
import com.firebase.uidemo.util.ConfigurationUtils;
import com.google.android.gms.tasks.OnCompleteListener;
Expand All @@ -22,19 +24,20 @@

import java.util.List;

import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class AnonymousUpgradeActivity extends AppCompatActivity {
public class AnonymousUpgradeActivity extends AppCompatActivity
implements ActivityResultCallback<FirebaseAuthUIAuthenticationResult> {

private static final String TAG = "AccountLink";

private static final int RC_SIGN_IN = 123;

@BindView(R.id.status_text)
TextView mStatus;

Expand All @@ -52,6 +55,9 @@ public class AnonymousUpgradeActivity extends AppCompatActivity {

private AuthCredential mPendingCredential;

private final ActivityResultLauncher<Intent> signIn =
registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this);

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Expand All @@ -64,8 +70,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
// Occurs after catching an email link
IdpResponse response = IdpResponse.fromResultIntent(getIntent());
if (response != null) {
handleSignInResult(RC_SIGN_IN, ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT,
getIntent());
handleSignInResult(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response);
}
}

Expand All @@ -90,12 +95,12 @@ public void onComplete(@NonNull Task<AuthResult> task) {
@OnClick(R.id.begin_flow)
public void startAuthUI() {
List<AuthUI.IdpConfig> providers = ConfigurationUtils.getConfiguredProviders(this);
Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
Intent signInIntent = AuthUI.getInstance().createSignInIntentBuilder()
.setLogo(R.drawable.firebase_auth_120dp)
.setAvailableProviders(providers)
.enableAnonymousUsersAutoUpgrade()
.build();
startActivityForResult(intent, RC_SIGN_IN);
signIn.launch(signInIntent);
}

@OnClick(R.id.resolve_merge)
Expand Down Expand Up @@ -137,34 +142,25 @@ public void onComplete(@NonNull Task<Void> task) {
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
handleSignInResult(requestCode, resultCode, data);
}

private void handleSignInResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RC_SIGN_IN) {
IdpResponse response = IdpResponse.fromResultIntent(data);
if (response == null) {
// User pressed back button
return;
}
if (resultCode == RESULT_OK) {
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance()
.getCurrentUser()));
} else if (response.getError().getErrorCode() == ErrorCodes
.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
setStatus("Merge conflict: user already exists.");
mResolveMergeButton.setEnabled(true);
mPendingCredential = response.getCredentialForLinking();
} else {
Toast.makeText(this, "Auth error, see logs", Toast.LENGTH_SHORT).show();
Log.w(TAG, "Error: " + response.getError().getMessage(), response.getError());
}

updateUI();
private void handleSignInResult(int resultCode, @Nullable IdpResponse response) {
if (response == null) {
// User pressed back button
return;
}
if (resultCode == RESULT_OK) {
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance()
.getCurrentUser()));
} else if (response.getError().getErrorCode() == ErrorCodes
.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
setStatus("Merge conflict: user already exists.");
mResolveMergeButton.setEnabled(true);
mPendingCredential = response.getCredentialForLinking();
} else {
Toast.makeText(this, "Auth error, see logs", Toast.LENGTH_SHORT).show();
Log.w(TAG, "Error: " + response.getError().getMessage(), response.getError());
}

updateUI();
}

private void updateUI() {
Expand Down Expand Up @@ -212,4 +208,9 @@ private String getUserIdentifier(FirebaseUser user) {
return "unknown";
}
}

@Override
public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) {
handleSignInResult(result.getResultCode(), result.getIdpResponse());
}
}
39 changes: 20 additions & 19 deletions app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import com.firebase.ui.auth.AuthUI.IdpConfig;
import com.firebase.ui.auth.ErrorCodes;
import com.firebase.ui.auth.IdpResponse;
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
import com.firebase.ui.auth.util.ExtraConstants;
import com.firebase.uidemo.R;
import com.firebase.uidemo.util.ConfigurationUtils;
Expand All @@ -45,6 +47,8 @@
import java.util.ArrayList;
import java.util.List;

import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -56,7 +60,8 @@
import butterknife.ButterKnife;
import butterknife.OnClick;

public class AuthUiActivity extends AppCompatActivity {
public class AuthUiActivity extends AppCompatActivity
implements ActivityResultCallback<FirebaseAuthUIAuthenticationResult> {
private static final String TAG = "AuthUiActivity";

private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/";
Expand All @@ -66,8 +71,6 @@ public class AuthUiActivity extends AppCompatActivity {
private static final String FIREBASE_PRIVACY_POLICY_URL = "https://firebase.google" +
".com/terms/analytics/#7_privacy";

private static final int RC_SIGN_IN = 100;

@BindView(R.id.root) View mRootView;

@BindView(R.id.google_provider) CheckBox mUseGoogleProvider;
Expand Down Expand Up @@ -111,6 +114,9 @@ public class AuthUiActivity extends AppCompatActivity {
@BindView(R.id.require_name) CheckBox mRequireName;
@BindView(R.id.use_auth_emulator) CheckBox mUseEmulator;

private final ActivityResultLauncher<Intent> signIn =
registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this);

@NonNull
public static Intent createIntent(@NonNull Context context) {
return new Intent(context, AuthUiActivity.class);
Expand Down Expand Up @@ -237,11 +243,11 @@ public void flipEmailLinkProviderCheckbox(boolean passwordProviderIsChecked) {

@OnClick(R.id.sign_in)
public void signIn() {
startActivityForResult(buildSignInIntent(/*link=*/null), RC_SIGN_IN);
signIn.launch(getSignInIntent(/*link=*/null));
}

public void signInWithEmailLink(@Nullable String link) {
startActivityForResult(buildSignInIntent(link), RC_SIGN_IN);
signIn.launch(getSignInIntent(link));
}

@NonNull
Expand All @@ -254,8 +260,7 @@ public AuthUI getAuthUI() {
return authUI;
}

@NonNull
public Intent buildSignInIntent(@Nullable String link) {
private Intent getSignInIntent(@Nullable String link) {
AuthUI.SignInIntentBuilder builder = getAuthUI().createSignInIntentBuilder()
.setTheme(getSelectedTheme())
.setLogo(getSelectedLogo())
Expand Down Expand Up @@ -290,7 +295,6 @@ public Intent buildSignInIntent(@Nullable String link) {
if (auth.getCurrentUser() != null && auth.getCurrentUser().isAnonymous()) {
builder.enableAnonymousUsersAutoUpgrade();
}

return builder.build();
}

Expand All @@ -309,14 +313,6 @@ public void onComplete(@NonNull Task<AuthResult> task) {
});
}

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
handleSignInResponse(resultCode, data);
}
}

@Override
protected void onResume() {
super.onResume();
Expand All @@ -327,9 +323,7 @@ protected void onResume() {
}
}

private void handleSignInResponse(int resultCode, @Nullable Intent data) {
IdpResponse response = IdpResponse.fromResultIntent(data);

private void handleSignInResponse(int resultCode, @Nullable IdpResponse response) {
// Successfully signed in
if (resultCode == RESULT_OK) {
startSignedInActivity(response);
Expand Down Expand Up @@ -529,4 +523,11 @@ private List<String> getFacebookPermissions() {
private void showSnackbar(@StringRes int errorMessageRes) {
Snackbar.make(mRootView, errorMessageRes, Snackbar.LENGTH_LONG).show();
}

@Override
public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) {
// Successfully signed in
IdpResponse response = result.getIdpResponse();
handleSignInResponse(result.getResultCode(), response);
}
}
4 changes: 4 additions & 0 deletions auth/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ android {
}

dependencies {
implementation(Config.Libs.Androidx.activity)
// The new activity result APIs force us to include Fragment 1.3.0
// See https://issuetracker.google.com/issues/152554847
implementation(Config.Libs.Androidx.fragment)
implementation(Config.Libs.Androidx.design)
implementation(Config.Libs.Androidx.customTabs)
implementation(Config.Libs.Androidx.constraint)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.firebase.ui.auth;

import android.content.Context;
import android.content.Intent;

import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;

import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
* A {@link ActivityResultContract} describing that the caller can launch authentication flow with a
* {@link Intent} and is guaranteed to receive a {@link FirebaseAuthUIAuthenticationResult} as
* result. The given input intent <b>must</b> be created using a
* {@link com.firebase.ui.auth.AuthUI.SignInIntentBuilder} in order to guarantee a successful
* launch of the authentication flow.
*/
public class FirebaseAuthUIActivityResultContract extends
ActivityResultContract<Intent, FirebaseAuthUIAuthenticationResult> {

@NonNull
@Override
public Intent createIntent(@NonNull Context context, Intent input) {
return input;
}

@Override
@NonNull
public FirebaseAuthUIAuthenticationResult parseResult(int resultCode, @Nullable Intent intent) {
return new FirebaseAuthUIAuthenticationResult(resultCode, IdpResponse.fromResultIntent(intent));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.firebase.ui.auth.data.model;

import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
import com.firebase.ui.auth.IdpResponse;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
* Result of launching a {@link FirebaseAuthUIActivityResultContract}
*/
public class FirebaseAuthUIAuthenticationResult {

@Nullable
private final IdpResponse idpResponse;
@NonNull
private final Integer resultCode;

public FirebaseAuthUIAuthenticationResult(@NonNull Integer resultCode, @Nullable IdpResponse idpResponse) {
this.idpResponse = idpResponse;
this.resultCode = resultCode;
}

/**
* The contained {@link IdpResponse} returned from the Firebase library
*/
@Nullable
public IdpResponse getIdpResponse() {
return idpResponse;
}

/**
* The result code of the received activity result
*
* @see android.app.Activity.RESULT_CANCELED
* @see android.app.Activity.RESULT_OK
*/
@NonNull
public Integer getResultCode() {
return resultCode;
}

@Override
public int hashCode() {
int result = idpResponse == null ? 0 : idpResponse.hashCode();
result = 31 * result + resultCode.hashCode();
return result;
}

@Override
public String toString() {
return "FirebaseAuthUIAuthenticationResult{" +
"idpResponse=" + idpResponse +
", resultCode='" + resultCode +
'}';
}
}
2 changes: 2 additions & 0 deletions buildSrc/src/main/kotlin/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,11 @@ object Config {

object Androidx {
const val annotations = "androidx.annotation:annotation:1.1.0"
const val activity = "androidx.activity:activity:1.2.0"
const val customTabs = "androidx.browser:browser:1.0.0"
const val cardView = "androidx.cardview:cardview:1.0.0"
const val constraint = "androidx.constraintlayout:constraintlayout:2.0.4"
const val fragment = "androidx.fragment:fragment:1.3.0"
const val lifecycleCompiler = "androidx.lifecycle:lifecycle-compiler:2.2.0"
const val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:2.2.0"
const val lifecycleRuntime = "androidx.lifecycle:lifecycle-runtime:2.2.0"
Expand Down