Skip to content

Commit c7f188d

Browse files
authored
AuthUI First-party support for Activity Result APIs (#1918)
1 parent d87a3a9 commit c7f188d

File tree

6 files changed

+152
-53
lines changed

6 files changed

+152
-53
lines changed

app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java

+35-34
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
import com.firebase.ui.auth.AuthUI;
1212
import com.firebase.ui.auth.ErrorCodes;
13+
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
1314
import com.firebase.ui.auth.IdpResponse;
15+
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
1416
import com.firebase.uidemo.R;
1517
import com.firebase.uidemo.util.ConfigurationUtils;
1618
import com.google.android.gms.tasks.OnCompleteListener;
@@ -22,19 +24,20 @@
2224

2325
import java.util.List;
2426

27+
import androidx.activity.result.ActivityResultCallback;
28+
import androidx.activity.result.ActivityResultLauncher;
2529
import androidx.annotation.NonNull;
2630
import androidx.annotation.Nullable;
2731
import androidx.appcompat.app.AppCompatActivity;
2832
import butterknife.BindView;
2933
import butterknife.ButterKnife;
3034
import butterknife.OnClick;
3135

32-
public class AnonymousUpgradeActivity extends AppCompatActivity {
36+
public class AnonymousUpgradeActivity extends AppCompatActivity
37+
implements ActivityResultCallback<FirebaseAuthUIAuthenticationResult> {
3338

3439
private static final String TAG = "AccountLink";
3540

36-
private static final int RC_SIGN_IN = 123;
37-
3841
@BindView(R.id.status_text)
3942
TextView mStatus;
4043

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

5356
private AuthCredential mPendingCredential;
5457

58+
private final ActivityResultLauncher<Intent> signIn =
59+
registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this);
60+
5561
@Override
5662
protected void onCreate(@Nullable Bundle savedInstanceState) {
5763
super.onCreate(savedInstanceState);
@@ -64,8 +70,7 @@ protected void onCreate(@Nullable Bundle savedInstanceState) {
6470
// Occurs after catching an email link
6571
IdpResponse response = IdpResponse.fromResultIntent(getIntent());
6672
if (response != null) {
67-
handleSignInResult(RC_SIGN_IN, ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT,
68-
getIntent());
73+
handleSignInResult(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response);
6974
}
7075
}
7176

@@ -90,12 +95,12 @@ public void onComplete(@NonNull Task<AuthResult> task) {
9095
@OnClick(R.id.begin_flow)
9196
public void startAuthUI() {
9297
List<AuthUI.IdpConfig> providers = ConfigurationUtils.getConfiguredProviders(this);
93-
Intent intent = AuthUI.getInstance().createSignInIntentBuilder()
98+
Intent signInIntent = AuthUI.getInstance().createSignInIntentBuilder()
9499
.setLogo(R.drawable.firebase_auth_120dp)
95100
.setAvailableProviders(providers)
96101
.enableAnonymousUsersAutoUpgrade()
97102
.build();
98-
startActivityForResult(intent, RC_SIGN_IN);
103+
signIn.launch(signInIntent);
99104
}
100105

101106
@OnClick(R.id.resolve_merge)
@@ -137,34 +142,25 @@ public void onComplete(@NonNull Task<Void> task) {
137142
});
138143
}
139144

140-
@Override
141-
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
142-
super.onActivityResult(requestCode, resultCode, data);
143-
handleSignInResult(requestCode, resultCode, data);
144-
}
145-
146-
private void handleSignInResult(int requestCode, int resultCode, Intent data) {
147-
if (requestCode == RC_SIGN_IN) {
148-
IdpResponse response = IdpResponse.fromResultIntent(data);
149-
if (response == null) {
150-
// User pressed back button
151-
return;
152-
}
153-
if (resultCode == RESULT_OK) {
154-
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance()
155-
.getCurrentUser()));
156-
} else if (response.getError().getErrorCode() == ErrorCodes
157-
.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
158-
setStatus("Merge conflict: user already exists.");
159-
mResolveMergeButton.setEnabled(true);
160-
mPendingCredential = response.getCredentialForLinking();
161-
} else {
162-
Toast.makeText(this, "Auth error, see logs", Toast.LENGTH_SHORT).show();
163-
Log.w(TAG, "Error: " + response.getError().getMessage(), response.getError());
164-
}
165-
166-
updateUI();
145+
private void handleSignInResult(int resultCode, @Nullable IdpResponse response) {
146+
if (response == null) {
147+
// User pressed back button
148+
return;
149+
}
150+
if (resultCode == RESULT_OK) {
151+
setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance()
152+
.getCurrentUser()));
153+
} else if (response.getError().getErrorCode() == ErrorCodes
154+
.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
155+
setStatus("Merge conflict: user already exists.");
156+
mResolveMergeButton.setEnabled(true);
157+
mPendingCredential = response.getCredentialForLinking();
158+
} else {
159+
Toast.makeText(this, "Auth error, see logs", Toast.LENGTH_SHORT).show();
160+
Log.w(TAG, "Error: " + response.getError().getMessage(), response.getError());
167161
}
162+
163+
updateUI();
168164
}
169165

