Skip to content

Commit 877a70c

Browse files
committed
Merge branch 'version-9.1.0-dev' into feat/edge-to-edge
2 parents cdd7394 + eca0ca3 commit 877a70c

File tree

13 files changed

+181
-118
lines changed

13 files changed

+181
-118
lines changed

README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,16 @@ libraries.
4848
```groovy
4949
dependencies {
5050
// FirebaseUI for Firebase Realtime Database
51-
implementation 'com.firebaseui:firebase-ui-database:8.0.2'
51+
implementation 'com.firebaseui:firebase-ui-database:9.0.0'
5252
5353
// FirebaseUI for Cloud Firestore
54-
implementation 'com.firebaseui:firebase-ui-firestore:8.0.2'
54+
implementation 'com.firebaseui:firebase-ui-firestore:9.0.0'
5555
5656
// FirebaseUI for Firebase Auth
57-
implementation 'com.firebaseui:firebase-ui-auth:8.0.2'
57+
implementation 'com.firebaseui:firebase-ui-auth:9.0.0'
5858
5959
// FirebaseUI for Cloud Storage
60-
implementation 'com.firebaseui:firebase-ui-storage:8.0.2'
60+
implementation 'com.firebaseui:firebase-ui-storage:9.0.0'
6161
}
6262
```
6363

@@ -71,6 +71,7 @@ After the project is synchronized, we're ready to start using Firebase functiona
7171
If you are using an old version of FirebaseUI and upgrading, please see the appropriate
7272
migration guide:
7373

74+
* [Upgrade from 8.0.2 to 9.x.x](./docs/upgrade-to-9.0.md)
7475
* [Upgrade from 7.2.0 to 8.x.x](./docs/upgrade-to-8.0.md)
7576
* [Upgrade from 6.4.0 to 7.x.x](./docs/upgrade-to-7.0.md)
7677
* [Upgrade from 5.1.0 to 6.x.x](./docs/upgrade-to-6.0.md)

