14
14
15
15
package com .firebase .ui .auth ;
16
16
17
- import android .app . Activity ;
17
+ import android .content . Context ;
18
18
import android .content .Intent ;
19
19
import android .os .Bundle ;
20
20
import android .os .Parcel ;
23
23
import android .support .annotation .DrawableRes ;
24
24
import android .support .annotation .NonNull ;
25
25
import android .support .annotation .Nullable ;
26
+ import android .support .annotation .RestrictTo ;
26
27
import android .support .annotation .StringDef ;
27
28
import android .support .annotation .StyleRes ;
28
- import android .support . v4 . app . FragmentActivity ;
29
+ import android .text . TextUtils ;
29
30
30
31
import com .facebook .login .LoginManager ;
31
32
import com .firebase .ui .auth .provider .TwitterProvider ;
32
33
import com .firebase .ui .auth .ui .ExtraConstants ;
33
34
import com .firebase .ui .auth .ui .FlowParameters ;
34
35
import com .firebase .ui .auth .ui .idp .AuthMethodPickerActivity ;
35
- import com .firebase .ui .auth .util .GoogleSignInHelper ;
36
+ import com .firebase .ui .auth .util .GoogleApiUtils ;
36
37
import com .firebase .ui .auth .util .Preconditions ;
37
- import com .firebase .ui .auth .util .signincontainer .SmartLockBase ;
38
+ import com .firebase .ui .auth .util .signincontainer .SaveSmartLock ;
38
39
import com .google .android .gms .auth .api .credentials .Credential ;
39
- import com .google .android .gms .common .api .Status ;
40
+ import com .google .android .gms .auth .api .credentials .CredentialsClient ;
41
+ import com .google .android .gms .auth .api .signin .GoogleSignIn ;
42
+ import com .google .android .gms .auth .api .signin .GoogleSignInOptions ;
43
+ import com .google .android .gms .common .api .ApiException ;
44
+ import com .google .android .gms .common .api .CommonStatusCodes ;
40
45
import com .google .android .gms .tasks .Continuation ;
41
46
import com .google .android .gms .tasks .Task ;
42
47
import com .google .android .gms .tasks .Tasks ;
43
48
import com .google .firebase .FirebaseApp ;
44
49
import com .google .firebase .auth .EmailAuthProvider ;
45
50
import com .google .firebase .auth .FacebookAuthProvider ;
46
51
import com .google .firebase .auth .FirebaseAuth ;
52
+ import com .google .firebase .auth .FirebaseAuthInvalidUserException ;
53
+ import com .google .firebase .auth .FirebaseAuthProvider ;
47
54
import com .google .firebase .auth .FirebaseUser ;
48
55
import com .google .firebase .auth .GoogleAuthProvider ;
49
56
import com .google .firebase .auth .PhoneAuthProvider ;
50
57
import com .google .firebase .auth .TwitterAuthProvider ;
58
+ import com .google .firebase .auth .UserInfo ;
51
59
52
60
import java .lang .annotation .Retention ;
53
61
import java .lang .annotation .RetentionPolicy ;
@@ -138,6 +146,16 @@ public class AuthUI {
138
146
PHONE_VERIFICATION_PROVIDER
139
147
)));
140
148
149
+ /**
150
+ * The set of social authentication providers supported in Firebase Auth UI.
151
+ */
152
+ @ RestrictTo (RestrictTo .Scope .LIBRARY_GROUP )
153
+ public static final Set <String > SOCIAL_PROVIDERS =
154
+ Collections .unmodifiableSet (new HashSet <>(Arrays .asList (
155
+ GoogleAuthProvider .PROVIDER_ID ,
156
+ FacebookAuthProvider .PROVIDER_ID ,
157
+ TwitterAuthProvider .PROVIDER_ID )));
158
+
141
159
private static final IdentityHashMap <FirebaseApp , AuthUI > INSTANCES = new IdentityHashMap <>();
142
160
143
161
private final FirebaseApp mApp ;
@@ -189,40 +207,17 @@ public static int getDefaultTheme() {
189
207
/**
190
208
* Signs the current user out, if one is signed in.
191
209
*
192
- * @param activity the activity requesting the user be signed out
210
+ * @param context the context requesting the user be signed out
193
211
* @return A task which, upon completion, signals that the user has been signed out ({@link
194
212
* Task#isSuccessful()}, or that the sign-out attempt failed unexpectedly !{@link
195
213
* Task#isSuccessful()}).
196
214
*/
197
215
@ NonNull
198
- public Task <Void > signOut (@ NonNull FragmentActivity activity ) {
199
- // Get Credentials Helper
200
- GoogleSignInHelper signInHelper = GoogleSignInHelper .getInstance (activity );
201
-
202
- // Firebase Sign out
216
+ public Task <Void > signOut (@ NonNull Context context ) {
203
217
mAuth .signOut ();
204
-
205
- // Disable credentials auto sign-in
206
- Task <Status > disableCredentialsTask = signInHelper .disableAutoSignIn ();
207
-
208
- // Google sign out
209
- Task <Status > signOutTask = signInHelper .signOut ();
210
-
211
- // Facebook sign out
212
- try {
213
- LoginManager .getInstance ().logOut ();
214
- } catch (NoClassDefFoundError e ) {
215
- // do nothing
216
- }
217
-
218
- // Twitter sign out
219
- try {
220
- TwitterProvider .signOut (activity );
221
- } catch (NoClassDefFoundError e ) {
222
- // do nothing
223
- }
224
- // Wait for all tasks to complete
225
- return Tasks .whenAll (disableCredentialsTask , signOutTask );
218
+ return Tasks .whenAll (
219
+ signOutIdps (context ),
220
+ GoogleApiUtils .getCredentialsClient (context ).disableAutoSignIn ());
226
221
}
227
222
228
223
/**
@@ -231,48 +226,94 @@ public Task<Void> signOut(@NonNull FragmentActivity activity) {
231
226
* fails if the Firebase Auth deletion fails. Credentials deletion failures are handled
232
227
* silently.
233
228
*
234
- * @param activity the calling {@link Activity }.
229
+ * @param context the calling {@link Context }.
235
230
*/
236
231
@ NonNull
237
- public Task <Void > delete (@ NonNull FragmentActivity activity ) {
238
- // Initialize SmartLock helper
239
- GoogleSignInHelper signInHelper = GoogleSignInHelper .getInstance (activity );
240
-
241
- FirebaseUser firebaseUser = FirebaseAuth .getInstance ().getCurrentUser ();
242
- if (firebaseUser == null ) {
243
- // If the current user is null, return a failed task immediately
244
- return Tasks .forException (new Exception ("No currently signed in user." ));
232
+ public Task <Void > delete (@ NonNull Context context ) {
233
+ final FirebaseUser currentUser = FirebaseAuth .getInstance ().getCurrentUser ();
234
+ if (currentUser == null ) {
235
+ return Tasks .forException (new FirebaseAuthInvalidUserException (
236
+ String .valueOf (CommonStatusCodes .SIGN_IN_REQUIRED ),
237
+ "No currently signed in user." ));
245
238
}
246
239
247
- // Delete the Firebase user
248
- Task < Void > deleteUserTask = firebaseUser . delete ( );
240
+ final List < Credential > credentials = getCredentialsFromFirebaseUser ( currentUser );
241
+ final CredentialsClient client = GoogleApiUtils . getCredentialsClient ( context );
249
242
250
- // Get all SmartLock credentials associated with the user
251
- List <Credential > credentials = SmartLockBase .credentialsFromFirebaseUser (firebaseUser );
243
+ // Ensure the order in which tasks are executed properly destructures the user.
244
+ return signOutIdps (context ).continueWithTask (new Continuation <Void , Task <Void >>() {
245
+ @ Override
246
+ public Task <Void > then (@ NonNull Task <Void > task ) {
247
+ task .getResult (); // Propagate exception if there was one
248
+ return currentUser .delete ();
249
+ }
250
+ }).continueWithTask (new Continuation <Void , Task <Void >>() {
251
+ @ Override
252
+ public Task <Void > then (@ NonNull Task <Void > task ) {
253
+ task .getResult (); // Propagate exception if there was one
252
254
253
- // For each Credential in the list, create a task to delete it.
254
- List <Task <?>> credentialTasks = new ArrayList <>();
255
- for (Credential credential : credentials ) {
256
- credentialTasks .add (signInHelper .delete (credential ));
255
+ List <Task <?>> credentialTasks = new ArrayList <>();
256
+ for (Credential credential : credentials ) {
257
+ credentialTasks .add (client .delete (credential ));
258
+ }
259
+ return Tasks .whenAll (credentialTasks )
260
+ .continueWithTask (new Continuation <Void , Task <Void >>() {
261
+ @ Override
262
+ public Task <Void > then (@ NonNull Task <Void > task ) {
263
+ Exception e = task .getException ();
264
+ Throwable t = e == null ? null : e .getCause ();
265
+ if (!(t instanceof ApiException )
266
+ || ((ApiException ) t ).getStatusCode () != CommonStatusCodes .CANCELED ) {
267
+ // Only propagate the exception if it isn't an invalid account
268
+ // one. This can occur if we failed to save the credential or it
269
+ // was deleted elsewhere. However, a lack of stored credential
270
+ // doesn't mean fully deleting the user failed.
271
+ task .getResult ();
272
+ }
273
+
274
+ return Tasks .forResult (null );
275
+ }
276
+ });
277
+ }
278
+ });
279
+ }
280
+
281
+ private Task <Void > signOutIdps (@ NonNull Context context ) {
282
+ try {
283
+ LoginManager .getInstance ().logOut ();
284
+ TwitterProvider .signOut (context );
285
+ } catch (NoClassDefFoundError e ) {
286
+ // Do nothing: this is perfectly fine if the dev doesn't include Facebook/Twitter
287
+ // support
257
288
}
258
289
259
- // Create a combined task that will succeed when all credential delete operations
260
- // have completed (even if they fail).
261
- final Task <Void > combinedCredentialTask = Tasks .whenAll (credentialTasks );
290
+ return GoogleSignIn .getClient (context , GoogleSignInOptions .DEFAULT_SIGN_IN ).signOut ();
291
+ }
262
292
263
- // Chain the Firebase Auth delete task with the combined Credentials task
264
- // and return.
265
- return deleteUserTask . continueWithTask ( new Continuation < Void , Task < Void >>() {
266
- @ Override
267
- public Task < Void > then (@ NonNull Task < Void > task ) throws Exception {
268
- // Call getResult() to propagate failure by throwing an exception
269
- // if there was one.
270
- task . getResult ( Exception . class );
293
+ /**
294
+ * Make a list of {@link Credential} from a FirebaseUser. Useful for deleting Credentials, not
295
+ * for saving since we don't have access to the password.
296
+ */
297
+ private static List < Credential > getCredentialsFromFirebaseUser (@ NonNull FirebaseUser user ) {
298
+ if ( TextUtils . isEmpty ( user . getEmail ()) && TextUtils . isEmpty ( user . getPhoneNumber ())) {
299
+ return Collections . emptyList ();
300
+ }
271
301
272
- // Return the combined credential task
273
- return combinedCredentialTask ;
302
+ List <Credential > credentials = new ArrayList <>();
303
+ for (UserInfo userInfo : user .getProviderData ()) {
304
+ if (FirebaseAuthProvider .PROVIDER_ID .equals (userInfo .getProviderId ())) {
305
+ continue ;
274
306
}
275
- });
307
+
308
+ String type = SaveSmartLock .providerIdToAccountType (userInfo .getProviderId ());
309
+
310
+ credentials .add (new Credential .Builder (
311
+ user .getEmail () == null ? user .getPhoneNumber () : user .getEmail ())
312
+ .setAccountType (type )
313
+ .build ());
314
+ }
315
+
316
+ return credentials ;
276
317
}
277
318
278
319
/**
0 commit comments