diff --git a/.travis.yml b/.travis.yml index 8581a0e96..d7aee02ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,7 +25,9 @@ android: before_script: - cp library/google-services.json app/google-services.json - cp library/google-services.json proguard-tests/google-services.json -script: ./gradlew clean assembleDebug proguard-tests:build check +script: + - ./gradlew clean + - ./gradlew assembleDebug proguard-tests:build check after_success: ./scripts/artifactory.sh after_failure: # tests diff --git a/README.md b/README.md index 7aaa4374d..62cd3863b 100644 --- a/README.md +++ b/README.md @@ -50,19 +50,19 @@ libraries. ```groovy dependencies { // FirebaseUI for Firebase Realtime Database - implementation 'com.firebaseui:firebase-ui-database:4.2.0' + implementation 'com.firebaseui:firebase-ui-database:4.2.1' // FirebaseUI for Cloud Firestore - implementation 'com.firebaseui:firebase-ui-firestore:4.2.0' + implementation 'com.firebaseui:firebase-ui-firestore:4.2.1' // FirebaseUI for Firebase Auth - implementation 'com.firebaseui:firebase-ui-auth:4.2.0' + implementation 'com.firebaseui:firebase-ui-auth:4.2.1' // FirebaseUI for Firebase Auth (GitHub provider) - implementation 'com.firebaseui:firebase-ui-auth-github:4.2.0' + implementation 'com.firebaseui:firebase-ui-auth-github:4.2.1' // FirebaseUI for Cloud Storage - implementation 'com.firebaseui:firebase-ui-storage:4.2.0' + implementation 'com.firebaseui:firebase-ui-storage:4.2.1' } ``` @@ -105,15 +105,15 @@ versions. This means that FirebaseUI has independent dependencies on each of the For best results, your app should depend on a version of each dependency with the same major version number as the version used by FirebaseUI. -As of version `4.2.0`, FirebaseUI has the following dependency versions: +As of version `4.2.1`, FirebaseUI has the following dependency versions: | Library | Version | |----------------------|--------------------------------| -| `firebase-auth` | 16.0.3 | -| `play-services-auth` | 16.0.0 | -| `firebase-database` | 16.0.2 | -| `firebase-firestore` | 17.1.0 | -| `firebase-storage` | 16.0.2 | +| `firebase-auth` | 16.0.5 | +| `play-services-auth` | 16.0.1 | +| `firebase-database` | 16.0.3 | +| `firebase-firestore` | 17.1.1 | +| `firebase-storage` | 16.0.3 | ### Upgrading dependencies diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java index 0b18950ce..a6efd38f6 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -81,11 +81,8 @@ public class AuthUiActivity extends AppCompatActivity { @BindView(R.id.google_logo) RadioButton mGoogleLogo; @BindView(R.id.no_logo) RadioButton mNoLogo; - @BindView(R.id.google_tos) RadioButton mUseGoogleTos; - @BindView(R.id.firebase_tos) RadioButton mUseFirebaseTos; - - @BindView(R.id.google_privacy) RadioButton mUseGooglePrivacyPolicy; - @BindView(R.id.firebase_privacy) RadioButton mUseFirebasePrivacyPolicy; + @BindView(R.id.google_tos_privacy) RadioButton mUseGoogleTosPp; + @BindView(R.id.firebase_tos_privacy) RadioButton mUseFirebaseTosPp; @BindView(R.id.google_scopes_header) TextView mGoogleScopesHeader; @BindView(R.id.google_scope_drive_file) CheckBox mGoogleScopeDriveFile; @@ -180,17 +177,20 @@ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { @OnClick(R.id.sign_in) public void signIn() { - startActivityForResult( - AuthUI.getInstance().createSignInIntentBuilder() - .setTheme(getSelectedTheme()) - .setLogo(getSelectedLogo()) - .setAvailableProviders(getSelectedProviders()) - .setTosAndPrivacyPolicyUrls(getSelectedTosUrl(), - getSelectedPrivacyPolicyUrl()) - .setIsSmartLockEnabled(mEnableCredentialSelector.isChecked(), - mEnableHintSelector.isChecked()) - .build(), - RC_SIGN_IN); + AuthUI.SignInIntentBuilder builder = AuthUI.getInstance().createSignInIntentBuilder() + .setTheme(getSelectedTheme()) + .setLogo(getSelectedLogo()) + .setAvailableProviders(getSelectedProviders()) + .setIsSmartLockEnabled(mEnableCredentialSelector.isChecked(), + mEnableHintSelector.isChecked()); + + if (getSelectedTosUrl() != null && getSelectedPrivacyPolicyUrl() != null) { + builder.setTosAndPrivacyPolicyUrls( + getSelectedTosUrl(), + getSelectedPrivacyPolicyUrl()); + } + + startActivityForResult(builder.build(), RC_SIGN_IN); } @OnClick(R.id.sign_in_silent) @@ -328,20 +328,30 @@ private List getSelectedProviders() { return selectedProviders; } + @Nullable private String getSelectedTosUrl() { - if (mUseGoogleTos.isChecked()) { + if (mUseGoogleTosPp.isChecked()) { return GOOGLE_TOS_URL; } - return FIREBASE_TOS_URL; + if (mUseFirebaseTosPp.isChecked()) { + return FIREBASE_TOS_URL; + } + + return null; } + @Nullable private String getSelectedPrivacyPolicyUrl() { - if (mUseGooglePrivacyPolicy.isChecked()) { + if (mUseGoogleTosPp.isChecked()) { return GOOGLE_PRIVACY_POLICY_URL; } - return FIREBASE_PRIVACY_POLICY_URL; + if (mUseFirebaseTosPp.isChecked()) { + return FIREBASE_PRIVACY_POLICY_URL; + } + + return null; } private void setGoogleScopesEnabled(boolean enabled) { diff --git a/app/src/main/res/layout/auth_ui_layout.xml b/app/src/main/res/layout/auth_ui_layout.xml index c16c150d1..6f99974a0 100644 --- a/app/src/main/res/layout/auth_ui_layout.xml +++ b/app/src/main/res/layout/auth_ui_layout.xml @@ -182,7 +182,7 @@ android:layout_height="wrap_content" android:layout_marginTop="16dp" android:layout_marginBottom="8dp" - android:text="@string/tos_header" /> + android:text="@string/tos_pp_header" /> + android:text="@string/tos_pp_google" /> - - - - - - - - + android:text="@string/tos_pp_firebase" /> + android:text="@string/tos_pp_none" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c500e7614..7900a7013 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -40,13 +40,10 @@ Google None - Terms of Service URL - Google - Firebase - - Privacy Policy URL - Google - Firebase + Terms of Service and Privacy Policy + Google + Firebase + None Example extra Google scopes Drive File diff --git a/auth-github/build.gradle.kts b/auth-github/build.gradle.kts index 2532967a2..dc0dd854c 100644 --- a/auth-github/build.gradle.kts +++ b/auth-github/build.gradle.kts @@ -1,3 +1,9 @@ +android { + lintOptions { + disable("UnknownNullness") // TODO fix in future PR + } +} + dependencies { compileOnly(project(":auth")) { isTransitive = false } compileOnly(Config.Libs.Firebase.auth) { isTransitive = false } diff --git a/auth/README.md b/auth/README.md index 847d0f7a6..4fe4fc52f 100644 --- a/auth/README.md +++ b/auth/README.md @@ -65,10 +65,10 @@ Gradle, add the dependency: ```groovy dependencies { // ... - implementation 'com.firebaseui:firebase-ui-auth:4.2.0' + implementation 'com.firebaseui:firebase-ui-auth:4.2.1' // Required only if GitHub OAuth support is required - implementation 'com.firebaseui:firebase-ui-auth-github:4.2.0' + implementation 'com.firebaseui:firebase-ui-auth-github:4.2.1' // Required only if Facebook login support is required // Find the latest Facebook SDK releases here: https://goo.gl/Ce5L94 diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java index 488c1777b..d358abd36 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java +++ b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java @@ -1025,6 +1025,7 @@ private abstract class AuthIntentBuilder { final List mProviders = new ArrayList<>(); String mTosUrl; String mPrivacyPolicyUrl; + boolean mAlwaysShowProviderChoice = false; boolean mEnableCredentials = true; boolean mEnableHints = true; @@ -1156,6 +1157,19 @@ public T setIsSmartLockEnabled(boolean enableCredentials, boolean enableHints) { return (T) this; } + /** + * Forces the sign-in method choice screen to always show, even if there is only + * a single provider configured. + *