app/build.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ android {
1010
namespace = "com.firebase.uidemo"
1111

1212
defaultConfig {
13-
minSdk = Config.SdkVersions.min
13+
minSdk = 23
1414
targetSdk = Config.SdkVersions.target
1515

1616
versionName = Config.version

auth/README.md

+11-73
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ providers such as Google Sign-In, and Facebook Login. It is built on top of
1313

1414
The best practices embodied in FirebaseUI aim to maximize sign-in
1515
and sign-up conversion for your app. It integrates with
16-
[Smart Lock for Passwords](https://developers.google.com/identity/smartlock-passwords/android/)
16+
[Credential Manager](https://developer.android.com/identity/sign-in/credential-manager)
1717
to store and retrieve credentials, enabling automatic and single-tap sign-in to
1818
your app for returning users. It also handles tricky use cases like
1919
account recovery and account linking that are security sensitive and
@@ -38,7 +38,6 @@ and [Web](https://github.com/firebase/firebaseui-web/).
3838
1. [Usage instructions](#using-firebaseui-for-authentication)
3939
1. [AuthUI sign-in](#authui-sign-in)
4040
1. [Handling responses](#handling-the-sign-in-response)
41-
1. [Silent sign-in](#silent-sign-in)
4241
1. [Sign out](#sign-out)
4342
1. [Account deletion](#deleting-accounts)
4443
1. [Upgrading Anonymous Users](#upgrading-anonymous-users)
@@ -65,7 +64,7 @@ Gradle, add the dependency:
6564
```groovy
6665
dependencies {
6766
// ...
68-
implementation 'com.firebaseui:firebase-ui-auth:8.0.2'
67+
implementation 'com.firebaseui:firebase-ui-auth:9.0.0'
6968
7069
// Required only if Facebook login support is required
7170
// Find the latest Facebook SDK releases here: https://github.com/facebook/facebook-android-sdk/blob/master/CHANGELOG.md
@@ -406,45 +405,19 @@ Intent signInIntent =
406405
.build();
407406
```
408407

409-
##### Smart Lock
408+
##### Credential Manager
410409

411-
By default, FirebaseUI uses [Smart Lock for Passwords](https://developers.google.com/identity/smartlock-passwords/android/)
410+
By default, FirebaseUI uses [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager)
412411
to store the user's credentials and automatically sign users into your app on subsequent attempts.
413-
Using Smart Lock is recommended to provide the best user experience, but in some cases you may want
414-
to disable Smart Lock for testing or development. To disable Smart Lock, you can use the
415-
`setIsSmartLockEnabled` method when building your sign-in Intent:
412+
Using Credential Manager is recommended to provide the best user experience, but in some cases you may want
413+
to disable Credential Manager for testing or development. To disable Credential Manager, you can use the
414+
`setCredentialManagerEnabled` method when building your sign-in Intent:
416415
417416
```java
418417
Intent signInIntent =
419418
AuthUI.getInstance()
420419
.createSignInIntentBuilder()
421-
.setIsSmartLockEnabled(false)
422-
.build();
423-
```
424-
425-
###### Smart Lock hints
426-
427-
If you'd like to keep Smart Lock's "hints" but disable the saving/retrieving of credentials, then
428-
you can use the two-argument version of `setIsSmartLockEnabled`:
429-
430-
```java
431-
Intent signInIntent =
432-
AuthUI.getInstance()
433-
.createSignInIntentBuilder()
434-
.setIsSmartLockEnabled(false, true)
435-
.build();
436-
```
437-
438-
###### Smart Lock in dev builds
439-
440-
It is often desirable to disable Smart Lock in development but enable it in production. To achieve
441-
this, you can use the `BuildConfig.DEBUG` flag to control Smart Lock:
442-
443-
```java
444-
Intent signInIntent =
445-
AuthUI.getInstance()
446-
.createSignInIntentBuilder()
447-
.setIsSmartLockEnabled(!BuildConfig.DEBUG /* credentials */, true /* hints */)
420+
.setCredentialManagerEnabled(false)
448421
.build();
449422
```
450423
@@ -603,48 +576,13 @@ if (metadata.getCreationTimestamp() == metadata.getLastSignInTimestamp()) {
603576
}
604577
```
605578

606-
### Silent sign-in
607-
608-
If a user is not currently signed in, then a silent sign-in process can be started first before
609-
displaying any UI to provide a seamless experience. Silent sign-in uses saved Smart Lock credentials
610-
and returns a successful `Task` only if the user has been fully signed in with Firebase.
611-
612-
Here's an example of how you could use silent sign-in paired with Firebase anonymous sign-in to get
613-
your users up and running as fast as possible:
614-
615-
```java
616-
List<IdpConfig> providers = getSelectedProviders();
617-
AuthUI.getInstance().silentSignIn(this, providers)
618-
.continueWithTask(this, new Continuation<AuthResult, Task<AuthResult>>() {
619-
@Override
620-
public Task<AuthResult> then(@NonNull Task<AuthResult> task) {
621-
if (task.isSuccessful()) {
622-
return task;
623-
} else {
624-
// Ignore any exceptions since we don't care about credential fetch errors.
625-
return FirebaseAuth.getInstance().signInAnonymously();
626-
}
627-
}
628-
}).addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
629-
@Override
630-
public void onComplete(@NonNull Task<AuthResult> task) {
631-
if (task.isSuccessful()) {
632-
// Signed in! Start loading data
633-
} else {
634-
// Uh oh, show error message
635-
}
636-
}
637-
});
638-
```
639-
640579
### Sign out
641580

642581
With the integrations provided by AuthUI, signing out a user is a multi-stage process:
643582

644583
1. The user must be signed out of the FirebaseAuth instance.
645-
1. Smart Lock for Passwords must be instructed to disable automatic sign-in, in
646-
order to prevent an automatic sign-in loop that prevents the user from
647-
switching accounts.
584+
1. Credential Manager must be instructed to clear the current user credential state from
585+
all credential providers.
648586
1. If the current user signed in using either Google or Facebook, the user must
649587
also be signed out using the associated API for that authentication method.
650588
This typically ensures that the user will not be automatically signed-in
@@ -677,7 +615,7 @@ if (v.getId() == R.id.sign_out) {
677615
With the integrations provided by FirebaseUI Auth, deleting a user is a multi-stage process:
678616

679617
1. The user must be deleted from Firebase Auth.
680-
1. Smart Lock for Passwords must be told to delete any existing Credentials for the user, so
618+
1. Credential Manager must be told to delete any existing Credentials for the user, so
681619
that they are not automatically prompted to # with a saved credential in the future.
682620

683621
This process is encapsulated by the `AuthUI.delete()` method, which returns a `Task` representing

auth/build.gradle.kts

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ android {
1111
namespace = "com.firebase.ui.auth"
1212

1313
defaultConfig {
14-
minSdk = Config.SdkVersions.min
15-
targetSdk =Config.SdkVersions.target
14+
// Auth requires a higher minSdk than the current Config.SdkVersions.min
15+
minSdk = 23
16+
targetSdk = Config.SdkVersions.target
1617

1718
buildConfigField("String", "VERSION_NAME", "\"${Config.version}\"")
1819

auth/src/main/java/com/firebase/ui/auth/AuthUI.java

+48-18
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,14 @@
1717
import android.content.Context;
1818
import android.content.Intent;
1919
import android.os.Bundle;
20+
import android.os.CancellationSignal;
2021
import android.os.Parcel;
2122
import android.os.Parcelable;
22-
import android.text.TextUtils;
2323
import android.util.Log;
2424

2525
import com.facebook.login.LoginManager;
2626
import com.firebase.ui.auth.data.model.FlowParameters;
2727
import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity;
28-
import com.firebase.ui.auth.util.CredentialUtils;
2928
import com.firebase.ui.auth.util.ExtraConstants;
3029
import com.firebase.ui.auth.util.GoogleApiUtils;
3130
import com.firebase.ui.auth.util.Preconditions;
@@ -35,26 +34,22 @@
3534
import com.google.android.gms.auth.api.signin.GoogleSignIn;
3635
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
3736
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
38-
import com.google.android.gms.common.api.ApiException;
3937
import com.google.android.gms.common.api.CommonStatusCodes;
4038
import com.google.android.gms.common.api.Scope;
4139
import com.google.android.gms.tasks.Task;
40+
import com.google.android.gms.tasks.TaskCompletionSource;
4241
import com.google.android.gms.tasks.Tasks;
4342
import com.google.firebase.FirebaseApp;
4443
import com.google.firebase.auth.ActionCodeSettings;
45-
import com.google.firebase.auth.AuthCredential;
46-
import com.google.firebase.auth.AuthResult;
4744
import com.google.firebase.auth.EmailAuthProvider;
4845
import com.google.firebase.auth.FacebookAuthProvider;
4946
import com.google.firebase.auth.FirebaseAuth;
5047
import com.google.firebase.auth.FirebaseAuthInvalidUserException;
51-
import com.google.firebase.auth.FirebaseAuthProvider;
5248
import com.google.firebase.auth.FirebaseUser;
5349
import com.google.firebase.auth.GithubAuthProvider;
5450
import com.google.firebase.auth.GoogleAuthProvider;
5551
import com.google.firebase.auth.PhoneAuthProvider;
5652
import com.google.firebase.auth.TwitterAuthProvider;
57-
import com.google.firebase.auth.UserInfo;
5853

5954
import java.lang.annotation.Retention;
6055
import java.lang.annotation.RetentionPolicy;
@@ -68,6 +63,8 @@
6863
import java.util.Locale;
6964
import java.util.Map;
7065
import java.util.Set;
66+
import java.util.concurrent.Executor;
67+
import java.util.concurrent.Executors;
7168

7269
import androidx.annotation.CallSuper;
7370
import androidx.annotation.DrawableRes;
@@ -76,6 +73,9 @@
7673
import androidx.annotation.RestrictTo;
7774
import androidx.annotation.StringDef;
7875
import androidx.annotation.StyleRes;
76+
import androidx.credentials.ClearCredentialStateRequest;
77+
import androidx.credentials.CredentialManagerCallback;
78+
import androidx.credentials.exceptions.ClearCredentialException;
7979

8080
/**
8181
* The entry point to the AuthUI authentication flow, and related utility methods. If your
@@ -280,8 +280,9 @@ public Task<Void> signOut(@NonNull Context context) {
280280
if (!playServicesAvailable) {
281281
Log.w(TAG, "Google Play services not available during signOut");
282282
}
283-
284-
return signOutIdps(context).continueWith(task -> {
283+
signOutIdps(context);
284+
Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
285+
return clearCredentialState(context, singleThreadExecutor).continueWith(task -> {
285286
task.getResult(); // Propagate exceptions if any.
286287
mAuth.signOut();
287288
return null;
@@ -303,9 +304,10 @@ public Task<Void> delete(@NonNull final Context context) {
303304
String.valueOf(CommonStatusCodes.SIGN_IN_REQUIRED),
304305
"No currently signed in user."));
305306
}
306-
307-
return signOutIdps(context).continueWithTask(task -> {
308-
task.getResult(); // Propagate exception if there was one.
307+
signOutIdps(context);
308+
Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
309+
return clearCredentialState(context, singleThreadExecutor).continueWithTask(task -> {
310+
task.getResult(); // Propagate exceptions if any.
309311
return currentUser.delete();
310312
});
311313
}
@@ -338,15 +340,43 @@ public int getEmulatorPort() {
338340
return mEmulatorPort;
339341
}
340342

341-
private Task<Void> signOutIdps(@NonNull Context context) {
343+
private void signOutIdps(@NonNull Context context) {
342344
if (ProviderAvailability.IS_FACEBOOK_AVAILABLE) {
343345
LoginManager.getInstance().logOut();
344346
}
345-
if (GoogleApiUtils.isPlayServicesAvailable(context)) {
346-
return GoogleSignIn.getClient(context, GoogleSignInOptions.DEFAULT_SIGN_IN).signOut();
347-
} else {
348-
return Tasks.forResult((Void) null);
349-
}
347+
}
348+
349+
/**
350+
* A Task to clear the credential state in Credential Manager.
351+
* @param context
352+
* @param executor
353+
* @return
354+
*/
355+
private Task<Void> clearCredentialState(
356+
@NonNull Context context,
357+
@NonNull Executor executor
358+
) {
359+
TaskCompletionSource<Void> completionSource = new TaskCompletionSource<>();
360+
361+
ClearCredentialStateRequest clearRequest = new ClearCredentialStateRequest();
362+
GoogleApiUtils.getCredentialManager(context)
363+
.clearCredentialStateAsync(
364+
clearRequest,
365+
new CancellationSignal(),
366+
executor,
367+
new CredentialManagerCallback<>() {
368+
@Override
369+
public void onResult(Void unused) {
370+
completionSource.setResult(unused);
371+
}
372+
373+
@Override
374+
public void onError(@NonNull ClearCredentialException e) {
375+
completionSource.setException(e);
376+
}
377+
}
378+
);
379+
return completionSource.getTask();
350380
}
351381

352382
/**

auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java

+5
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
9595
mSignInButton.setOnClickListener(this);
9696
m#Button.setOnClickListener(this);
9797

98+
// Hide # button for email link authentication
99+
if (getEmailProvider().equals(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
100+
m#Button.setVisibility(View.GONE);
101+
}
102+
98103
TextView termsText = view.findViewById(R.id.email_tos_and_pp_text);
99104
TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
100105
FlowParameters flowParameters = getFlowParams();

auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.kt

+2-1
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ import androidx.credentials.exceptions.GetCredentialException
8686
import com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER
8787
import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_BUTTON_ID
8888
import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_PROVIDER_ID
89+
import com.firebase.ui.auth.util.GoogleApiUtils
8990
import com.google.android.libraries.identity.googleid.GetGoogleIdOption
9091
import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
9192
import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
@@ -105,7 +106,7 @@ class AuthMethodPickerActivity : AppCompatBase() {
105106
// For demonstration, assume that CredentialManager provides a create() method.
106107
private val credentialManager by lazy {
107108
// Replace with your actual CredentialManager instance creation.
108-
CredentialManager.create(this)
109+
GoogleApiUtils.getCredentialManager(this)
109110
}
110111

111112
companion object {

auth/src/main/java/com/firebase/ui/auth/util/GoogleApiUtils.java

+6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import androidx.annotation.NonNull;
1111
import androidx.annotation.RestrictTo;
12+
import androidx.credentials.CredentialManager;
1213

1314
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1415
public final class GoogleApiUtils {
@@ -25,4 +26,9 @@ public static boolean isPlayServicesAvailable(@NonNull Context context) {
2526
public static SignInClient getSignInClient(@NonNull Context context) {
2627
return Identity.getSignInClient(context);
2728
}
29+
30+
@NonNull
31+
public static CredentialManager getCredentialManager(@NonNull Context context) {
32+
return CredentialManager.create(context);
33+
}
2834
}

auth/src/main/java/com/firebase/ui/auth/viewmodel/credentialmanager/CredentialManagerHandler.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import kotlinx.coroutines.launch
1818
class CredentialManagerHandler(application: Application) :
1919
AuthViewModelBase<IdpResponse>(application) {
2020

21-
private val credentialManager = CredentialManager.create(application)
21+
private val credentialManager = GoogleApiUtils.getCredentialManager(application)
2222
private var response: IdpResponse? = null
2323

2424
fun setResponse(newResponse: IdpResponse) {

buildSrc/src/main/kotlin/Config.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
object Config {
2-
const val version = "8.0.2"
2+
const val version = "9.1.0-SNAPSHOT"
33
val submodules = listOf("auth", "common", "firestore", "database", "storage")
44

55
private const val kotlinVersion = "2.1.0"
@@ -53,7 +53,7 @@ object Config {
5353
}
5454

5555
object PlayServices {
56-
const val auth = "com.google.android.gms:play-services-auth:20.3.0"
56+
const val auth = "com.google.android.gms:play-services-auth:21.3.0"
5757
}
5858

5959
object Provider {

0 commit comments

Comments
 (0)