diff --git a/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java index d31b69845..ace5b7178 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java @@ -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; @@ -22,6 +24,8 @@ 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; @@ -29,12 +33,11 @@ import butterknife.ButterKnife; import butterknife.OnClick; -public class AnonymousUpgradeActivity extends AppCompatActivity { +public class AnonymousUpgradeActivity extends AppCompatActivity + implements ActivityResultCallback { private static final String TAG = "AccountLink"; - private static final int RC_SIGN_IN = 123; - @BindView(R.id.status_text) TextView mStatus; @@ -52,6 +55,9 @@ public class AnonymousUpgradeActivity extends AppCompatActivity { private AuthCredential mPendingCredential; + private final ActivityResultLauncher signIn = + registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this); + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -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); } } @@ -90,12 +95,12 @@ public void onComplete(@NonNull Task task) { @OnClick(R.id.begin_flow) public void startAuthUI() { List 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) @@ -137,34 +142,25 @@ public void onComplete(@NonNull Task 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() { @@ -212,4 +208,9 @@ private String getUserIdentifier(FirebaseUser user) { return "unknown"; } } + + @Override + public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) { + handleSignInResult(result.getResultCode(), result.getIdpResponse()); + } } diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java index adab4512f..539fa0e63 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -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; @@ -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; @@ -56,7 +60,8 @@ import butterknife.ButterKnife; import butterknife.OnClick; -public class AuthUiActivity extends AppCompatActivity { +public class AuthUiActivity extends AppCompatActivity + implements ActivityResultCallback { private static final String TAG = "AuthUiActivity"; private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/"; @@ -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; @@ -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 signIn = + registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this); + @NonNull public static Intent createIntent(@NonNull Context context) { return new Intent(context, AuthUiActivity.class); @@ -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 @@ -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()) @@ -290,7 +295,6 @@ public Intent buildSignInIntent(@Nullable String link) { if (auth.getCurrentUser() != null && auth.getCurrentUser().isAnonymous()) { builder.enableAnonymousUsersAutoUpgrade(); } - return builder.build(); } @@ -309,14 +313,6 @@ public void onComplete(@NonNull Task 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(); @@ -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); @@ -529,4 +523,11 @@ private List 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); + } } diff --git a/auth/build.gradle.kts b/auth/build.gradle.kts index 53931f7e3..7675ee9da 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -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) diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java new file mode 100644 index 000000000..1edbd3a8e --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java @@ -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 must 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 { + + @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)); + } + +} diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java b/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java new file mode 100644 index 000000000..eea7deabc --- /dev/null +++ b/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java @@ -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 + + '}'; + } +} diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 2156953a6..108519e21 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -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"