Skip to content

Commit 85d7b70

Browse files
SUPERCILEXsamtstern
authored andcommitted
Completely rewrite CredentialsApiHelper.java and hopefully fix memory leak (#566)
1 parent 33b47d7 commit 85d7b70

File tree

11 files changed

+257
-39
lines changed

11 files changed

+257
-39
lines changed

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

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,15 @@
2323
import android.support.annotation.Nullable;
2424
import android.support.annotation.StyleRes;
2525
import android.support.annotation.VisibleForTesting;
26+
import android.support.v4.app.FragmentActivity;
2627

2728
import com.facebook.login.LoginManager;
2829
import com.firebase.ui.auth.ui.FlowParameters;
2930
import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity;
31+
import com.firebase.ui.auth.util.CredentialTaskApi;
3032
import com.firebase.ui.auth.util.CredentialsApiHelper;
3133
import com.firebase.ui.auth.util.GoogleApiClientTaskHelper;
34+
import com.firebase.ui.auth.util.GoogleSignInHelper;
3235
import com.firebase.ui.auth.util.Preconditions;
3336
import com.firebase.ui.auth.util.signincontainer.SmartLockBase;
3437
import com.google.android.gms.auth.api.Auth;
@@ -313,10 +316,12 @@ public static int getDefaultTheme() {
313316
* Signs the current user out, if one is signed in.
314317
*
315318
* @param activity The activity requesting the user be signed out.
316-
* @return a task which, upon completion, signals that the user has been signed out
317-
* ({@code result.isSuccess()}, or that the sign-out attempt failed unexpectedly
318-
* ({@code !result.isSuccess()}).
319+
* @return a task which, upon completion, signals that the user has been signed out ({@code
320+
* result.isSuccess()}, or that the sign-out attempt failed unexpectedly ({@code
321+
* !result.isSuccess()}).
322+
* @deprecated use {@link #signOut(FragmentActivity)} instead
319323
*/
324+
@Deprecated
320325
public Task<Void> signOut(@NonNull Activity activity) {
321326
// Get helper for Google Sign In and Credentials API
322327
GoogleApiClientTaskHelper taskHelper = GoogleApiClientTaskHelper.getInstance(activity);
@@ -325,7 +330,7 @@ public Task<Void> signOut(@NonNull Activity activity) {
325330
.addApi(Auth.GOOGLE_SIGN_IN_API, GoogleSignInOptions.DEFAULT_SIGN_IN);
326331

327332
// Get Credentials Helper
328-
CredentialsApiHelper credentialsHelper = CredentialsApiHelper.getInstance(taskHelper);
333+
CredentialTaskApi credentialsHelper = CredentialsApiHelper.getInstance(taskHelper);
329334

330335
// Firebase Sign out
331336
mAuth.signOut();
@@ -359,8 +364,62 @@ public Void then(@NonNull Task<GoogleApiClient> task) throws Exception {
359364
* silently.
360365
*
361366
* @param activity the calling {@link Activity}.
367+
* @deprecated use {@link #delete(FragmentActivity)} instead
362368
*/
369+
@Deprecated
363370
public Task<Void> delete(@NonNull Activity activity) {
371+
// Initialize SmartLock helper
372+
GoogleApiClientTaskHelper gacHelper = GoogleApiClientTaskHelper.getInstance(activity);
373+
gacHelper.getBuilder().addApi(Auth.CREDENTIALS_API);
374+
CredentialTaskApi credentialHelper = CredentialsApiHelper.getInstance(gacHelper);
375+
376+
return getDeleteTask(credentialHelper);
377+
}
378+
379+
/**
380+
* Signs the current user out, if one is signed in.
381+
*
382+
* @param activity the activity requesting the user be signed out
383+
* @return A task which, upon completion, signals that the user has been signed out ({@link
384+
* Task#isSuccessful()}, or that the sign-out attempt failed unexpectedly !{@link
385+
* Task#isSuccessful()}).
386+
*/
387+
public Task<Void> signOut(@NonNull FragmentActivity activity) {
388+
// Get Credentials Helper
389+
GoogleSignInHelper credentialsHelper = GoogleSignInHelper.getInstance(activity);
390+
391+
// Firebase Sign out
392+
mAuth.signOut();
393+
394+
// Disable credentials auto sign-in
395+
Task<Status> disableCredentialsTask = credentialsHelper.disableAutoSignIn();
396+
397+
// Google sign out
398+
Task<Status> signOutTask = credentialsHelper.signOut();
399+
400+
// Facebook sign out
401+
LoginManager.getInstance().logOut();
402+
403+
// Wait for all tasks to complete
404+
return Tasks.whenAll(disableCredentialsTask, signOutTask);
405+
}
406+
407+
/**
408+
* Delete the use from FirebaseAuth and delete any associated credentials from the Credentials
409+
* API. Returns a {@link Task} that succeeds if the Firebase Auth user deletion succeeds and
410+
* fails if the Firebase Auth deletion fails. Credentials deletion failures are handled
411+
* silently.
412+
*
413+
* @param activity the calling {@link Activity}.
414+
*/
415+
public Task<Void> delete(@NonNull FragmentActivity activity) {
416+
// Initialize SmartLock helper
417+
CredentialTaskApi credentialHelper = GoogleSignInHelper.getInstance(activity);
418+
419+
return getDeleteTask(credentialHelper);
420+
}
421+
422+
private Task<Void> getDeleteTask(CredentialTaskApi credentialHelper) {
364423
FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
365424
if (firebaseUser == null) {
366425
// If the current user is null, return a failed task immediately
@@ -370,11 +429,6 @@ public Task<Void> delete(@NonNull Activity activity) {
370429
// Delete the Firebase user
371430
Task<Void> deleteUserTask = firebaseUser.delete();
372431

373-
// Initialize SmartLock helper
374-
GoogleApiClientTaskHelper gacHelper = GoogleApiClientTaskHelper.getInstance(activity);
375-
gacHelper.getBuilder().addApi(Auth.CREDENTIALS_API);
376-
CredentialsApiHelper credentialHelper = CredentialsApiHelper.getInstance(gacHelper);
377-
378432
// Get all SmartLock credentials associated with the user
379433
List<Credential> credentials = SmartLockBase.credentialsFromFirebaseUser(firebaseUser);
380434

@@ -557,8 +611,8 @@ public SignInIntentBuilder setTosUrl(@Nullable String tosUrl) {
557611
* <p>If no providers are explicitly specified by calling this method, then the email
558612
* provider is the default supported provider.
559613
*
560-
* @param idpConfigs a list of {@link IdpConfig}s, where each {@link IdpConfig} contains
561-
* the configuration parameters for the IDP.
614+
* @param idpConfigs a list of {@link IdpConfig}s, where each {@link IdpConfig} contains the
615+
* configuration parameters for the IDP.
562616
* @see IdpConfig
563617
*/
564618
public SignInIntentBuilder setProviders(@NonNull List<IdpConfig> idpConfigs) {

auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import com.firebase.ui.auth.AuthUI.IdpConfig;
3030
import com.firebase.ui.auth.IdpResponse;
3131
import com.firebase.ui.auth.R;
32-
import com.firebase.ui.auth.util.GoogleApiConstants;
32+
import com.firebase.ui.auth.util.GoogleApiHelper;
3333
import com.google.android.gms.auth.api.Auth;
3434
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
3535
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
@@ -80,7 +80,7 @@ public GoogleProvider(FragmentActivity activity, IdpConfig idpConfig, @Nullable
8080
}
8181

8282
mGoogleApiClient = new GoogleApiClient.Builder(activity)
83-
.enableAutoManage(activity, GoogleApiConstants.AUTO_MANAGE_ID0, this)
83+
.enableAutoManage(activity, GoogleApiHelper.getSafeAutoManageId(), this)
8484
.addApi(Auth.GOOGLE_SIGN_IN_API, builder.build())
8585
.build();
8686
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import com.firebase.ui.auth.ui.TaskFailureLogger;
2424
import com.firebase.ui.auth.ui.User;
2525
import com.firebase.ui.auth.ui.email.fieldvalidators.EmailFieldValidator;
26-
import com.firebase.ui.auth.util.GoogleApiConstants;
26+
import com.firebase.ui.auth.util.GoogleApiHelper;
2727
import com.google.android.gms.auth.api.Auth;
2828
import com.google.android.gms.auth.api.credentials.Credential;
2929
import com.google.android.gms.auth.api.credentials.CredentialPickerConfig;
@@ -234,7 +234,7 @@ private void showEmailAutoCompleteHint() {
234234
private PendingIntent getEmailHintIntent() {
235235
GoogleApiClient client = new GoogleApiClient.Builder(getContext())
236236
.addApi(Auth.CREDENTIALS_API)
237-
.enableAutoManage(getActivity(), GoogleApiConstants.AUTO_MANAGE_ID3,
237+
.enableAutoManage(getActivity(), GoogleApiHelper.getSafeAutoManageId(),
238238
new GoogleApiClient.OnConnectionFailedListener() {
239239
@Override
240240
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.firebase.ui.auth.util;
2+
3+
import com.google.android.gms.auth.api.credentials.Credential;
4+
import com.google.android.gms.common.api.Status;
5+
import com.google.android.gms.tasks.Task;
6+
7+
public interface CredentialTaskApi {
8+
Task<Status> disableAutoSignIn();
9+
10+
Task<Status> delete(Credential credential);
11+
}

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@
3131
* A {@link com.google.android.gms.tasks.Task Task} based wrapper for the Smart Lock for Passwords
3232
* API.
3333
*/
34-
public class CredentialsApiHelper {
34+
@Deprecated
35+
public class CredentialsApiHelper implements CredentialTaskApi {
3536
@NonNull
3637
private final GoogleApiClientTaskHelper mClientHelper;
3738

@@ -51,6 +52,7 @@ public static CredentialsApiHelper getInstance(GoogleApiClientTaskHelper taskHel
5152
return new CredentialsApiHelper(taskHelper);
5253
}
5354

55+
@Override
5456
public Task<Status> delete(final Credential credential) {
5557
return mClientHelper.getConnectedGoogleApiClient().continueWithTask(
5658
new ExceptionForwardingContinuation<GoogleApiClient, Status>() {
@@ -64,6 +66,7 @@ protected void process(
6466
});
6567
}
6668

69+
@Override
6770
public Task<Status> disableAutoSignIn() {
6871
return mClientHelper.getConnectedGoogleApiClient().continueWithTask(
6972
new ExceptionForwardingContinuation<GoogleApiClient, Status>() {
@@ -82,20 +85,20 @@ public void onResult(@NonNull Status status) {
8285
});
8386
}
8487

85-
private abstract static class ExceptionForwardingContinuation<InT, OutT>
86-
implements Continuation<InT, Task<OutT>> {
88+
private abstract static class ExceptionForwardingContinuation<TIn, TOut>
89+
implements Continuation<TIn, Task<TOut>> {
8790

8891
@Override
89-
public final Task<OutT> then(@NonNull Task<InT> task) throws Exception {
90-
TaskCompletionSource<OutT> source = new TaskCompletionSource<>();
92+
public final Task<TOut> then(@NonNull Task<TIn> task) throws Exception {
93+
TaskCompletionSource<TOut> source = new TaskCompletionSource<>();
9194
// calling task.getResult() will implicitly re-throw the exception of the original
9295
// task, which will be returned as the result for the output task. Similarly,
9396
// if process() throws an exception, this will be turned into the task result.
9497
process(task.getResult(), source);
9598
return source.getTask();
9699
}
97100

98-
protected abstract void process(InT in, TaskCompletionSource<OutT> result);
101+
protected abstract void process(TIn in, TaskCompletionSource<TOut> result);
99102
}
100103

101104
private static final class TaskResultCaptor<R extends Result> implements ResultCallback<R> {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
* {@link com.google.android.gms.common.api.GoogleApiClient} instance, which manages a single
3434
* instance per activity.
3535
*/
36+
@Deprecated
3637
public class GoogleApiClientTaskHelper {
3738

3839
private static final IdentityHashMap<Activity, GoogleApiClientTaskHelper> INSTANCES

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

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package com.firebase.ui.auth.util;
2+
3+
import android.os.Bundle;
4+
import android.support.annotation.NonNull;
5+
import android.support.annotation.Nullable;
6+
import android.support.v4.app.FragmentActivity;
7+
8+
import com.google.android.gms.common.ConnectionResult;
9+
import com.google.android.gms.common.api.GoogleApiClient;
10+
import com.google.android.gms.common.api.Result;
11+
import com.google.android.gms.common.api.ResultCallback;
12+
import com.google.android.gms.tasks.OnCompleteListener;
13+
import com.google.android.gms.tasks.OnSuccessListener;
14+
import com.google.android.gms.tasks.Task;
15+
import com.google.android.gms.tasks.TaskCompletionSource;
16+
17+
import java.net.ConnectException;
18+
import java.util.concurrent.atomic.AtomicInteger;
19+
20+
/**
21+
* A {@link Task} based wrapper to get a connect {@link GoogleApiClient}.
22+
*/
23+
public abstract class GoogleApiHelper implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
24+
private static final AtomicInteger SAFE_ID = new AtomicInteger(0);
25+
26+
protected GoogleApiClient mClient;
27+
private TaskCompletionSource<Bundle> mGoogleApiConnectionTask = new TaskCompletionSource<>();
28+
29+
protected GoogleApiHelper(FragmentActivity activity, GoogleApiClient.Builder builder) {
30+
builder.enableAutoManage(activity, getSafeAutoManageId(), this);
31+
builder.addConnectionCallbacks(this);
32+
mClient = builder.build();
33+
}
34+
35+
public static int getSafeAutoManageId() {
36+
return SAFE_ID.getAndIncrement();
37+
}
38+
39+
public Task<Bundle> getConnectedApiTask() {
40+
return mGoogleApiConnectionTask.getTask();
41+
}
42+
43+
@Override
44+
public void onConnected(@Nullable Bundle bundle) {
45+
mGoogleApiConnectionTask.setResult(bundle);
46+
}
47+
48+
@Override
49+
public void onConnectionSuspended(int i) {
50+
// Just wait
51+
}
52+
53+
@Override
54+
public void onConnectionFailed(@NonNull ConnectionResult result) {
55+
mGoogleApiConnectionTask.setException(new ConnectException(result.toString()));
56+
}
57+
58+
protected static final class TaskResultCaptor<R extends Result> implements ResultCallback<R> {
59+
private TaskCompletionSource<R> mSource;
60+
61+
public TaskResultCaptor(TaskCompletionSource<R> source) {
62+
mSource = source;
63+
}
64+
65+
@Override
66+
public void onResult(@NonNull R result) {
67+
mSource.setResult(result);
68+
}
69+
}
70+
71+
protected static class ExceptionForwarder<TResult> implements OnCompleteListener<TResult> {
72+
private TaskCompletionSource mSource;
73+
private OnSuccessListener<TResult> mListener;
74+
75+
public ExceptionForwarder(TaskCompletionSource source,
76+
OnSuccessListener<TResult> listener) {
77+
mSource = source;
78+
mListener = listener;
79+
}
80+
81+
@Override
82+
public void onComplete(@NonNull Task<TResult> task) {
83+
if (task.isSuccessful()) {
84+
mListener.onSuccess(task.getResult());
85+
} else {
86+
mSource.setException(task.getException());
87+
}
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)