+ *

This is false by default. + * @param alwaysShow if true, force the sign-in choice screen to show. + */ + @NonNull + public T setAlwaysShowSignInMethodScreen(boolean alwaysShow) { + mAlwaysShowProviderChoice = alwaysShow; + return (T) this; + } + @CallSuper @NonNull public Intent build() { @@ -1201,7 +1215,8 @@ protected FlowParameters getFlowParams() { mPrivacyPolicyUrl, mEnableCredentials, mEnableHints, - mEnableAnonymousUpgrade); + mEnableAnonymousUpgrade, + mAlwaysShowProviderChoice); } } } diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java b/auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java index ee088ebfc..c05398360 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java @@ -57,6 +57,7 @@ public class FlowParameters implements Parcelable { public final boolean enableCredentials; public final boolean enableHints; public final boolean enableAnonymousUpgrade; + public final boolean alwaysShowProviderChoice; public FlowParameters( @NonNull String appName, @@ -67,7 +68,8 @@ public FlowParameters( @Nullable String privacyPolicyUrl, boolean enableCredentials, boolean enableHints, - boolean enableAnonymousUpgrade) { + boolean enableAnonymousUpgrade, + boolean alwaysShowProviderChoice) { this.appName = Preconditions.checkNotNull(appName, "appName cannot be null"); this.providers = Collections.unmodifiableList( Preconditions.checkNotNull(providers, "providers cannot be null")); @@ -78,6 +80,7 @@ public FlowParameters( this.enableCredentials = enableCredentials; this.enableHints = enableHints; this.enableAnonymousUpgrade = enableAnonymousUpgrade; + this.alwaysShowProviderChoice = alwaysShowProviderChoice; } /** @@ -98,6 +101,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeInt(enableCredentials ? 1 : 0); dest.writeInt(enableHints ? 1 : 0); dest.writeInt(enableAnonymousUpgrade ? 1 : 0); + dest.writeInt(alwaysShowProviderChoice ? 1 : 0); } @Override @@ -117,6 +121,7 @@ public FlowParameters createFromParcel(Parcel in) { boolean enableCredentials = in.readInt() != 0; boolean enableHints = in.readInt() != 0; boolean enableAnonymousUpgrade = in.readInt() != 0; + boolean alwaysShowProviderChoice = in.readInt() != 0; return new FlowParameters( appName, @@ -127,7 +132,8 @@ public FlowParameters createFromParcel(Parcel in) { privacyPolicyUrl, enableCredentials, enableHints, - enableAnonymousUpgrade); + enableAnonymousUpgrade, + alwaysShowProviderChoice); } @Override @@ -151,4 +157,8 @@ public boolean isPrivacyPolicyUrlProvided() { public boolean isAnonymousUpgradeEnabled() { return enableAnonymousUpgrade; } + + public boolean shouldShowProviderChoice() { + return !isSingleProviderFlow() || alwaysShowProviderChoice; + } } diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java index cba8420d6..5376d1bde 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.java @@ -96,7 +96,7 @@ public void onComplete(@NonNull Task task) { private void startAuthMethodChoice() { // If there is only one provider selected, launch the flow directly - if (getArguments().isSingleProviderFlow()) { + if (!getArguments().shouldShowProviderChoice()) { AuthUI.IdpConfig firstIdpConfig = getArguments().providers.get(0); String firstProvider = firstIdpConfig.getProviderId(); switch (firstProvider) { diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java index 3a017d0bb..658f73530 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java @@ -8,6 +8,7 @@ import android.support.annotation.RestrictTo; import android.support.v7.app.AppCompatActivity; +import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.ErrorCodes; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.data.model.FlowParameters; @@ -29,11 +30,13 @@ protected static Intent createBaseIntent( @NonNull Context context, @NonNull Class target, @NonNull FlowParameters flowParams) { - return new Intent( + Intent intent = new Intent( checkNotNull(context, "context cannot be null"), checkNotNull(target, "target activity cannot be null")) .putExtra(ExtraConstants.FLOW_PARAMS, checkNotNull(flowParams, "flowParams cannot be null")); + intent.setExtrasClassLoader(AuthUI.class.getClassLoader()); + return intent; } @Override 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 c84d27878..220b85b32 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 @@ -113,7 +113,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text); FlowParameters flowParameters = getFlowParams(); - if (flowParameters.isSingleProviderFlow()) { + if (!flowParameters.shouldShowProviderChoice()) { PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicyText(requireContext(), flowParameters, termsText); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java index 3ed8b5553..1bc7d4444 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.java @@ -132,6 +132,12 @@ protected void onFailure(@NonNull Exception e) { PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicyText(this, getFlowParams(), termsText); + + // No ToS or PP provided, so we should hide the view entirely + if (!getFlowParams().isPrivacyPolicyUrlProvided() && + !getFlowParams().isTermsOfServiceUrlProvided()) { + termsText.setVisibility(View.GONE); + } } private void populateIdpList(List providerConfigs, diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneNumberFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneNumberFragment.java index 93ed3a9fc..fbd398909 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneNumberFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneNumberFragment.java @@ -173,7 +173,7 @@ private String getPseudoValidPhoneNumber() { private void setupPrivacyDisclosures(TextView footerText) { FlowParameters params = getFlowParams(); - if (params.isSingleProviderFlow()) { + if (!params.shouldShowProviderChoice()) { PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicySmsText(requireContext(), params, mSmsTermsText); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListAdapter.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListAdapter.java index 17f22df9f..da2336e43 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListAdapter.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListAdapter.java @@ -34,8 +34,13 @@ * Array adapter used to display a list of countries with section indices. */ final class CountryListAdapter extends ArrayAdapter implements SectionIndexer { + + // Map from first letter --> position in the list private final HashMap alphaIndex = new LinkedHashMap<>(); + + // Map from display name --> position in the list private final HashMap countryPosition = new LinkedHashMap<>(); + private String[] sections; public CountryListAdapter(Context context) { @@ -67,6 +72,11 @@ public void setData(List countries) { notifyDataSetChanged(); } + @Override + public int getCount() { + return countryPosition.size(); + } + @Override public Object[] getSections() { return sections; @@ -92,6 +102,16 @@ public int getPositionForSection(int index) { @Override public int getSectionForPosition(int position) { + if (sections == null) { + return 0; + } + + for (int i = 0; i < sections.length; i++) { + if (getPositionForSection(i) > position) { + return i - 1; + } + } + return 0; } diff --git a/auth/src/main/res/drawable-v21/fui_idp_button_background_anonymous.xml b/auth/src/main/res/drawable-v21/fui_idp_button_background_anonymous.xml new file mode 100644 index 000000000..379b2c09c --- /dev/null +++ b/auth/src/main/res/drawable-v21/fui_idp_button_background_anonymous.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/auth/src/main/res/drawable/fui_ic_anonymous_white_24dp.xml b/auth/src/main/res/drawable/fui_ic_anonymous_white_24dp.xml index 212818c29..af6df65ba 100644 --- a/auth/src/main/res/drawable/fui_ic_anonymous_white_24dp.xml +++ b/auth/src/main/res/drawable/fui_ic_anonymous_white_24dp.xml @@ -1,12 +1,10 @@ + android:viewportWidth="24.0"> + android:pathData="M12,5.9c1.16,0 2.1,0.94 2.1,2.1s-0.94,2.1 -2.1,2.1S9.9,9.16 9.9,8s0.94,-2.1 2.1,-2.1m0,9c2.97,0 6.1,1.46 6.1,2.1v1.1L5.9,18.1L5.9,17c0,-0.64 3.13,-2.1 6.1,-2.1M12,4C9.79,4 8,5.79 8,8s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,13c-2.67,0 -8,1.34 -8,4v3h16v-3c0,-2.66 -5.33,-4 -8,-4z" /> diff --git a/auth/src/main/res/layout-land/fui_auth_method_picker_layout.xml b/auth/src/main/res/layout-land/fui_auth_method_picker_layout.xml index 646d3cbda..8b2c4fcb4 100644 --- a/auth/src/main/res/layout-land/fui_auth_method_picker_layout.xml +++ b/auth/src/main/res/layout-land/fui_auth_method_picker_layout.xml @@ -15,14 +15,22 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> + + app:layout_constraintVertical_weight="2" + tools:background="#bfbfbf" + tools:ignore="ContentDescription" + tools:layout_height="100dp" + tools:layout_width="100dp" /> + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintVertical_weight="1" + tools:text="By continuing you are indicating that you accept our Terms of Service and Privacy Policy" /> + style="@style/FirebaseUI.AuthMethodPicker.ButtonHolder" + tools:background="#bfbfbf" + tools:layout_height="250dp" /> diff --git a/auth/src/main/res/layout/fui_auth_method_picker_layout.xml b/auth/src/main/res/layout/fui_auth_method_picker_layout.xml index e5d558340..4bac31ba1 100644 --- a/auth/src/main/res/layout/fui_auth_method_picker_layout.xml +++ b/auth/src/main/res/layout/fui_auth_method_picker_layout.xml @@ -14,6 +14,8 @@ app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> + + tools:background="#bfbfbf" + tools:ignore="ContentDescription" + tools:layout_height="100dp" + tools:layout_width="100dp" /> + android:gravity="bottom" + tools:background="#bfbfbf" + tools:layout_height="250dp" /> @@ -49,12 +57,16 @@ style="@style/FirebaseUI.Text.BodyText" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/fui_field_padding_vert" + android:paddingStart="8dp" + android:paddingEnd="8dp" android:layout_marginBottom="@dimen/fui_field_padding_vert" android:gravity="center" android:textColor="?android:textColorTertiary" android:textIsSelectable="true" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/container" - app:layout_constraintBottom_toBottomOf="parent" /> + app:layout_constraintBottom_toBottomOf="parent" + tools:text="By continuing you are indicating that you accept our Terms of Service and Privacy Policy" /> diff --git a/auth/src/main/res/layout/fui_check_email_layout.xml b/auth/src/main/res/layout/fui_check_email_layout.xml index 1e588c3f0..8e15f4b48 100644 --- a/auth/src/main/res/layout/fui_check_email_layout.xml +++ b/auth/src/main/res/layout/fui_check_email_layout.xml @@ -23,6 +23,7 @@