From 804f4e75de112a35adfdb7c005f4a7495f472b78 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Wed, 26 Jul 2017 10:23:07 -0700 Subject: [PATCH 01/11] Ouch! Fix terrible db listener leak (#828) --- .../CachingObservableSnapshotArray.java | 8 +++++ .../firebase/ui/database/FirebaseArray.java | 31 +++++------------ .../ui/database/FirebaseIndexArray.java | 34 ++++++++----------- .../ui/database/ObservableSnapshotArray.java | 27 ++++++++++++--- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/database/src/main/java/com/firebase/ui/database/CachingObservableSnapshotArray.java b/database/src/main/java/com/firebase/ui/database/CachingObservableSnapshotArray.java index 1a564f65e..9f376e0fa 100644 --- a/database/src/main/java/com/firebase/ui/database/CachingObservableSnapshotArray.java +++ b/database/src/main/java/com/firebase/ui/database/CachingObservableSnapshotArray.java @@ -43,6 +43,14 @@ public T getObject(int index) { } } + @Override + protected void onDestroy() { + super.onDestroy(); + clearData(); + } + + /** @deprecated use {@link ObservableSnapshotArray#onDestroy()} instead */ + @Deprecated protected void clearData() { getSnapshots().clear(); mObjectCache.clear(); diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseArray.java b/database/src/main/java/com/firebase/ui/database/FirebaseArray.java index e26620372..f57f01706 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseArray.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseArray.java @@ -14,8 +14,6 @@ package com.firebase.ui.database; -import android.support.annotation.NonNull; - import com.google.firebase.database.ChildEventListener; import com.google.firebase.database.DataSnapshot; import com.google.firebase.database.DatabaseError; @@ -66,30 +64,17 @@ protected List getSnapshots() { } @Override - public ChangeEventListener addChangeEventListener(@NonNull ChangeEventListener listener) { - boolean wasListening = isListening(); - super.addChangeEventListener(listener); - - // Only start listening when the first listener is added - if (!wasListening) { - mQuery.addChildEventListener(this); - mQuery.addValueEventListener(this); - } - - return listener; + protected void onCreate() { + super.onCreate(); + mQuery.addChildEventListener(this); + mQuery.addValueEventListener(this); } @Override - public void removeChangeEventListener(@NonNull ChangeEventListener listener) { - super.removeChangeEventListener(listener); - - // Clear data when all listeners are removed - if (!isListening()) { - mQuery.removeEventListener((ValueEventListener) this); - mQuery.removeEventListener((ChildEventListener) this); - - clearData(); - } + protected void onDestroy() { + super.onDestroy(); + mQuery.removeEventListener((ValueEventListener) this); + mQuery.removeEventListener((ChildEventListener) this); } @Override diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java b/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java index 008470303..8a104c488 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java @@ -14,7 +14,6 @@ package com.firebase.ui.database; -import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import android.util.Log; @@ -85,10 +84,25 @@ public String parseSnapshot(DataSnapshot snapshot) { return snapshot.getKey(); } }); + } + @Override + protected void onCreate() { + super.onCreate(); mKeySnapshots.addChangeEventListener(this); } + @Override + protected void onDestroy() { + super.onDestroy(); + mKeySnapshots.removeChangeEventListener(this); + + for (DatabaseReference ref : mRefs.keySet()) { + ref.removeEventListener(mRefs.get(ref)); + } + mRefs.clear(); + } + @Override public void onChildChanged(EventType type, DataSnapshot snapshot, int index, int oldIndex) { switch (type) { @@ -121,29 +135,11 @@ public void onCancelled(DatabaseError error) { Log.e(TAG, "A fatal error occurred retrieving the necessary keys to populate your adapter."); } - @Override - public void removeChangeEventListener(@NonNull ChangeEventListener listener) { - super.removeChangeEventListener(listener); - if (!isListening()) { - for (DatabaseReference ref : mRefs.keySet()) { - ref.removeEventListener(mRefs.get(ref)); - } - - clearData(); - } - } - @Override protected List getSnapshots() { return mDataSnapshots; } - @Override - protected void clearData() { - super.clearData(); - mRefs.clear(); - } - private int getIndexForKey(String key) { int dataCount = size(); int index = 0; diff --git a/database/src/main/java/com/firebase/ui/database/ObservableSnapshotArray.java b/database/src/main/java/com/firebase/ui/database/ObservableSnapshotArray.java index 446b4e2cc..2c7b5e4f0 100644 --- a/database/src/main/java/com/firebase/ui/database/ObservableSnapshotArray.java +++ b/database/src/main/java/com/firebase/ui/database/ObservableSnapshotArray.java @@ -51,6 +51,8 @@ public ObservableSnapshotArray(@NonNull SnapshotParser parser) { public ChangeEventListener addChangeEventListener(@NonNull ChangeEventListener listener) { Preconditions.checkNotNull(listener); + boolean wasListening = isListening(); + mListeners.add(listener); for (int i = 0; i < size(); i++) { listener.onChildChanged(ChangeEventListener.EventType.ADDED, get(i), i, -1); @@ -59,9 +61,18 @@ public ChangeEventListener addChangeEventListener(@NonNull ChangeEventListener l listener.onDataChanged(); } + if (!wasListening) { onCreate(); } + return listener; } + /** + * Called when the {@link ObservableSnapshotArray} is active and should start listening to the + * Firebase database. + */ + @CallSuper + protected void onCreate() {} + /** * Detach a {@link com.google.firebase.database.ChildEventListener} from this array. */ @@ -69,10 +80,18 @@ public ChangeEventListener addChangeEventListener(@NonNull ChangeEventListener l public void removeChangeEventListener(@NonNull ChangeEventListener listener) { mListeners.remove(listener); - // Reset mHasDataChanged if there are no more listeners - if (!isListening()) { - mHasDataChanged = false; - } + if (!isListening()) { onDestroy(); } + } + + /** + * Called when the {@link ObservableSnapshotArray} is inactive and should stop listening to the + * Firebase database. + *

+ * All data should also be cleared here. + */ + @CallSuper + protected void onDestroy() { + mHasDataChanged = false; } /** From 340035c186950e91afd4a0b45386dc330938ab11 Mon Sep 17 00:00:00 2001 From: Cinfwat Dogak Date: Mon, 31 Jul 2017 15:59:16 +0100 Subject: [PATCH 02/11] Update styles.xml (#834) --- auth/src/main/res/layout/fui_register_email_layout.xml | 7 +++---- auth/src/main/res/values/styles.xml | 8 ++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/auth/src/main/res/layout/fui_register_email_layout.xml b/auth/src/main/res/layout/fui_register_email_layout.xml index fa7c2abde..2c0250848 100644 --- a/auth/src/main/res/layout/fui_register_email_layout.xml +++ b/auth/src/main/res/layout/fui_register_email_layout.xml @@ -35,18 +35,17 @@ + android:layout_height="wrap_content" /> diff --git a/auth/src/main/res/values/styles.xml b/auth/src/main/res/values/styles.xml index cfecba6c5..ce3f85e89 100644 --- a/auth/src/main/res/values/styles.xml +++ b/auth/src/main/res/values/styles.xml @@ -125,6 +125,10 @@ textEmailAddress + + + + From 280ad5c3bb0ccfd6be53e2ef2019e95f37f97320 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 31 Jul 2017 09:05:23 -0700 Subject: [PATCH 03/11] Fix email -> Google smart lock credential not being deleted when user selects it (#837) --- .../ui/auth/util/signincontainer/SignInDelegate.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java index c997c8998..df2ace11e 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/signincontainer/SignInDelegate.java @@ -16,10 +16,10 @@ import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; import com.firebase.ui.auth.ResultCodes; +import com.firebase.ui.auth.User; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.TaskFailureLogger; -import com.firebase.ui.auth.User; import com.firebase.ui.auth.ui.email.RegisterEmailActivity; import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity; import com.firebase.ui.auth.ui.phone.PhoneVerificationActivity; @@ -40,6 +40,7 @@ import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.EmailAuthProvider; import com.google.firebase.auth.FacebookAuthProvider; +import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; import com.google.firebase.auth.FirebaseAuthInvalidUserException; import com.google.firebase.auth.GoogleAuthProvider; import com.google.firebase.auth.PhoneAuthProvider; @@ -289,7 +290,8 @@ public void onSuccess(AuthResult authResult) { .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - if (e instanceof FirebaseAuthInvalidUserException) { + if (e instanceof FirebaseAuthInvalidUserException + || e instanceof FirebaseAuthInvalidCredentialsException) { // In this case the credential saved in SmartLock was not // a valid credential, we should delete it from SmartLock // before continuing. From 382ee817f9cfcb637440a85c3dbb2e2bb40db3ed Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 31 Jul 2017 09:06:48 -0700 Subject: [PATCH 04/11] Fix email -> Google account linking regression (#836) --- .../ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java | 9 ++++++--- .../ui/auth/ui/email/CheckEmailFragment.java | 3 +-- .../ui/auth/ui/email/RegisterEmailActivity.java | 12 ++++-------- .../ui/auth/ui/email/RegisterEmailFragment.java | 4 +--- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java index eb44b9f8b..38052db5a 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/accountlink/WelcomeBackIdpPrompt.java @@ -18,6 +18,7 @@ import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import android.support.annotation.RestrictTo; import android.util.Log; import android.view.View; @@ -64,7 +65,7 @@ public static Intent createIntent( Context context, FlowParameters flowParams, User existingUser, - IdpResponse newUserResponse) { + @Nullable IdpResponse newUserResponse) { return HelperActivityBase.createBaseIntent(context, WelcomeBackIdpPrompt.class, flowParams) .putExtra(ExtraConstants.EXTRA_USER, existingUser) .putExtra(ExtraConstants.EXTRA_IDP_RESPONSE, newUserResponse); @@ -75,8 +76,10 @@ protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fui_welcome_back_idp_prompt_layout); - IdpResponse newUserIdpResponse = IdpResponse.fromResultIntent(getIntent()); - mPrevCredential = ProviderUtils.getAuthCredential(newUserIdpResponse); + IdpResponse newUserResponse = IdpResponse.fromResultIntent(getIntent()); + if (newUserResponse != null) { + mPrevCredential = ProviderUtils.getAuthCredential(newUserResponse); + } User oldUser = User.getUser(getIntent()); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java index 552145c40..a67ba939e 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java @@ -206,8 +206,7 @@ public void onSuccess(String provider) { mListener.onExistingEmailUser( new User.Builder(EmailAuthProvider.PROVIDER_ID, email).build()); } else { - mListener.onExistingIdpUser( - new User.Builder(EmailAuthProvider.PROVIDER_ID, email).build()); + mListener.onExistingIdpUser(new User.Builder(provider, email).build()); } } }) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java index c1ec3fc52..b80df0db0 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailActivity.java @@ -23,11 +23,11 @@ import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; +import com.firebase.ui.auth.User; import com.firebase.ui.auth.ui.AppCompatBase; import com.firebase.ui.auth.ui.ExtraConstants; import com.firebase.ui.auth.ui.FlowParameters; import com.firebase.ui.auth.ui.HelperActivityBase; -import com.firebase.ui.auth.User; import com.firebase.ui.auth.ui.accountlink.WelcomeBackIdpPrompt; import com.firebase.ui.auth.ui.accountlink.WelcomeBackPasswordPrompt; @@ -104,13 +104,9 @@ public void onExistingEmailUser(User user) { @Override public void onExistingIdpUser(User user) { // Existing social user, direct them to sign in using their chosen provider. - Intent intent = WelcomeBackIdpPrompt.createIntent( - this, - getFlowParams(), - user, - new IdpResponse.Builder(user).build()); - - startActivityForResult(intent, RC_WELCOME_BACK_IDP); + startActivityForResult( + WelcomeBackIdpPrompt.createIntent(this, getFlowParams(), user, null), + RC_WELCOME_BACK_IDP); setSlideAnimation(); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java index 728c18091..9882818e3 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java @@ -302,9 +302,7 @@ public void onSuccess(String provider) { getFlowParams(), new User.Builder(provider, email) .build(), - new IdpResponse.Builder(new User.Builder( - EmailAuthProvider.PROVIDER_ID, - email).build()).build()), + null), RegisterEmailActivity.RC_WELCOME_BACK_IDP); } } From e16784d9917e281196e47575d10b30c77f6f76c8 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Mon, 31 Jul 2017 15:24:00 -0700 Subject: [PATCH 05/11] Add support for Android Architecture Components (#827) --- .../uidemo/database/ChatActivity.java | 36 ++++++------ .../uidemo/database/ChatIndexActivity.java | 3 +- app/src/main/res/layout/activity_chat.xml | 2 +- database/build.gradle | 4 ++ .../firebase/ui/database/FirebaseAdapter.java | 3 +- .../ui/database/FirebaseIndexListAdapter.java | 43 +++++++++++--- .../FirebaseIndexRecyclerAdapter.java | 46 +++++++++++++-- .../ui/database/FirebaseListAdapter.java | 54 +++++++++++++++++- .../ui/database/FirebaseRecyclerAdapter.java | 57 ++++++++++++++++++- 9 files changed, 212 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java b/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java index 134076e71..6429b0a2c 100644 --- a/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java +++ b/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java @@ -14,6 +14,8 @@ package com.firebase.uidemo.database; +import android.arch.lifecycle.LifecycleRegistry; +import android.arch.lifecycle.LifecycleRegistryOwner; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v7.app.AppCompatActivity; @@ -37,9 +39,13 @@ import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.Query; -public class ChatActivity extends AppCompatActivity implements FirebaseAuth.AuthStateListener, View.OnClickListener { +public class ChatActivity extends AppCompatActivity + implements FirebaseAuth.AuthStateListener, View.OnClickListener, LifecycleRegistryOwner { private static final String TAG = "RecyclerViewDemo"; + // TODO remove once arch components are merged into support lib + private final LifecycleRegistry mRegistry = new LifecycleRegistry(this); + private FirebaseAuth mAuth; protected DatabaseReference mChatRef; private Button mSendButton; @@ -47,7 +53,7 @@ public class ChatActivity extends AppCompatActivity implements FirebaseAuth.Auth private RecyclerView mMessages; private LinearLayoutManager mManager; - protected FirebaseRecyclerAdapter mAdapter; + private FirebaseRecyclerAdapter mAdapter; protected TextView mEmptyListMessage; @Override @@ -70,8 +76,10 @@ protected void onCreate(Bundle savedInstanceState) { mManager.setReverseLayout(false); mMessages = (RecyclerView) findViewById(R.id.messagesList); - mMessages.setHasFixedSize(false); + mMessages.setHasFixedSize(true); mMessages.setLayoutManager(mManager); + + if (isSignedIn()) { attachRecyclerViewAdapter(); } } @Override @@ -81,19 +89,7 @@ public void onStart() { // Default Database rules do not allow unauthenticated reads, so we need to // sign in before attaching the RecyclerView adapter otherwise the Adapter will // not be able to read any data from the Database. - if (isSignedIn()) { - attachRecyclerViewAdapter(); - } else { - signInAnonymously(); - } - } - - @Override - public void onStop() { - super.onStop(); - if (mAdapter != null) { - mAdapter.cleanup(); - } + if (!isSignedIn()) { signInAnonymously(); } } @Override @@ -147,7 +143,8 @@ protected FirebaseRecyclerAdapter getAdapter() { Chat.class, R.layout.message, ChatHolder.class, - lastFifty) { + lastFifty, + this) { @Override public void populateViewHolder(ChatHolder holder, Chat chat, int position) { holder.bind(chat); @@ -182,4 +179,9 @@ private void updateUI() { mSendButton.setEnabled(isSignedIn()); mMessageEdit.setEnabled(isSignedIn()); } + + @Override + public LifecycleRegistry getLifecycle() { + return mRegistry; + } } diff --git a/app/src/main/java/com/firebase/uidemo/database/ChatIndexActivity.java b/app/src/main/java/com/firebase/uidemo/database/ChatIndexActivity.java index 2cad956ac..dbe9a68d6 100644 --- a/app/src/main/java/com/firebase/uidemo/database/ChatIndexActivity.java +++ b/app/src/main/java/com/firebase/uidemo/database/ChatIndexActivity.java @@ -39,7 +39,8 @@ protected FirebaseRecyclerAdapter getAdapter() { R.layout.message, ChatHolder.class, mChatIndicesRef.limitToLast(50), - mChatRef) { + mChatRef, + this) { @Override public void populateViewHolder(ChatHolder holder, Chat chat, int position) { holder.bind(chat); diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml index 326cfde18..7c0171e08 100644 --- a/app/src/main/res/layout/activity_chat.xml +++ b/app/src/main/res/layout/activity_chat.xml @@ -16,7 +16,7 @@ extends ChangeEventListener { +interface FirebaseAdapter extends ChangeEventListener, LifecycleObserver { /** * If you need to do some setup before the adapter starts listening for change events in the * database, do so it here and then call {@code super.startListening()}. diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseIndexListAdapter.java b/database/src/main/java/com/firebase/ui/database/FirebaseIndexListAdapter.java index 83019a50b..e678b3e77 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseIndexListAdapter.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseIndexListAdapter.java @@ -1,6 +1,7 @@ package com.firebase.ui.database; -import android.app.Activity; +import android.arch.lifecycle.LifecycleOwner; +import android.content.Context; import android.support.annotation.LayoutRes; import android.widget.ListView; @@ -18,25 +19,51 @@ public abstract class FirebaseIndexListAdapter extends FirebaseListAdapter * {@link DatabaseReference}. * @param dataRef The Firebase location to watch for data changes. Each key key found at {@code * keyQuery}'s location represents a list item in the {@link ListView}. - * @see FirebaseIndexListAdapter#FirebaseIndexListAdapter(Activity, SnapshotParser, int, Query, - * DatabaseReference) + * @see FirebaseListAdapter#FirebaseListAdapter(Context, ObservableSnapshotArray, int, + * LifecycleOwner) */ - public FirebaseIndexListAdapter(Activity activity, + public FirebaseIndexListAdapter(Context context, + SnapshotParser parser, + @LayoutRes int modelLayout, + Query keyQuery, + DatabaseReference dataRef, + LifecycleOwner owner) { + super(context, new FirebaseIndexArray<>(keyQuery, dataRef, parser), modelLayout, owner); + } + + /** + * @see #FirebaseIndexListAdapter(Context, SnapshotParser, int, Query, DatabaseReference, + * LifecycleOwner) + */ + public FirebaseIndexListAdapter(Context context, SnapshotParser parser, @LayoutRes int modelLayout, Query keyQuery, DatabaseReference dataRef) { - super(activity, new FirebaseIndexArray<>(keyQuery, dataRef, parser), modelLayout); + super(context, new FirebaseIndexArray<>(keyQuery, dataRef, parser), modelLayout); + } + + /** + * @see #FirebaseIndexListAdapter(Context, SnapshotParser, int, Query, DatabaseReference, + * LifecycleOwner) + */ + public FirebaseIndexListAdapter(Context context, + Class modelClass, + @LayoutRes int modelLayout, + Query keyQuery, + DatabaseReference dataRef, + LifecycleOwner owner) { + this(context, new ClassSnapshotParser<>(modelClass), modelLayout, keyQuery, dataRef, owner); } /** - * @see #FirebaseIndexListAdapter(Activity, SnapshotParser, int, Query, DatabaseReference) + * @see #FirebaseIndexListAdapter(Context, SnapshotParser, int, Query, DatabaseReference) */ - public FirebaseIndexListAdapter(Activity activity, + public FirebaseIndexListAdapter(Context context, Class modelClass, @LayoutRes int modelLayout, Query keyQuery, DatabaseReference dataRef) { - this(activity, new ClassSnapshotParser<>(modelClass), modelLayout, keyQuery, dataRef); + this(context, new ClassSnapshotParser<>(modelClass), modelLayout, keyQuery, dataRef); } } diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseIndexRecyclerAdapter.java b/database/src/main/java/com/firebase/ui/database/FirebaseIndexRecyclerAdapter.java index e10fa4a23..268ce7563 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseIndexRecyclerAdapter.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseIndexRecyclerAdapter.java @@ -1,5 +1,6 @@ package com.firebase.ui.database; +import android.arch.lifecycle.LifecycleOwner; import android.support.annotation.LayoutRes; import android.support.v7.widget.RecyclerView; @@ -18,7 +19,24 @@ public abstract class FirebaseIndexRecyclerAdapter * @param dataRef The Firebase location to watch for data changes. Each key key found at {@code * keyQuery}'s location represents a list item in the {@link RecyclerView}. - * @see FirebaseRecyclerAdapter#FirebaseRecyclerAdapter(ObservableSnapshotArray, int, Class) + * @see FirebaseRecyclerAdapter#FirebaseRecyclerAdapter(ObservableSnapshotArray, int, Class, + * LifecycleOwner) + */ + public FirebaseIndexRecyclerAdapter(SnapshotParser parser, + @LayoutRes int modelLayout, + Class viewHolderClass, + Query keyQuery, + DatabaseReference dataRef, + LifecycleOwner owner) { + super(new FirebaseIndexArray<>(keyQuery, dataRef, parser), + modelLayout, + viewHolderClass, + owner); + } + + /** + * @see #FirebaseIndexRecyclerAdapter(SnapshotParser, int, Class, Query, DatabaseReference, + * LifecycleOwner) */ public FirebaseIndexRecyclerAdapter(SnapshotParser parser, @LayoutRes int modelLayout, @@ -28,6 +46,24 @@ public FirebaseIndexRecyclerAdapter(SnapshotParser parser, super(new FirebaseIndexArray<>(keyQuery, dataRef, parser), modelLayout, viewHolderClass); } + /** + * @see #FirebaseIndexRecyclerAdapter(SnapshotParser, int, Class, Query, DatabaseReference, + * LifecycleOwner) + */ + public FirebaseIndexRecyclerAdapter(Class modelClass, + @LayoutRes int modelLayout, + Class viewHolderClass, + Query keyQuery, + DatabaseReference dataRef, + LifecycleOwner owner) { + this(new ClassSnapshotParser<>(modelClass), + modelLayout, + viewHolderClass, + keyQuery, + dataRef, + owner); + } + /** * @see #FirebaseIndexRecyclerAdapter(SnapshotParser, int, Class, Query, DatabaseReference) */ @@ -37,9 +73,9 @@ public FirebaseIndexRecyclerAdapter(Class modelClass, Query keyQuery, DatabaseReference dataRef) { this(new ClassSnapshotParser<>(modelClass), - modelLayout, - viewHolderClass, - keyQuery, - dataRef); + modelLayout, + viewHolderClass, + keyQuery, + dataRef); } } diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseListAdapter.java b/database/src/main/java/com/firebase/ui/database/FirebaseListAdapter.java index b62abbb32..a4e46a57b 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseListAdapter.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseListAdapter.java @@ -1,8 +1,12 @@ package com.firebase.ui.database; import android.app.Activity; +import android.arch.lifecycle.Lifecycle; +import android.arch.lifecycle.LifecycleOwner; +import android.arch.lifecycle.OnLifecycleEvent; import android.content.Context; import android.support.annotation.LayoutRes; +import android.support.v4.app.FragmentActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -39,14 +43,28 @@ public abstract class FirebaseListAdapter extends BaseAdapter implements Fire * @param modelLayout This is the layout used to represent a single list item. You will be * responsible for populating an instance of the corresponding view with the * data from an instance of modelClass. + * @param owner the lifecycle owner used to automatically listen and cleanup after {@link + * FragmentActivity#onStart()} and {@link FragmentActivity#onStop()} events + * reflectively. */ public FirebaseListAdapter(Context context, ObservableSnapshotArray snapshots, - @LayoutRes int modelLayout) { + @LayoutRes int modelLayout, + LifecycleOwner owner) { mContext = context; mSnapshots = snapshots; mLayout = modelLayout; + if (owner != null) { owner.getLifecycle().addObserver(this); } + } + + /** + * @see #FirebaseListAdapter(Context, ObservableSnapshotArray, int, LifecycleOwner) + */ + public FirebaseListAdapter(Context context, + ObservableSnapshotArray snapshots, + @LayoutRes int modelLayout) { + this(context, snapshots, modelLayout, null); startListening(); } @@ -65,6 +83,18 @@ public FirebaseListAdapter(Context context, this(context, new FirebaseArray<>(query, parser), modelLayout); } + /** + * @see #FirebaseListAdapter(Context, SnapshotParser, int, Query) + * @see #FirebaseListAdapter(Context, ObservableSnapshotArray, int, LifecycleOwner) + */ + public FirebaseListAdapter(Context context, + SnapshotParser parser, + @LayoutRes int modelLayout, + Query query, + LifecycleOwner owner) { + this(context, new FirebaseArray<>(query, parser), modelLayout, owner); + } + /** * @see #FirebaseListAdapter(Context, SnapshotParser, int, Query) */ @@ -75,7 +105,19 @@ public FirebaseListAdapter(Context context, this(context, new ClassSnapshotParser<>(modelClass), modelLayout, query); } + /** + * @see #FirebaseListAdapter(Context, SnapshotParser, int, Query, LifecycleOwner) + */ + public FirebaseListAdapter(Context context, + Class modelClass, + @LayoutRes int modelLayout, + Query query, + LifecycleOwner owner) { + this(context, new ClassSnapshotParser<>(modelClass), modelLayout, query, owner); + } + @Override + @OnLifecycleEvent(Lifecycle.Event.ON_START) public void startListening() { if (!mSnapshots.isListening(this)) { mSnapshots.addChangeEventListener(this); @@ -87,6 +129,16 @@ public void cleanup() { mSnapshots.removeChangeEventListener(this); } + @SuppressWarnings("unused") + @OnLifecycleEvent(Lifecycle.Event.ON_ANY) + void cleanup(LifecycleOwner source, Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_STOP) { + cleanup(); + } else if (event == Lifecycle.Event.ON_DESTROY) { + source.getLifecycle().removeObserver(this); + } + } + @Override public void onChildChanged(ChangeEventListener.EventType type, DataSnapshot snapshot, diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java b/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java index 116cec0b6..da5e68ba5 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java @@ -1,6 +1,10 @@ package com.firebase.ui.database; +import android.arch.lifecycle.Lifecycle; +import android.arch.lifecycle.LifecycleOwner; +import android.arch.lifecycle.OnLifecycleEvent; import android.support.annotation.LayoutRes; +import android.support.v4.app.FragmentActivity; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; @@ -42,14 +46,28 @@ public abstract class FirebaseRecyclerAdapter snapshots, @LayoutRes int modelLayout, - Class viewHolderClass) { + Class viewHolderClass, + LifecycleOwner owner) { mSnapshots = snapshots; mViewHolderClass = viewHolderClass; mModelLayout = modelLayout; + if (owner != null) { owner.getLifecycle().addObserver(this); } + } + + /** + * @see #FirebaseRecyclerAdapter(ObservableSnapshotArray, int, Class, LifecycleOwner) + */ + public FirebaseRecyclerAdapter(ObservableSnapshotArray snapshots, + @LayoutRes int modelLayout, + Class viewHolderClass) { + this(snapshots, modelLayout, viewHolderClass, null); startListening(); } @@ -68,6 +86,18 @@ public FirebaseRecyclerAdapter(SnapshotParser parser, this(new FirebaseArray<>(query, parser), modelLayout, viewHolderClass); } + /** + * @see #FirebaseRecyclerAdapter(SnapshotParser, int, Class, Query) + * @see #FirebaseRecyclerAdapter(ObservableSnapshotArray, int, Class, LifecycleOwner) + */ + public FirebaseRecyclerAdapter(SnapshotParser parser, + @LayoutRes int modelLayout, + Class viewHolderClass, + Query query, + LifecycleOwner owner) { + this(new FirebaseArray<>(query, parser), modelLayout, viewHolderClass, owner); + } + /** * @see #FirebaseRecyclerAdapter(SnapshotParser, int, Class, Query) */ @@ -78,7 +108,19 @@ public FirebaseRecyclerAdapter(Class modelClass, this(new ClassSnapshotParser<>(modelClass), modelLayout, viewHolderClass, query); } + /** + * @see #FirebaseRecyclerAdapter(SnapshotParser, int, Class, Query, LifecycleOwner) + */ + public FirebaseRecyclerAdapter(Class modelClass, + @LayoutRes int modelLayout, + Class viewHolderClass, + Query query, + LifecycleOwner owner) { + this(new ClassSnapshotParser<>(modelClass), modelLayout, viewHolderClass, query, owner); + } + @Override + @OnLifecycleEvent(Lifecycle.Event.ON_START) public void startListening() { if (!mSnapshots.isListening(this)) { mSnapshots.addChangeEventListener(this); @@ -88,6 +130,17 @@ public void startListening() { @Override public void cleanup() { mSnapshots.removeChangeEventListener(this); + notifyDataSetChanged(); + } + + @SuppressWarnings("unused") + @OnLifecycleEvent(Lifecycle.Event.ON_ANY) + void cleanup(LifecycleOwner source, Lifecycle.Event event) { + if (event == Lifecycle.Event.ON_STOP) { + cleanup(); + } else if (event == Lifecycle.Event.ON_DESTROY) { + source.getLifecycle().removeObserver(this); + } } @Override @@ -134,7 +187,7 @@ public DatabaseReference getRef(int position) { @Override public int getItemCount() { - return mSnapshots.size(); + return mSnapshots.isListening(this) ? mSnapshots.size() : 0; } @Override From 023b69d22456d6f93c1168de20256d0931e3e63b Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 1 Aug 2017 12:49:44 -0700 Subject: [PATCH 06/11] Remove custom test runner, update robolectric (#841) --- auth/build.gradle | 4 +- .../auth/ui/email/RegisterEmailFragment.java | 14 +++--- .../java/com/firebase/ui/auth/AuthUITest.java | 4 +- .../ui/auth/testhelpers/AuthHelperShadow.java | 13 +++--- .../CustomRobolectricGradleTestRunner.java | 43 ------------------- .../ui/auth/testhelpers/FakeAuthResult.java | 16 +++++-- .../ui/auth/testhelpers/TestHelper.java | 15 +++++++ .../ui/email/RecoverPasswordActivityTest.java | 6 +-- .../ui/email/RegisterEmailActivityTest.java | 42 ++++++++++++------ .../email/WelcomeBackPasswordPromptTest.java | 10 ++--- .../ui/idp/AuthMethodPickerActivityTest.java | 13 +++--- .../ui/idp/CredentialSignInHandlerTest.java | 7 +-- .../phone/BucketedTextChangeListenerTest.java | 4 +- .../ui/auth/ui/phone/CountryInfoTests.java | 4 +- .../ui/phone/CountryListLoadTaskTests.java | 4 +- .../ui/auth/ui/phone/PhoneNumberTest.java | 4 +- .../auth/ui/phone/PhoneNumberUtilsTest.java | 4 +- .../phone/PhoneVerificationActivityTest.java | 18 ++++---- .../ui/auth/ui/phone/SpacedEditTextTest.java | 4 +- 19 files changed, 112 insertions(+), 117 deletions(-) delete mode 100644 auth/src/test/java/com/firebase/ui/auth/testhelpers/CustomRobolectricGradleTestRunner.java diff --git a/auth/build.gradle b/auth/build.gradle index d14c5ad70..7561e56ce 100644 --- a/auth/build.gradle +++ b/auth/build.gradle @@ -60,9 +60,7 @@ dependencies { testCompile 'junit:junit:4.12' //noinspection GradleDynamicVersion testCompile 'org.mockito:mockito-core:2.8.+' - testCompile 'org.robolectric:robolectric:3.2.2' - // See https://github.com/robolectric/robolectric/issues/1932#issuecomment-219796474 - testCompile 'org.khronos:opengl-api:gl1.1-android-2.1_r1' + testCompile 'org.robolectric:robolectric:3.4' testCompile 'com.facebook.android:facebook-android-sdk:4.23.0' } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java index 9882818e3..753877b03 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java @@ -244,13 +244,15 @@ public void onComplete(@NonNull Task task) { // This executes even if the name change fails, since // the account creation succeeded and we want to save // the credential to SmartLock (if enabled). + IdpResponse response = new IdpResponse.Builder( + new User.Builder(EmailAuthProvider.PROVIDER_ID, email) + .setName(name) + .setPhotoUri(mUser.getPhotoUri()) + .build()) + .build(); + mActivity.saveCredentialsOrFinish( - mSaveSmartLock, user, password, - new IdpResponse.Builder(new User.Builder( - EmailAuthProvider.PROVIDER_ID, email) - .setName(name) - .setPhotoUri(mUser.getPhotoUri()) - .build()).build()); + mSaveSmartLock, user, password, response); } }); } diff --git a/auth/src/test/java/com/firebase/ui/auth/AuthUITest.java b/auth/src/test/java/com/firebase/ui/auth/AuthUITest.java index 3c41d8290..651dc2c2e 100644 --- a/auth/src/test/java/com/firebase/ui/auth/AuthUITest.java +++ b/auth/src/test/java/com/firebase/ui/auth/AuthUITest.java @@ -16,7 +16,6 @@ import com.firebase.ui.auth.AuthUI.IdpConfig; import com.firebase.ui.auth.AuthUI.SignInIntentBuilder; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.TestConstants; import com.firebase.ui.auth.testhelpers.TestHelper; import com.firebase.ui.auth.ui.ExtraConstants; @@ -26,6 +25,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -33,7 +33,7 @@ import static junit.framework.Assert.assertEquals; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class AuthUITest { private FirebaseApp mFirebaseApp; diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/AuthHelperShadow.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/AuthHelperShadow.java index aac94791a..06459b8c6 100644 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/AuthHelperShadow.java +++ b/auth/src/test/java/com/firebase/ui/auth/testhelpers/AuthHelperShadow.java @@ -18,21 +18,16 @@ public class AuthHelperShadow { public static final FirebaseAuth sFirebaseAuth; - public static final FirebaseUser sFirebaseUser; public static final CredentialsApi sCredentialsApi; public static final SaveSmartLock sSaveSmartLock; public static final PhoneAuthProvider sPhoneAuthProvider; + private static FirebaseUser sFirebaseUser; + static { // CredentialsApi sCredentialsApi = Mockito.mock(CredentialsApi.class); - // FirebaseUser - sFirebaseUser = Mockito.mock(FirebaseUser.class); - when(sFirebaseUser.getEmail()).thenReturn(TestConstants.EMAIL); - when(sFirebaseUser.getDisplayName()).thenReturn(TestConstants.NAME); - when(sFirebaseUser.getPhotoUrl()).thenReturn(TestConstants.PHOTO_URI); - // FirebaseAuth sFirebaseAuth = Mockito.mock(FirebaseAuth.class); when(sFirebaseAuth.getCurrentUser()).thenReturn(sFirebaseUser); @@ -58,6 +53,10 @@ public static CredentialsApi getCredentialsApi() { @Implementation public static FirebaseUser getCurrentUser() { + if (sFirebaseUser == null) { + sFirebaseUser = TestHelper.getMockFirebaseUser(); + } + return sFirebaseUser; } diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/CustomRobolectricGradleTestRunner.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/CustomRobolectricGradleTestRunner.java deleted file mode 100644 index 340864245..000000000 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/CustomRobolectricGradleTestRunner.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.ui.auth.testhelpers; - -import com.facebook.login.LoginManager; -import com.firebase.ui.auth.provider.FacebookProvider; -import com.firebase.ui.auth.provider.GoogleProvider; -import com.firebase.ui.auth.util.AuthHelper; - -import org.junit.runners.model.InitializationError; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.annotation.Config; -import org.robolectric.internal.bytecode.InstrumentationConfiguration; - -public class CustomRobolectricGradleTestRunner extends RobolectricTestRunner { - public CustomRobolectricGradleTestRunner(Class klass) throws InitializationError { - super(klass); - } - - @Override - public InstrumentationConfiguration createClassLoaderConfig(Config config) { - InstrumentationConfiguration.Builder builder = InstrumentationConfiguration.newBuilder(); - - builder.addInstrumentedClass(AuthHelper.class.getName()); - builder.addInstrumentedClass(FacebookProvider.class.getName()); - builder.addInstrumentedClass(GoogleProvider.class.getName()); - builder.addInstrumentedClass(LoginManager.class.getName()); - - return builder.build(); - } -} diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java index be6b669c2..2b2870fba 100644 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java +++ b/auth/src/test/java/com/firebase/ui/auth/testhelpers/FakeAuthResult.java @@ -21,13 +21,21 @@ public final class FakeAuthResult implements AuthResult { public static final AuthResult INSTANCE = new FakeAuthResult(); - private FakeAuthResult() { - // Singleton - } + private FirebaseUser mUser; + + // Singleton + private FakeAuthResult() {} @Override public FirebaseUser getUser() { - return AuthHelperShadow.getCurrentUser(); + // TODO(samstern): This method should just call AuthHelperShadow.getCurrentUser(), + // but it fails + + if (mUser == null) { + mUser = TestHelper.getMockFirebaseUser(); + } + + return mUser; } @Override diff --git a/auth/src/test/java/com/firebase/ui/auth/testhelpers/TestHelper.java b/auth/src/test/java/com/firebase/ui/auth/testhelpers/TestHelper.java index 01d1977d0..f6780274d 100644 --- a/auth/src/test/java/com/firebase/ui/auth/testhelpers/TestHelper.java +++ b/auth/src/test/java/com/firebase/ui/auth/testhelpers/TestHelper.java @@ -30,14 +30,19 @@ import java.util.List; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; public class TestHelper { private static final String APPLICATION_ID = "testAppId"; private static final String API_KEY = "fakeKey"; private static final String FIREBASE_APP_NAME = "firebaseAppName"; + private static FirebaseUser sFirebaseUser; + public static FirebaseApp initializeApp(Context context) { try { return FirebaseApp.initializeApp( @@ -52,6 +57,15 @@ public static FirebaseApp initializeApp(Context context) { } } + public static FirebaseUser getMockFirebaseUser() { + FirebaseUser user = mock(FirebaseUser.class); + when(user.getEmail()).thenReturn(TestConstants.EMAIL); + when(user.getDisplayName()).thenReturn(TestConstants.NAME); + when(user.getPhotoUrl()).thenReturn(TestConstants.PHOTO_URI); + + return user; + } + public static FlowParameters getFlowParameters(List providerIds) { List idpConfigs = new ArrayList<>(); for (String providerId : providerIds) { @@ -86,6 +100,7 @@ public static void verifySmartLockSave(String providerId, String email, idpResponseCaptor.capture()); // Check email and password + assertNotNull(userCaptor.getValue()); assertEquals(email, userCaptor.getValue().getEmail()); assertEquals(password, passwordCaptor.getValue()); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivityTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivityTest.java index 3dbdfbc6f..fd8957fc2 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivityTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivityTest.java @@ -21,7 +21,6 @@ import com.firebase.ui.auth.R; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.TestConstants; import com.firebase.ui.auth.testhelpers.TestHelper; @@ -29,6 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -37,7 +37,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class RecoverPasswordActivityTest { @@ -51,7 +51,7 @@ private RecoverPasswordActivity createActivity() { RuntimeEnvironment.application, TestHelper.getFlowParameters(Collections.emptyList()), TestConstants.EMAIL); - return Robolectric.buildActivity(RecoverPasswordActivity.class).withIntent(startIntent) + return Robolectric.buildActivity(RecoverPasswordActivity.class, startIntent) .create().visible().get(); } diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/email/RegisterEmailActivityTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/email/RegisterEmailActivityTest.java index bf99210f7..5651faf25 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/email/RegisterEmailActivityTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/email/RegisterEmailActivityTest.java @@ -15,7 +15,6 @@ package com.firebase.ui.auth.ui.email; import android.content.Intent; -import android.os.Bundle; import android.support.design.widget.TextInputLayout; import android.widget.Button; import android.widget.EditText; @@ -26,7 +25,6 @@ import com.firebase.ui.auth.User; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.FakeAuthResult; import com.firebase.ui.auth.testhelpers.TestConstants; import com.firebase.ui.auth.testhelpers.TestHelper; @@ -36,7 +34,9 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -45,10 +45,10 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; - -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class RegisterEmailActivityTest { @@ -56,9 +56,9 @@ private RegisterEmailActivity createActivity() { Intent startIntent = RegisterEmailActivity.createIntent( RuntimeEnvironment.application, TestHelper.getFlowParameters(Collections.singletonList(AuthUI.EMAIL_PROVIDER))); - return Robolectric.buildActivity(RegisterEmailActivity.class) - .withIntent(startIntent) - .create(new Bundle()) + + return Robolectric.buildActivity(RegisterEmailActivity.class, startIntent) + .create() .start() .visible() .get(); @@ -115,23 +115,39 @@ public void testSignUpButton_successfulRegistrationShouldContinueToSaveCredentia .setPhotoUri(TestConstants.PHOTO_URI) .build()); + EditText email = (EditText) registerEmailActivity.findViewById(R.id.email); EditText name = (EditText) registerEmailActivity.findViewById(R.id.name); EditText password = (EditText) registerEmailActivity.findViewById(R.id.password); + + email.setText(TestConstants.EMAIL); name.setText(TestConstants.NAME); password.setText(TestConstants.PASSWORD); - when(AuthHelperShadow.sFirebaseUser.updateProfile(any(UserProfileChangeRequest.class))) - .thenReturn(new AutoCompleteTask(null, true, null)); - + // Mock user creation requests when(AuthHelperShadow.sFirebaseAuth - .createUserWithEmailAndPassword( - TestConstants.EMAIL, - TestConstants.PASSWORD)) + .createUserWithEmailAndPassword(TestConstants.EMAIL, TestConstants.PASSWORD)) .thenReturn(new AutoCompleteTask<>(FakeAuthResult.INSTANCE, true, null)); + // Mock change profle requests + when(FakeAuthResult.INSTANCE.getUser() + .updateProfile(any(UserProfileChangeRequest.class))) + .thenReturn(new AutoCompleteTask(null, true, null)); + Button button = (Button) registerEmailActivity.findViewById(R.id.button_create); button.performClick(); + // Verify create user request + verify(AuthHelperShadow.sFirebaseAuth).createUserWithEmailAndPassword( + TestConstants.EMAIL, + TestConstants.PASSWORD); + + // After create user request, should try to update profile with name + ArgumentCaptor changeRequestCaptor = + ArgumentCaptor.forClass(UserProfileChangeRequest.class); + verify(FakeAuthResult.INSTANCE.getUser()).updateProfile(changeRequestCaptor.capture()); + assertEquals(changeRequestCaptor.getValue().getDisplayName(), TestConstants.NAME); + + // Finally, the new credential should be saved to SmartLock TestHelper.verifySmartLockSave( EmailAuthProvider.PROVIDER_ID, TestConstants.EMAIL, diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPromptTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPromptTest.java index e32201af0..5f6fb2df3 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPromptTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPromptTest.java @@ -19,13 +19,13 @@ import android.widget.Button; import android.widget.EditText; +import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.BuildConfig; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; import com.firebase.ui.auth.User; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.FakeAuthResult; import com.firebase.ui.auth.testhelpers.TestConstants; import com.firebase.ui.auth.testhelpers.TestHelper; @@ -36,6 +36,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.annotation.Config; @@ -50,7 +51,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class WelcomeBackPasswordPromptTest { @Before @@ -61,14 +62,13 @@ public void setUp() { private WelcomeBackPasswordPrompt createActivity() { Intent startIntent = WelcomeBackPasswordPrompt.createIntent( RuntimeEnvironment.application, - TestHelper.getFlowParameters(Collections.emptyList()), + TestHelper.getFlowParameters(Collections.singletonList(AuthUI.EMAIL_PROVIDER)), new IdpResponse.Builder( new User.Builder(EmailAuthProvider.PROVIDER_ID, TestConstants.EMAIL) .build()) .build()); return Robolectric - .buildActivity(WelcomeBackPasswordPrompt.class) - .withIntent(startIntent) + .buildActivity(WelcomeBackPasswordPrompt.class, startIntent) .create() .visible() .get(); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivityTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivityTest.java index 2e2bdd25f..72be3ef12 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivityTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivityTest.java @@ -24,7 +24,6 @@ import com.firebase.ui.auth.R; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.FacebookProviderShadow; import com.firebase.ui.auth.testhelpers.FakeAuthResult; import com.firebase.ui.auth.testhelpers.GoogleProviderShadow; @@ -41,6 +40,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.Shadows; import org.robolectric.annotation.Config; @@ -57,7 +57,7 @@ import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, shadows = { GoogleProviderShadow.class, @@ -121,7 +121,7 @@ public void testFacebookLoginFlow() { // initialize mocks reset(AuthHelperShadow.sSaveSmartLock); - when(AuthHelperShadow.sFirebaseUser.getProviders()) + when(AuthHelperShadow.getCurrentUser().getProviders()) .thenReturn(Arrays.asList(FacebookAuthProvider.PROVIDER_ID)); when(AuthHelperShadow.sFirebaseAuth.signInWithCredential((AuthCredential) any())) .thenReturn(new AutoCompleteTask<>(FakeAuthResult.INSTANCE, true, null)); @@ -147,7 +147,7 @@ public void testGoogleLoginFlow() { AuthMethodPickerActivity authMethodPickerActivity = createActivity(providers); - when(AuthHelperShadow.sFirebaseUser.getProviders()) + when(AuthHelperShadow.getCurrentUser().getProviders()) .thenReturn(Arrays.asList(GoogleAuthProvider.PROVIDER_ID)); when(AuthHelperShadow.sFirebaseAuth.signInWithCredential((AuthCredential) any())) @@ -168,7 +168,7 @@ public void testTwitterLoginFlowStarts() { AuthMethodPickerActivity authMethodPickerActivity = createActivity(providers); - when(AuthHelperShadow.sFirebaseUser.getProviders()) + when(AuthHelperShadow.getCurrentUser().getProviders()) .thenReturn(Arrays.asList(TwitterAuthProvider.PROVIDER_ID)); when(AuthHelperShadow.sFirebaseAuth.signInWithCredential(any(AuthCredential.class))) @@ -190,8 +190,7 @@ private AuthMethodPickerActivity createActivity(List providers) { TestHelper.getFlowParameters(providers)); return Robolectric - .buildActivity(AuthMethodPickerActivity.class) - .withIntent(startIntent) + .buildActivity(AuthMethodPickerActivity.class, startIntent) .create() .visible() .get(); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandlerTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandlerTest.java index 9d24b60c7..8942e50a6 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandlerTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/idp/CredentialSignInHandlerTest.java @@ -21,7 +21,6 @@ import com.firebase.ui.auth.User; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.FakeAuthResult; import com.firebase.ui.auth.testhelpers.FakeProviderQueryResult; import com.firebase.ui.auth.testhelpers.TestConstants; @@ -49,6 +48,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -60,7 +60,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class CredentialSignInHandlerTest { private static final int RC_ACCOUNT_LINK = 3; @@ -104,7 +104,8 @@ public void testSignInSucceeded() { idpResponseCaptor.capture()); assertEquals(smartLock, smartLockCaptor.getValue()); - assertEquals(AuthHelperShadow.sFirebaseUser, firebaseUserCaptor.getValue()); + assertEquals(TestConstants.EMAIL, + firebaseUserCaptor.getValue().getEmail()); IdpResponse response = idpResponseCaptor.getValue(); assertEquals(idpResponse.getProviderType(), response.getProviderType()); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/BucketedTextChangeListenerTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/BucketedTextChangeListenerTest.java index cb4d5a254..c89d9afb0 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/BucketedTextChangeListenerTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/BucketedTextChangeListenerTest.java @@ -20,19 +20,19 @@ import android.widget.EditText; import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InOrder; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class BucketedTextChangeListenerTest { EditText editText; diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryInfoTests.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryInfoTests.java index bd0e9884f..6a084b6e1 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryInfoTests.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryInfoTests.java @@ -18,10 +18,10 @@ package com.firebase.ui.auth.ui.phone; import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.util.Locale; @@ -30,7 +30,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotSame; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class CountryInfoTests { private static final Locale COUNTRY_NAME_US = new Locale("", "US"); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java index 2167809b5..bf121b2bf 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/CountryListLoadTaskTests.java @@ -19,11 +19,11 @@ package com.firebase.ui.auth.ui.phone; import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.util.ArrayList; @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class CountryListLoadTaskTests { private static final ArrayList COUNTRY_LIST = new ArrayList<>(); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberTest.java index 9a8169b9e..666077eca 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberTest.java @@ -18,16 +18,16 @@ package com.firebase.ui.auth.ui.phone; import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class PhoneNumberTest { @Test diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java index 853b918c3..e05be5408 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java @@ -22,10 +22,10 @@ import android.telephony.TelephonyManager; import com.firebase.ui.auth.BuildConfig; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.annotation.Config; import java.util.Locale; @@ -40,7 +40,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class PhoneNumberUtilsTest { @Test diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java index bc0edcec1..bbefbe383 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java @@ -28,7 +28,6 @@ import com.firebase.ui.auth.R; import com.firebase.ui.auth.testhelpers.AuthHelperShadow; import com.firebase.ui.auth.testhelpers.AutoCompleteTask; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import com.firebase.ui.auth.testhelpers.FakeAuthResult; import com.firebase.ui.auth.testhelpers.TestHelper; import com.google.firebase.auth.AuthCredential; @@ -46,6 +45,7 @@ import org.mockito.Captor; import org.mockito.Mock; import org.robolectric.Robolectric; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowAlertDialog; @@ -72,7 +72,7 @@ import static org.mockito.MockitoAnnotations.initMocks; import static org.robolectric.Shadows.shadowOf; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) public class PhoneVerificationActivityTest { private PhoneVerificationActivity mActivity; @@ -97,7 +97,7 @@ private PhoneVerificationActivity createActivity() { RuntimeEnvironment.application, TestHelper.getFlowParameters( Collections.singletonList(AuthUI.PHONE_VERIFICATION_PROVIDER)), null); - return Robolectric.buildActivity(PhoneVerificationActivity.class).withIntent(startIntent) + return Robolectric.buildActivity(PhoneVerificationActivity.class, startIntent) .create(new Bundle()).start().visible().get(); } @@ -120,8 +120,8 @@ public void testPhoneNumberFromSmartlock_prePopulatesPhoneNumberInBundle() { Collections.singletonList(AuthUI.PHONE_VERIFICATION_PROVIDER)), YE_RAW_PHONE); - mActivity = Robolectric.buildActivity(PhoneVerificationActivity.class).withIntent - (startIntent).create(new Bundle()).start().visible().get(); + mActivity = Robolectric.buildActivity(PhoneVerificationActivity.class, startIntent) + .create(new Bundle()).start().visible().get(); VerifyPhoneNumberFragment verifyPhoneNumberFragment = (VerifyPhoneNumberFragment) mActivity.getSupportFragmentManager() @@ -273,8 +273,8 @@ public void testAutoVerify() { reset(AuthHelperShadow.sSaveSmartLock); reset(AuthHelperShadow.sFirebaseAuth); - when(AuthHelperShadow.sFirebaseUser.getPhoneNumber()).thenReturn(PHONE); - when(AuthHelperShadow.sFirebaseUser.getEmail()).thenReturn(null); + when(AuthHelperShadow.getCurrentUser().getPhoneNumber()).thenReturn(PHONE); + when(AuthHelperShadow.getCurrentUser().getEmail()).thenReturn(null); when(AuthHelperShadow.sFirebaseAuth.signInWithCredential(any(AuthCredential.class))) .thenReturn(new AutoCompleteTask<>(FakeAuthResult.INSTANCE, true, null)); mActivity.verifyPhoneNumber(PHONE, false); @@ -300,8 +300,8 @@ public void testSMSAutoRetrieval() { reset(AuthHelperShadow.sSaveSmartLock); when(credential.getSmsCode()).thenReturn("123456"); - when(AuthHelperShadow.sFirebaseUser.getPhoneNumber()).thenReturn(PHONE); - when(AuthHelperShadow.sFirebaseUser.getEmail()).thenReturn(null); + when(AuthHelperShadow.getCurrentUser().getPhoneNumber()).thenReturn(PHONE); + when(AuthHelperShadow.getCurrentUser().getEmail()).thenReturn(null); when(AuthHelperShadow.sFirebaseAuth.signInWithCredential(any(AuthCredential.class))) .thenReturn(new AutoCompleteTask<>(FakeAuthResult.INSTANCE, true, null)); diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/SpacedEditTextTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/SpacedEditTextTest.java index be45d4a11..b0d7c56ff 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/SpacedEditTextTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/SpacedEditTextTest.java @@ -25,11 +25,11 @@ import com.firebase.ui.auth.BuildConfig; import com.firebase.ui.auth.R; -import com.firebase.ui.auth.testhelpers.CustomRobolectricGradleTestRunner; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; @@ -37,7 +37,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@RunWith(CustomRobolectricGradleTestRunner.class) +@RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) public class SpacedEditTextTest { SpacedEditText spacedEditText; From 145f7899d296fed0b21198d1237f124cc8d7ea33 Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 1 Aug 2017 13:16:21 -0700 Subject: [PATCH 07/11] Fix sample app and GSI error handling (#839) Fixes #833 Change-Id: I6f22cf814625a1f4a1c14c3007cfd3491b8cd22a --- .../java/com/firebase/uidemo/auth/SignedInActivity.java | 9 +++++++-- .../com/firebase/ui/auth/provider/GoogleProvider.java | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java index f0d129bcf..509352af2 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java @@ -54,6 +54,7 @@ public class SignedInActivity extends AppCompatActivity { + private static final String EXTRA_IDP_RESPONSE = "extra_idp_response"; private static final String EXTRA_SIGNED_IN_CONFIG = "extra_signed_in_config"; @BindView(android.R.id.content) @@ -83,7 +84,11 @@ public static Intent createIntent( Context context, IdpResponse idpResponse, SignedInConfig signedInConfig) { - Intent startIntent = idpResponse == null ? new Intent() : idpResponse.toIntent(); + + Intent startIntent = new Intent(); + if (idpResponse != null) { + startIntent.putExtra(EXTRA_IDP_RESPONSE, idpResponse); + } return startIntent.setClass(context, SignedInActivity.class) .putExtra(EXTRA_SIGNED_IN_CONFIG, signedInConfig); @@ -100,7 +105,7 @@ public void onCreate(Bundle savedInstanceState) { return; } - mIdpResponse = IdpResponse.fromResultIntent(getIntent()); + mIdpResponse = getIntent().getParcelableExtra(EXTRA_IDP_RESPONSE); mSignedInConfig = getIntent().getParcelableExtra(EXTRA_SIGNED_IN_CONFIG); setContentView(R.layout.signed_in_layout); diff --git a/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java b/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java index 960c6e368..9748a6769 100644 --- a/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java +++ b/auth/src/main/java/com/firebase/ui/auth/provider/GoogleProvider.java @@ -165,6 +165,11 @@ private void onError(GoogleSignInResult result) { .build(); startLogin(mActivity); } else { + if (status.getStatusCode() == CommonStatusCodes.DEVELOPER_ERROR) { + Log.w(TAG, "Developer error: this application is misconfigured. Check your SHA1 " + + " and package name in the Firebase console."); + Toast.makeText(mActivity, "Developer error.", Toast.LENGTH_SHORT).show(); + } onError(status.getStatusCode() + " " + status.getStatusMessage()); } } From d2c7ed74870837203d87dd819a48da68d10c4d5a Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 1 Aug 2017 13:54:26 -0700 Subject: [PATCH 08/11] Remove phone number validation on the client side (#840) --- .../ui/auth/ui/phone/PhoneNumberUtils.java | 15 +++++--- .../auth/ui/phone/PhoneNumberUtilsTest.java | 35 ------------------- 2 files changed, 10 insertions(+), 40 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtils.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtils.java index b7b16b346..ae90453cf 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtils.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtils.java @@ -70,11 +70,6 @@ static void load() { */ @Nullable static String formatPhoneNumber(@NonNull String phoneNumber, @NonNull CountryInfo countryInfo) { - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - return android.telephony.PhoneNumberUtils - .formatNumberToE164(phoneNumber, countryInfo.locale.getCountry()); - } return phoneNumber.startsWith("+") ? phoneNumber : ("+" + String.valueOf(countryInfo.countryCode) @@ -93,6 +88,16 @@ static String formatPhoneNumber(@NonNull String phoneNumber, @NonNull CountryInf static String formatPhoneNumberUsingCurrentCountry( @NonNull String phoneNumber, Context context) { final CountryInfo currentCountry = PhoneNumberUtils.getCurrentCountryInfo(context); + + // TODO(samstern): Remove this call once the next version of Play servics is released + // estimated timelime August 10th. It is a known issue that phone numbers + // from the hint selector are susceptible to the false negatives of this + // method. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + return android.telephony.PhoneNumberUtils + .formatNumberToE164(phoneNumber, currentCountry.locale.getCountry()); + } + return formatPhoneNumber(phoneNumber, currentCountry); } diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java index e05be5408..d7d98ea61 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneNumberUtilsTest.java @@ -36,7 +36,6 @@ import static com.firebase.ui.auth.ui.phone.PhoneNumberUtils.getPhoneNumber; import static com.firebase.ui.auth.ui.phone.PhoneTestConstants.RAW_PHONE; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -83,40 +82,6 @@ public void testGetCountryCode() throws Exception { assertEquals(null, getCountryCode(new Locale("", "DJJZ").getCountry())); } - @Test - @Config(constants = BuildConfig.class, sdk = 21) - public void testFormatNumberToE164_aboveApi21() { - String validPhoneNumber = "+919994947354"; - CountryInfo indiaCountryInfo = new CountryInfo(new Locale("", "IN"), 91); - //no leading plus - assertEquals(validPhoneNumber, formatPhoneNumber("9994947354", indiaCountryInfo)); - //no country code - assertEquals(validPhoneNumber, formatPhoneNumber("919994947354", indiaCountryInfo)); - //fully formatted - assertEquals(validPhoneNumber, formatPhoneNumber("+919994947354", indiaCountryInfo)); - //with hyphens - assertEquals(validPhoneNumber, formatPhoneNumber("+91-(999)-(49)-(47354)", indiaCountryInfo)); - //with spaces leading plus - assertEquals(validPhoneNumber, formatPhoneNumber("+91 99949 47354", indiaCountryInfo)); - // space formatting - assertEquals(validPhoneNumber, formatPhoneNumber("91 99949 47354", indiaCountryInfo)); - // parantheses and hyphens - assertEquals(validPhoneNumber, formatPhoneNumber("(99949) 47-354", indiaCountryInfo)); - // mismatched country - assertEquals(validPhoneNumber, formatPhoneNumber("+919994947354", - new CountryInfo( - new Locale("", "US"), 1))); - // incorrect country with well formatted number - assertNull(formatPhoneNumber("999474735", indiaCountryInfo)); - - // incorrect country with unformattednumber - assertNull(validPhoneNumber, formatPhoneNumber( - "919994947354", new CountryInfo(new Locale("", "US"), 1))); - //incorrect country, incorrect phone number - assertNull(formatPhoneNumber("+914349873457", new CountryInfo( - new Locale("", "US"), 1))); - } - @Test @Config(constants = BuildConfig.class, sdk = 16) public void testFormatNumberToE164_belowApi21() { From 3a082fdff490564e22bc0f8a08ceb91dbf015932 Mon Sep 17 00:00:00 2001 From: Alex Saveau Date: Tue, 1 Aug 2017 14:48:56 -0700 Subject: [PATCH 09/11] Fix phone verification progress dialog bugs on pre-L devices (#817) --- .../ui/phone/CompletableProgressDialog.java | 41 ++++++----- .../ui/phone/PhoneVerificationActivity.java | 9 +-- .../res/layout/fui_phone_progress_dialog.xml | 68 ++++++++----------- .../phone/PhoneVerificationActivityTest.java | 9 +-- 4 files changed, 56 insertions(+), 71 deletions(-) diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CompletableProgressDialog.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CompletableProgressDialog.java index 3f46e9149..f4db8f639 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CompletableProgressDialog.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CompletableProgressDialog.java @@ -14,9 +14,13 @@ package com.firebase.ui.auth.ui.phone; -import android.app.ProgressDialog; -import android.content.Context; +import android.app.Dialog; import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.VisibleForTesting; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentManager; +import android.support.v7.app.AlertDialog; import android.view.View; import android.widget.ImageView; import android.widget.ProgressBar; @@ -24,41 +28,42 @@ import com.firebase.ui.auth.R; -public final class CompletableProgressDialog extends ProgressDialog { +public final class CompletableProgressDialog extends DialogFragment { + private static final String TAG = "ComProgressDialog"; + private ProgressBar mProgress; - private TextView mMessageView; + @VisibleForTesting TextView mMessageView; private CharSequence mMessage; private ImageView mSuccessImage; - public CompletableProgressDialog(Context context) { - super(context); - } - - public CompletableProgressDialog(Context context, int theme) { - super(context, theme); + public static CompletableProgressDialog show(FragmentManager manager) { + CompletableProgressDialog dialog = new CompletableProgressDialog(); + dialog.show(manager, TAG); + return dialog; } + @NonNull @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.fui_phone_progress_dialog); + public Dialog onCreateDialog(Bundle savedInstanceState) { + View rootView = View.inflate(getContext(), R.layout.fui_phone_progress_dialog, null); - mProgress = (ProgressBar) findViewById(R.id.progress_bar); - mMessageView = (TextView) findViewById(R.id.progress_msg); - mSuccessImage = (ImageView) findViewById(R.id.progress_success_imaage); + mProgress = (ProgressBar) rootView.findViewById(R.id.progress_bar); + mMessageView = (TextView) rootView.findViewById(R.id.progress_msg); + mSuccessImage = (ImageView) rootView.findViewById(R.id.progress_success_imaage); if (mMessage != null) { setMessage(mMessage); } + + return new AlertDialog.Builder(getContext()).setView(rootView).create(); } - public void complete(String msg) { + public void onComplete(String msg) { setMessage(msg); mProgress.setVisibility(View.GONE); mSuccessImage.setVisibility(View.VISIBLE); } - @Override public void setMessage(CharSequence message) { if (mProgress != null) { mMessageView.setText(message); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivity.java index 0a3864dd1..aaf48b545 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivity.java @@ -67,7 +67,7 @@ private enum VerificationState { private static final String KEY_STATE = "KEY_STATE"; private AlertDialog mAlertDialog; - private CompletableProgressDialog mProgressDialog; + @VisibleForTesting CompletableProgressDialog mProgressDialog; private Handler mHandler; private String mPhoneNumber; private String mVerificationId; @@ -371,7 +371,7 @@ public void onClick(DialogInterface dialog, int which) { private void completeLoadingDialog(String content) { if (mProgressDialog != null) { - mProgressDialog.complete(content); + mProgressDialog.onComplete(content); } } @@ -379,13 +379,10 @@ private void showLoadingDialog(String message) { dismissLoadingDialog(); if (mProgressDialog == null) { - mProgressDialog = new CompletableProgressDialog(this); - mProgressDialog.setIndeterminate(true); - mProgressDialog.setTitle(""); + mProgressDialog = CompletableProgressDialog.show(getSupportFragmentManager()); } mProgressDialog.setMessage(message); - mProgressDialog.show(); } private void dismissLoadingDialog() { diff --git a/auth/src/main/res/layout/fui_phone_progress_dialog.xml b/auth/src/main/res/layout/fui_phone_progress_dialog.xml index b5b225cfd..dd7a4976f 100644 --- a/auth/src/main/res/layout/fui_phone_progress_dialog.xml +++ b/auth/src/main/res/layout/fui_phone_progress_dialog.xml @@ -1,47 +1,33 @@ - + android:layout_height="wrap_content" + android:orientation="horizontal" + android:padding="8dp"> - - - + - + - - - + - + diff --git a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java index bbefbe383..cbf6fb97b 100644 --- a/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java +++ b/auth/src/test/java/com/firebase/ui/auth/ui/phone/PhoneVerificationActivityTest.java @@ -14,7 +14,6 @@ package com.firebase.ui.auth.ui.phone; -import android.app.AlertDialog; import android.content.Intent; import android.os.Bundle; import android.view.View; @@ -48,7 +47,6 @@ import org.robolectric.RobolectricTestRunner; import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowAlertDialog; import org.robolectric.shadows.ShadowLooper; import java.util.Collections; @@ -70,7 +68,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.MockitoAnnotations.initMocks; -import static org.robolectric.Shadows.shadowOf; @RunWith(RobolectricTestRunner.class) @Config(constants = BuildConfig.class, sdk = 25) @@ -155,10 +152,10 @@ public void testVerifyPhoneNumberInvalidPhoneException_showsInlineError() { reset(AuthHelperShadow.sPhoneAuthProvider); mActivity.verifyPhoneNumber(PHONE, false); - AlertDialog alert = ShadowAlertDialog.getLatestAlertDialog(); - ShadowAlertDialog sAlert = shadowOf(alert); //was dialog displayed - assertEquals(mActivity.getString(R.string.fui_verifying), sAlert.getMessage()); + assertEquals( + mActivity.getString(R.string.fui_verifying), + mActivity.mProgressDialog.mMessageView.getText()); //was upstream method invoked verify(AuthHelperShadow.sPhoneAuthProvider).verifyPhoneNumber( From 7f546f0a291ce6d0025330f8e0b3090ffa169e0e Mon Sep 17 00:00:00 2001 From: Sam Stern Date: Tue, 1 Aug 2017 17:22:52 -0700 Subject: [PATCH 10/11] Make shadows have buttons on pre-L devices. (#843) --- .../fui_idp_button_background_email.xml | 9 +++++++++ .../fui_idp_button_background_facebook.xml | 9 +++++++++ .../fui_idp_button_background_google.xml | 9 +++++++++ .../fui_idp_button_background_phone.xml | 9 +++++++++ .../fui_idp_button_background_twitter.xml | 9 +++++++++ .../fui_idp_button_background_email.xml | 16 +++++++++++++--- .../fui_idp_button_background_facebook.xml | 16 +++++++++++++--- .../fui_idp_button_background_google.xml | 16 +++++++++++++--- .../fui_idp_button_background_phone.xml | 16 +++++++++++++--- .../fui_idp_button_background_twitter.xml | 16 +++++++++++++--- auth/src/main/res/values/colors.xml | 7 +++++++ auth/src/main/res/values/dimens.xml | 5 +++++ auth/src/main/res/values/styles.xml | 19 +++++++++---------- 13 files changed, 131 insertions(+), 25 deletions(-) create mode 100644 auth/src/main/res/drawable-v21/fui_idp_button_background_email.xml create mode 100644 auth/src/main/res/drawable-v21/fui_idp_button_background_facebook.xml create mode 100644 auth/src/main/res/drawable-v21/fui_idp_button_background_google.xml create mode 100644 auth/src/main/res/drawable-v21/fui_idp_button_background_phone.xml create mode 100644 auth/src/main/res/drawable-v21/fui_idp_button_background_twitter.xml diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_email.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_email.xml new file mode 100644 index 000000000..b954d447b --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_email.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_facebook.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_facebook.xml new file mode 100644 index 000000000..b3318323d --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_facebook.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_google.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_google.xml new file mode 100644 index 000000000..2b2303896 --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_google.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_phone.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_phone.xml new file mode 100644 index 000000000..55f310552 --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_phone.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_twitter.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_twitter.xml new file mode 100644 index 000000000..860c536f6 --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_twitter.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_email.xml b/auth/src/main/res/drawable/fui_idp_button_background_email.xml index 9787633ac..64c267943 100644 --- a/auth/src/main/res/drawable/fui_idp_button_background_email.xml +++ b/auth/src/main/res/drawable/fui_idp_button_background_email.xml @@ -1,9 +1,19 @@ - + - + - + + + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_facebook.xml b/auth/src/main/res/drawable/fui_idp_button_background_facebook.xml index fc20c10d8..798b977f5 100644 --- a/auth/src/main/res/drawable/fui_idp_button_background_facebook.xml +++ b/auth/src/main/res/drawable/fui_idp_button_background_facebook.xml @@ -1,9 +1,19 @@ - + - + - + + + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_google.xml b/auth/src/main/res/drawable/fui_idp_button_background_google.xml index a30ab484f..4d866fd74 100644 --- a/auth/src/main/res/drawable/fui_idp_button_background_google.xml +++ b/auth/src/main/res/drawable/fui_idp_button_background_google.xml @@ -1,9 +1,19 @@ - + - + - + + + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_phone.xml b/auth/src/main/res/drawable/fui_idp_button_background_phone.xml index ca7462f62..8120539f1 100644 --- a/auth/src/main/res/drawable/fui_idp_button_background_phone.xml +++ b/auth/src/main/res/drawable/fui_idp_button_background_phone.xml @@ -1,9 +1,19 @@ - + - + - + + + + + + + diff --git a/auth/src/main/res/drawable/fui_idp_button_background_twitter.xml b/auth/src/main/res/drawable/fui_idp_button_background_twitter.xml index 296690958..ea70da983 100644 --- a/auth/src/main/res/drawable/fui_idp_button_background_twitter.xml +++ b/auth/src/main/res/drawable/fui_idp_button_background_twitter.xml @@ -1,9 +1,19 @@ - + - + - + + + + + + + diff --git a/auth/src/main/res/values/colors.xml b/auth/src/main/res/values/colors.xml index bc3274f39..c03640c70 100644 --- a/auth/src/main/res/values/colors.xml +++ b/auth/src/main/res/values/colors.xml @@ -8,4 +8,11 @@ #DD2C00 #4285F4 #00000000 + + #64BEBEBE + #D0021B + #3B5998 + #ffffff + #43C5A5 + #FF5BAAF4 diff --git a/auth/src/main/res/values/dimens.xml b/auth/src/main/res/values/dimens.xml index fb858c07c..234bafc37 100644 --- a/auth/src/main/res/values/dimens.xml +++ b/auth/src/main/res/values/dimens.xml @@ -9,4 +9,9 @@ 16dp 16dp 24dp + + 0dp + 3dp + 1dp + 2dp diff --git a/auth/src/main/res/values/styles.xml b/auth/src/main/res/values/styles.xml index ce3f85e89..af2a1d352 100644 --- a/auth/src/main/res/values/styles.xml +++ b/auth/src/main/res/values/styles.xml @@ -170,6 +170,15 @@ @dimen/fui_body_padding_bottom + + - -