170166
private void updateUI() {
@@ -212,4 +208,9 @@ private String getUserIdentifier(FirebaseUser user) {
212208
return "unknown";
213209
}
214210
}
211+
212+
@Override
213+
public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) {
214+
handleSignInResult(result.getResultCode(), result.getIdpResponse());
215+
}
215216
}

app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java

+20-19
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
import com.firebase.ui.auth.AuthUI.IdpConfig;
3232
import com.firebase.ui.auth.ErrorCodes;
3333
import com.firebase.ui.auth.IdpResponse;
34+
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
35+
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
3436
import com.firebase.ui.auth.util.ExtraConstants;
3537
import com.firebase.uidemo.R;
3638
import com.firebase.uidemo.util.ConfigurationUtils;
@@ -45,6 +47,8 @@
4547
import java.util.ArrayList;
4648
import java.util.List;
4749

50+
import androidx.activity.result.ActivityResultCallback;
51+
import androidx.activity.result.ActivityResultLauncher;
4852
import androidx.annotation.DrawableRes;
4953
import androidx.annotation.NonNull;
5054
import androidx.annotation.Nullable;
@@ -56,7 +60,8 @@
5660
import butterknife.ButterKnife;
5761
import butterknife.OnClick;
5862

59-
public class AuthUiActivity extends AppCompatActivity {
63+
public class AuthUiActivity extends AppCompatActivity
64+
implements ActivityResultCallback<FirebaseAuthUIAuthenticationResult> {
6065
private static final String TAG = "AuthUiActivity";
6166

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

69-
private static final int RC_SIGN_IN = 100;
70-
7174
@BindView(R.id.root) View mRootView;
7275

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

117+
private final ActivityResultLauncher<Intent> signIn =
118+
registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this);
119+
114120
@NonNull
115121
public static Intent createIntent(@NonNull Context context) {
116122
return new Intent(context, AuthUiActivity.class);
@@ -237,11 +243,11 @@ public void flipEmailLinkProviderCheckbox(boolean passwordProviderIsChecked) {
237243

238244
@OnClick(R.id.sign_in)
239245
public void signIn() {
240-
startActivityForResult(buildSignInIntent(/*link=*/null), RC_SIGN_IN);
246+
signIn.launch(getSignInIntent(/*link=*/null));
241247
}
242248

243249
public void signInWithEmailLink(@Nullable String link) {
244-
startActivityForResult(buildSignInIntent(link), RC_SIGN_IN);
250+
signIn.launch(getSignInIntent(link));
245251
}
246252

247253
@NonNull
@@ -254,8 +260,7 @@ public AuthUI getAuthUI() {
254260
return authUI;
255261
}
256262

257-
@NonNull
258-
public Intent buildSignInIntent(@Nullable String link) {
263+
private Intent getSignInIntent(@Nullable String link) {
259264
AuthUI.SignInIntentBuilder builder = getAuthUI().createSignInIntentBuilder()
260265
.setTheme(getSelectedTheme())
261266
.setLogo(getSelectedLogo())
@@ -290,7 +295,6 @@ public Intent buildSignInIntent(@Nullable String link) {
290295
if (auth.getCurrentUser() != null && auth.getCurrentUser().isAnonymous()) {
291296
builder.enableAnonymousUsersAutoUpgrade();
292297
}
293-
294298
return builder.build();
295299
}
296300

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

312-
@Override
313-
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
314-
super.onActivityResult(requestCode, resultCode, data);
315-
if (requestCode == RC_SIGN_IN) {
316-
handleSignInResponse(resultCode, data);
317-
}
318-
}
319-
320316
@Override
321317
protected void onResume() {
322318
super.onResume();
@@ -327,9 +323,7 @@ protected void onResume() {
327323
}
328324
}
329325

330-
private void handleSignInResponse(int resultCode, @Nullable Intent data) {
331-
IdpResponse response = IdpResponse.fromResultIntent(data);
332-
326+
private void handleSignInResponse(int resultCode, @Nullable IdpResponse response) {
333327
// Successfully signed in
334328
if (resultCode == RESULT_OK) {
335329
startSignedInActivity(response);
@@ -529,4 +523,11 @@ private List<String> getFacebookPermissions() {
529523
private void showSnackbar(@StringRes int errorMessageRes) {
530524
Snackbar.make(mRootView, errorMessageRes, Snackbar.LENGTH_LONG).show();
531525
}
526+
527+
@Override
528+
public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) {
529+
// Successfully signed in
530+
IdpResponse response = result.getIdpResponse();
531+
handleSignInResponse(result.getResultCode(), response);
532+
}
532533
}

auth/build.gradle.kts

+4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ android {
2626
}
2727

2828
dependencies {
29+
implementation(Config.Libs.Androidx.activity)
30+
// The new activity result APIs force us to include Fragment 1.3.0
31+
// See https://issuetracker.google.com/issues/152554847
32+
implementation(Config.Libs.Androidx.fragment)
2933
implementation(Config.Libs.Androidx.design)
3034
implementation(Config.Libs.Androidx.customTabs)
3135
implementation(Config.Libs.Androidx.constraint)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.firebase.ui.auth;
2+
3+
import android.content.Context;
4+
import android.content.Intent;
5+
6+
import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
7+
8+
import androidx.activity.result.contract.ActivityResultContract;
9+
import androidx.annotation.NonNull;
10+
import androidx.annotation.Nullable;
11+
12+
/**
13+
* A {@link ActivityResultContract} describing that the caller can launch authentication flow with a
14+
* {@link Intent} and is guaranteed to receive a {@link FirebaseAuthUIAuthenticationResult} as
15+
* result. The given input intent <b>must</b> be created using a
16+
* {@link com.firebase.ui.auth.AuthUI.SignInIntentBuilder} in order to guarantee a successful
17+
* launch of the authentication flow.
18+
*/
19+
public class FirebaseAuthUIActivityResultContract extends
20+
ActivityResultContract<Intent, FirebaseAuthUIAuthenticationResult> {
21+
22+
@NonNull
23+
@Override
24+
public Intent createIntent(@NonNull Context context, Intent input) {
25+
return input;
26+
}
27+
28+
@Override
29+
@NonNull
30+
public FirebaseAuthUIAuthenticationResult parseResult(int resultCode, @Nullable Intent intent) {
31+
return new FirebaseAuthUIAuthenticationResult(resultCode, IdpResponse.fromResultIntent(intent));
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.firebase.ui.auth.data.model;
2+
3+
import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
4+
import com.firebase.ui.auth.IdpResponse;
5+
6+
import androidx.annotation.NonNull;
7+
import androidx.annotation.Nullable;
8+
9+
/**
10+
* Result of launching a {@link FirebaseAuthUIActivityResultContract}
11+
*/
12+
public class FirebaseAuthUIAuthenticationResult {
13+
14+
@Nullable
15+
private final IdpResponse idpResponse;
16+
@NonNull
17+
private final Integer resultCode;
18+
19+
public FirebaseAuthUIAuthenticationResult(@NonNull Integer resultCode, @Nullable IdpResponse idpResponse) {
20+
this.idpResponse = idpResponse;
21+
this.resultCode = resultCode;
22+
}
23+
24+
/**
25+
* The contained {@link IdpResponse} returned from the Firebase library
26+
*/
27+
@Nullable
28+
public IdpResponse getIdpResponse() {
29+
return idpResponse;
30+
}
31+
32+
/**
33+
* The result code of the received activity result
34+
*
35+
* @see android.app.Activity.RESULT_CANCELED
36+
* @see android.app.Activity.RESULT_OK
37+
*/
38+
@NonNull
39+
public Integer getResultCode() {
40+
return resultCode;
41+
}
42+
43+
@Override
44+
public int hashCode() {
45+
int result = idpResponse == null ? 0 : idpResponse.hashCode();
46+
result = 31 * result + resultCode.hashCode();
47+
return result;
48+
}
49+
50+
@Override
51+
public String toString() {
52+
return "FirebaseAuthUIAuthenticationResult{" +
53+
"idpResponse=" + idpResponse +
54+
", resultCode='" + resultCode +
55+
'}';
56+
}
57+
}

buildSrc/src/main/kotlin/Config.kt

+2
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ object Config {
2626

2727
object Androidx {
2828
const val annotations = "androidx.annotation:annotation:1.1.0"
29+
const val activity = "androidx.activity:activity:1.2.0"
2930
const val customTabs = "androidx.browser:browser:1.0.0"
3031
const val cardView = "androidx.cardview:cardview:1.0.0"
3132
const val constraint = "androidx.constraintlayout:constraintlayout:2.0.4"
33+
const val fragment = "androidx.fragment:fragment:1.3.0"
3234
const val lifecycleCompiler = "androidx.lifecycle:lifecycle-compiler:2.2.0"
3335
const val lifecycleExtensions = "androidx.lifecycle:lifecycle-extensions:2.2.0"
3436
const val lifecycleRuntime = "androidx.lifecycle:lifecycle-runtime:2.2.0"

0 commit comments

Comments
 (0)