diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 000000000..d5eefc3f2 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,20 @@ +name: Android CI + +on: + - pull_request + - push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Build with Gradle + run: ./scripts/build.sh + - name: Print Logs + if: failure() + run: ./scripts/print_build_logs.sh \ No newline at end of file diff --git a/.github/workflows/artifactory.yml b/.github/workflows/artifactory.yml new file mode 100644 index 000000000..c98f703d6 --- /dev/null +++ b/.github/workflows/artifactory.yml @@ -0,0 +1,21 @@ +name: Artifactory + +on: + push: + branches: + - 'version-*-dev' + +jobs: + artifactory: + runs-on: ubuntu-latest + env: + BINTRAY_USER: ${{ secrets.BINTRAY_USER }} + BINTRAY_KEY: ${{ secrets.BINTRAY_KEY }} + steps: + - uses: actions/checkout@v2 + - name: set up JDK 1.8 + uses: actions/setup-java@v1 + with: + java-version: 1.8 + - name: Publish to Artifactory + run: ./scripts/artifactory.sh \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 615c5e52f..000000000 --- a/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: android -jdk: oraclejdk8 -sudo: required # See https://github.com/travis-ci/travis-ci/issues/5582 -env: - global: - - secure: AeYETdNH/qdqvc3QScrZYJgV4Gupkj0LKeCpUP96I8DJBGmxZQeRWMI+mjtJzksTddnYnRQCU189UCJJJk8MH79cZQsvLAc9M8/f7nnX2s54NG2KZZV08t8xvlCS/XUmkwY6fCQsIoDAGA/+35pHp+pRU3j3gq/qxHtWWIJglt+Oew+fz5aHAO711EtnjuHwwFcc0WTPFDhOlo7goe0myAPG0I09g1yaW2odj1lQrej9bLOCvs6SJ6RRD/+4ZNLMw+r8Q662SGztOh//uCAZU67KD6FCst9AEgfECB6GPyN/UCr0rV/7HTU0rcjthr4QbNaCFPZ3hNCy13Wh7gPUM5quh481bgvx5YTtZCfKhAz0m53mkYseUlemlB7R54tOkcIezsByMhF8In2bOwVUD8JZmkhFb3bBDeao6g63defta0WIMlyYAWNxIdz//JDs40MCkOEj8s/VehvdOOnYUjwcXFsmSfqZP0Vf3Dpin51lxz1h+tSOMI5RmWEg5WICsIo/W7nvV85zgtd6/dWxunhDgKrhBpwrJsIrsOz2fDNIA9DB6BCAjYFK5rd/E4Ycww0Y8/Lxlqht39IJTSCApT17fNtFbF357z9fiKBeNXoaRIULPKXXPMMkHHPRai9RFYdCf8sCZ75aVkBJ4PsWexgBU+cTjHuw0saao68eS5s= - - secure: bPFo+6ADLN/iAZmhHBYIexE0zuiXQT5M4RA20/t0QSh6EZ8DCtbrLy8jT3L3YU1rge1Qxv8J258GxN3N6UelI/dg96e15dHUX1+8dZCx/txxPBe/q4YamaKwwJForXje/5bqMz2YqdT1e50ScxKVXo/YRWF26hynNGsq916ATKjwM9GRUep7A6aa7EKrCvuXwELzUZqV/gfA8KMD+s03T+9vpgsxfGv6tnp5lpHeR5dDtI4wFcTPLkoRfhbuIoUTBBRaHbNJpHm8fBhTQTpGTG5pxC7QVkIbArdgIwptv3XeTn/7iPdv0zcMXnof4C8hJ3tguitR3q4ae5wS/QZGi8wxpL7L/tLkeswLpkjflbIBj4/U+nUS2DayVisu4zSTSKR1A6ARvlWFhSEYxhjp9i8z45uytCod22PeXg939OUK2IosNpwychWGCab7fVECOqlwruFIwi7Tu25bng6IWtD7zipFtMrBK2JY9ykLjJx5InXN3T2OJsq3V/g6dNRCF3isIUhxWMkYqK+bMTRbcQRA55Tb3kvZ9x1GEOVLUhwtzYdFWDtRN6MlJkhrEvznL9hawa6AKYVf38wws65/K/scjtNpbkFlW0py1EBD1VSAJSl7nThZJPRIH64Uv3FbYztNR3a6rk54L3WBc53svpNcesPU/M2I/BzwUsjfvIM= -before_cache: - - rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - - rm -fr $HOME/.gradle/caches/*/plugin-resolution/ -cache: - directories: - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache -before_install: - - yes | sdkmanager "platforms;android-28" - - mkdir "$ANDROID_HOME/licenses" || true - - echo -e "\n8933bad161af4178b1185d1a37fbf41ea5269c55\n24333f8a63b6825ea9c5514f83c2829b004d1fee" > "$ANDROID_HOME/licenses/android-sdk-license" - - echo -e "\n84831b9409646a918e30573bab4c9c91346d8abd\n504667f4c0de7af1a06de9f4b1727b84351f2910" > "$ANDROID_HOME/licenses/android-sdk-preview-license" -android: - components: - # https://github.com/travis-ci/travis-ci/issues/6040#issuecomment-219367943 - - tools - - tools -before_script: - - cp library/google-services.json app/google-services.json - - cp library/google-services.json proguard-tests/google-services.json -script: - - ./gradlew clean - - ./gradlew assembleDebug proguard-tests:build check -after_success: ./scripts/artifactory.sh -after_failure: - # tests - - cat app/build/reports/tests/testDebugUnitTest/index.html - - cat auth/build/reports/tests/testDebugUnitTest/index.html - - cat database/build/reports/tests/testDebugUnitTest/index.html - - cat storage/build/reports/tests/testDebugUnitTest/index.html - - # app - - cat app/build/reports/checkstyle.html - - cat app/build/reports/lint-results.xml - - cat app/build/reports/lint-results.html - - cat app/build/reports/findbugs.html - - cat app/build/reports/pmd.html diff --git a/README.md b/README.md index 20be02625..edaa0aef8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ [![FirebaseOpensource.com](https://img.shields.io/badge/Docs-firebaseopensource.com-orange.svg)]( https://firebaseopensource.com/projects/firebase/firebaseui-android ) -[![Build Status](https://travis-ci.org/firebase/FirebaseUI-Android.svg?branch=master)](https://travis-ci.org/firebase/FirebaseUI-Android) +[![Actions Status][gh-actions-badge]][gh-actions] FirebaseUI is an open-source library for Android that allows you to quickly connect common UI elements to [Firebase](https://firebase.google.com) APIs. @@ -48,16 +48,16 @@ libraries. ```groovy dependencies { // FirebaseUI for Firebase Realtime Database - implementation 'com.firebaseui:firebase-ui-database:6.2.0' + implementation 'com.firebaseui:firebase-ui-database:6.2.1' // FirebaseUI for Cloud Firestore - implementation 'com.firebaseui:firebase-ui-firestore:6.2.0' + implementation 'com.firebaseui:firebase-ui-firestore:6.2.1' // FirebaseUI for Firebase Auth - implementation 'com.firebaseui:firebase-ui-auth:6.2.0' + implementation 'com.firebaseui:firebase-ui-auth:6.2.1' // FirebaseUI for Cloud Storage - implementation 'com.firebaseui:firebase-ui-storage:6.2.0' + implementation 'com.firebaseui:firebase-ui-storage:6.2.1' } ``` @@ -241,3 +241,6 @@ accept your pull requests. you are contributing. 1. Ensure that your code has an appropriate set of unit tests which all pass. 1. Submit a pull request targeting the latest dev branch. + +[gh-actions]: https://github.com/firebase/FirebaseUI-Android/actions +[gh-actions-badge]: https://github.com/firebase/FirebaseUI-Android/workflows/Android%20CI/badge.svg \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 38eb718fe..1f1af68fa 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,7 +32,6 @@ android { } dependencies { - implementation(Config.Libs.Firebase.core) implementation(Config.Libs.Androidx.design) implementation(Config.Libs.Androidx.multidex) diff --git a/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java b/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java index 321487012..687dcd564 100644 --- a/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java +++ b/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java @@ -2,9 +2,14 @@ import com.squareup.leakcanary.LeakCanary; +import androidx.appcompat.app.AppCompatDelegate; import androidx.multidex.MultiDexApplication; public class FirebaseUIDemo extends MultiDexApplication { + static { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); + } + @Override public void onCreate() { super.onCreate(); 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 13cf6d00e..261b80447 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -347,7 +347,7 @@ private void startSignedInActivity(@Nullable IdpResponse response) { @OnClick({R.id.default_theme, R.id.purple_theme, R.id.green_theme, R.id.dark_theme}) public void toggleDarkTheme() { int mode = mDarkTheme.isChecked() ? - AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO; + AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY; AppCompatDelegate.setDefaultNightMode(mode); getDelegate().setLocalNightMode(mode); } diff --git a/auth/README.md b/auth/README.md index c9d97495a..70d89d00e 100644 --- a/auth/README.md +++ b/auth/README.md @@ -65,7 +65,7 @@ Gradle, add the dependency: ```groovy dependencies { // ... - implementation 'com.firebaseui:firebase-ui-auth:6.2.0' + implementation 'com.firebaseui:firebase-ui-auth:6.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/build.gradle.kts b/auth/build.gradle.kts index 3b62a9845..53931f7e3 100644 --- a/auth/build.gradle.kts +++ b/auth/build.gradle.kts @@ -34,6 +34,7 @@ dependencies { implementation(Config.Libs.Androidx.lifecycleExtensions) annotationProcessor(Config.Libs.Androidx.lifecycleCompiler) + implementation(platform(Config.Libs.Firebase.bom)) api(Config.Libs.Firebase.auth) api(Config.Libs.PlayServices.auth) 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 2b51a077d..06d10c7a6 100644 --- a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java +++ b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java @@ -487,7 +487,6 @@ public SignInIntentBuilder createSignInIntentBuilder() { ANONYMOUS_PROVIDER, EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD }) - @Retention(RetentionPolicy.SOURCE) public @interface SupportedProvider { } @@ -996,7 +995,8 @@ public GoogleBuilder() { @NonNull public GoogleBuilder setScopes(@NonNull List scopes) { GoogleSignInOptions.Builder builder = - new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN); + new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) + .requestEmail(); for (String scope : scopes) { builder.requestScopes(new Scope(scope)); } diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/FacebookSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/FacebookSignInHandler.java index b32b5c250..9bc574785 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/remote/FacebookSignInHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/remote/FacebookSignInHandler.java @@ -20,6 +20,7 @@ import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.data.model.Resource; import com.firebase.ui.auth.data.model.User; +import com.firebase.ui.auth.data.model.UserCancellationException; import com.firebase.ui.auth.ui.HelperActivityBase; import com.firebase.ui.auth.util.ExtraConstants; import com.firebase.ui.auth.viewmodel.ProviderSignInBase; @@ -111,7 +112,7 @@ public void onSuccess(LoginResult result) { @Override public void onCancel() { - onError(new FacebookException()); + setResult(Resource.forFailure(new UserCancellationException())); } @Override diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpSignInHandler.java index a0db313a0..c887f07f3 100644 --- a/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpSignInHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpSignInHandler.java @@ -22,6 +22,7 @@ import com.firebase.ui.auth.data.model.UserCancellationException; import com.firebase.ui.auth.ui.HelperActivityBase; import com.firebase.ui.auth.util.ExtraConstants; +import com.firebase.ui.auth.util.FirebaseAuthError; import com.firebase.ui.auth.util.data.AuthOperationManager; import com.firebase.ui.auth.util.data.ProviderUtils; import com.firebase.ui.auth.viewmodel.ProviderSignInBase; @@ -32,6 +33,7 @@ import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.FirebaseAuthUserCollisionException; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.OAuthCredential; @@ -91,17 +93,27 @@ public void onSuccess(@NonNull AuthResult authResult) { new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - if (e instanceof FirebaseAuthUserCollisionException) { - FirebaseAuthUserCollisionException collisionException = - (FirebaseAuthUserCollisionException) e; - - setResult(Resource.forFailure( - new FirebaseUiUserCollisionException( - ErrorCodes.ERROR_GENERIC_IDP_RECOVERABLE_ERROR, - "Recoverable error.", - provider.getProviderId(), - collisionException.getEmail(), - collisionException.getUpdatedCredential()))); + if (e instanceof FirebaseAuthException) { + FirebaseAuthError error = + FirebaseAuthError.fromException((FirebaseAuthException) e); + + if (e instanceof FirebaseAuthUserCollisionException) { + FirebaseAuthUserCollisionException collisionException = + (FirebaseAuthUserCollisionException) e; + + setResult(Resource.forFailure( + new FirebaseUiUserCollisionException( + ErrorCodes.ERROR_GENERIC_IDP_RECOVERABLE_ERROR, + "Recoverable error.", + provider.getProviderId(), + collisionException.getEmail(), + collisionException.getUpdatedCredential()))); + } else if (error == FirebaseAuthError.ERROR_WEB_CONTEXT_CANCELED) { + setResult(Resource.forFailure( + new UserCancellationException())); + } else { + setResult(Resource.forFailure(e)); + } } else { setResult(Resource.forFailure(e)); } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCatcherActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCatcherActivity.java index 6ac1a44c4..f5abccc29 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCatcherActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCatcherActivity.java @@ -128,6 +128,7 @@ public void onClick(DialogInterface dialog, int id) { @Override public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); if (requestCode == RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW || requestCode == RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW) { IdpResponse response = IdpResponse.fromResultIntent(data); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCrossDeviceLinkingFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCrossDeviceLinkingFragment.java index a80320923..d22da246f 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCrossDeviceLinkingFragment.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCrossDeviceLinkingFragment.java @@ -1,5 +1,6 @@ package com.firebase.ui.auth.ui.email; +import android.annotation.SuppressLint; import android.os.Build; import android.os.Bundle; import android.text.SpannableStringBuilder; @@ -22,8 +23,6 @@ import androidx.annotation.RestrictTo; import androidx.fragment.app.FragmentActivity; -import static android.text.Layout.JUSTIFICATION_MODE_INTER_WORD; - /** * Fragment that tells the user that a linking flow cannot be completed as they have opened the * email link on a different device. @@ -52,6 +51,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, @SuppressWarnings("WrongConstant") @Override + @SuppressLint("WrongConstant") public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { mProgressBar = view.findViewById(R.id.top_progress_bar); mContinueButton = view.findViewById(R.id.button_continue); @@ -73,7 +73,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat // Justifies the text if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - body.setJustificationMode(JUSTIFICATION_MODE_INTER_WORD); + body.setJustificationMode(android.text.Layout.JUSTIFICATION_MODE_INTER_WORD); } TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackEmailLinkPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackEmailLinkPrompt.java index fb69a62dc..8ed47079b 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackEmailLinkPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackEmailLinkPrompt.java @@ -1,5 +1,6 @@ package com.firebase.ui.auth.ui.email; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.os.Build; @@ -22,8 +23,6 @@ import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; -import static android.text.Layout.JUSTIFICATION_MODE_INTER_WORD; - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public class WelcomeBackEmailLinkPrompt extends AppCompatBase implements View.OnClickListener { @@ -85,7 +84,7 @@ private void setBodyText() { body.setText(spannableStringBuilder); // Justifies the text if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - body.setJustificationMode(JUSTIFICATION_MODE_INTER_WORD); + body.setJustificationMode(android.text.Layout.JUSTIFICATION_MODE_INTER_WORD); } } diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPrompt.java index 88d7a7b8a..33e01bbd6 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPrompt.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPrompt.java @@ -28,11 +28,13 @@ import com.firebase.ui.auth.ErrorCodes; import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException; +import com.firebase.ui.auth.FirebaseUiException; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; import com.firebase.ui.auth.data.model.FlowParameters; import com.firebase.ui.auth.ui.AppCompatBase; import com.firebase.ui.auth.util.ExtraConstants; +import com.firebase.ui.auth.util.FirebaseAuthError; import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils; import com.firebase.ui.auth.util.data.ProviderUtils; import com.firebase.ui.auth.util.ui.ImeHelper; @@ -41,7 +43,10 @@ import com.firebase.ui.auth.viewmodel.email.WelcomeBackPasswordHandler; import com.google.android.material.textfield.TextInputLayout; import com.google.firebase.auth.AuthCredential; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException; +import com.google.firebase.auth.FirebaseAuthInvalidUserException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -120,9 +125,21 @@ protected void onFailure(@NonNull Exception e) { if (e instanceof FirebaseAuthAnonymousUpgradeException) { IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse(); finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent()); - } else { - mPasswordLayout.setError(getString(getErrorMessage(e))); + return; } + + if (e instanceof FirebaseAuthException) { + FirebaseAuthException authEx = (FirebaseAuthException) e; + FirebaseAuthError error = FirebaseAuthError.fromException(authEx); + if (error == FirebaseAuthError.ERROR_USER_DISABLED) { + IdpResponse resp = IdpResponse.from( + new FirebaseUiException(ErrorCodes.ERROR_USER_DISABLED)); + finish(RESULT_CANCELED, resp.toIntent()); + return; + } + } + + mPasswordLayout.setError(getString(getErrorMessage(e))); } }); 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 72f507fac..5c467cae0 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 @@ -163,12 +163,19 @@ protected void onSuccess(@NonNull IdpResponse response) { @Override protected void onFailure(@NonNull Exception e) { + if (e instanceof UserCancellationException) { + // User pressed back, there is no error. + return; + } + if (e instanceof FirebaseAuthAnonymousUpgradeException) { finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, ((FirebaseAuthAnonymousUpgradeException) e).getResponse().toIntent()); - } else if ((!(e instanceof UserCancellationException))) { - String text = e instanceof FirebaseUiException ? e.getMessage() : - getString(R.string.fui_error_unknown); + } else if (e instanceof FirebaseUiException) { + FirebaseUiException fue = (FirebaseUiException) e; + finish(RESULT_CANCELED, IdpResponse.from(fue).toIntent()); + } else { + String text = getString(R.string.fui_error_unknown); Toast.makeText(AuthMethodPickerActivity.this, text, Toast.LENGTH_SHORT).show(); @@ -223,7 +230,7 @@ private void populateIdpListCustomLayout(List providerConfigs) { final String providerId = providerOrEmailLinkProvider(idpConfig.getProviderId()); if (!providerButtonIds.containsKey(providerId)) { - throw new IllegalStateException("No button found for auth provider: " + providerId); + throw new IllegalStateException("No button found for auth provider: " + idpConfig.getProviderId()); } @IdRes int buttonId = providerButtonIds.get(providerId); diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java index 3f27fdbba..926526eaa 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java @@ -55,8 +55,8 @@ public final class CountryListSpinner extends AppCompatEditText implements View. private String mSelectedCountryName; private CountryInfo mSelectedCountryInfo; - private Set mWhitelistedCountryIsos; - private Set mBlacklistedCountryIsos; + private Set mWhitelistedCountryIsos = new HashSet<>(); + private Set mBlacklistedCountryIsos = new HashSet<>(); public CountryListSpinner(Context context) { this(context, null, android.R.attr.spinnerStyle); @@ -86,11 +86,11 @@ public void init(Bundle params) { private List getCountriesToDisplayInSpinner(Bundle params) { initCountrySpinnerIsosFromParams(params); - Map countryInfoMap = PhoneNumberUtils.getImmutableCountryIsoMap(); + // We consider all countries to be whitelisted if there are no whitelisted // or blacklisted countries given as input. - if (mWhitelistedCountryIsos == null && mBlacklistedCountryIsos == null) { + if (mWhitelistedCountryIsos.isEmpty() && mBlacklistedCountryIsos.isEmpty()) { this.mWhitelistedCountryIsos = new HashSet<>(countryInfoMap.keySet()); } @@ -100,7 +100,7 @@ private List getCountriesToDisplayInSpinner(Bundle params) { // We assume no countries are to be excluded. Here, we correct this assumption based on the // contents of either lists. Set excludedCountries = new HashSet<>(); - if (mWhitelistedCountryIsos == null) { + if (!mBlacklistedCountryIsos.isEmpty()) { // Exclude all countries in the mBlacklistedCountryIsos list. excludedCountries.addAll(mBlacklistedCountryIsos); } else { @@ -129,7 +129,9 @@ private void initCountrySpinnerIsosFromParams(@NonNull Bundle params) { if (whitelistedCountries != null) { mWhitelistedCountryIsos = convertCodesToIsos(whitelistedCountries); - } else if (blacklistedCountries != null) { + } + + if (blacklistedCountries != null) { mBlacklistedCountryIsos = convertCodesToIsos(blacklistedCountries); } } @@ -164,9 +166,16 @@ private void setDefaultCountryForSpinner(List countries) { public boolean isValidIso(String iso) { iso = iso.toUpperCase(Locale.getDefault()); - return ((mWhitelistedCountryIsos == null && mBlacklistedCountryIsos == null) - || (mWhitelistedCountryIsos != null && mWhitelistedCountryIsos.contains(iso)) - || (mBlacklistedCountryIsos != null && !mBlacklistedCountryIsos.contains(iso))); + boolean valid = true; + if (!mWhitelistedCountryIsos.isEmpty()) { + valid = valid && mWhitelistedCountryIsos.contains(iso); + } + + if (!mBlacklistedCountryIsos.isEmpty()) { + valid = valid && !mBlacklistedCountryIsos.contains(iso); + } + + return valid; } @Override diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java index bcc17ab5d..d4a178fbc 100644 --- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java +++ b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java @@ -21,6 +21,7 @@ import com.firebase.ui.auth.ErrorCodes; import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException; +import com.firebase.ui.auth.FirebaseUiException; import com.firebase.ui.auth.IdpResponse; import com.firebase.ui.auth.R; import com.firebase.ui.auth.data.model.FlowParameters; @@ -33,6 +34,7 @@ import com.firebase.ui.auth.viewmodel.ResourceObserver; import com.firebase.ui.auth.viewmodel.phone.PhoneProviderResponseHandler; import com.google.android.material.textfield.TextInputLayout; +import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.PhoneAuthProvider; @@ -159,8 +161,14 @@ private void handleError(@Nullable Exception e) { IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse(); finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent()); } else if (e instanceof FirebaseAuthException) { - errorView.setError(getErrorMessage( - FirebaseAuthError.fromException((FirebaseAuthException) e))); + FirebaseAuthError error = FirebaseAuthError.fromException((FirebaseAuthException) e); + if (error == FirebaseAuthError.ERROR_USER_DISABLED) { + IdpResponse response = IdpResponse.from( + new FirebaseUiException(ErrorCodes.ERROR_USER_DISABLED)); + finish(RESULT_CANCELED, response.toIntent()); + return; + } + errorView.setError(getErrorMessage(error)); } else if (e != null) { errorView.setError(e.getLocalizedMessage()); } else { diff --git a/auth/src/main/java/com/firebase/ui/auth/util/CredentialUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/CredentialUtils.java index 1c3663774..df318ad64 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/CredentialUtils.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/CredentialUtils.java @@ -2,6 +2,7 @@ import android.net.Uri; import android.text.TextUtils; +import android.util.Log; import com.firebase.ui.auth.IdpResponse; import com.google.android.gms.auth.api.credentials.Credential; @@ -16,6 +17,9 @@ */ @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) public class CredentialUtils { + + private static final String TAG = "CredentialUtils"; + private CredentialUtils() { throw new AssertionError("No instance for you!"); } @@ -35,8 +39,14 @@ public static Credential buildCredential(@NonNull FirebaseUser user, Uri profilePictureUri = user.getPhotoUrl() == null ? null : Uri.parse(user.getPhotoUrl().toString()); - if (TextUtils.isEmpty(email) && TextUtils.isEmpty(phone)) { return null; } - if (password == null && accountType == null) { return null; } + if (TextUtils.isEmpty(email) && TextUtils.isEmpty(phone)) { + Log.w(TAG, "User (accountType=" + accountType + ") has no email or phone number, cannot build credential."); + return null; + } + if (password == null && accountType == null) { + Log.w(TAG, "User has no accountType or password, cannot build credential."); + return null; + } Credential.Builder builder = new Credential.Builder(TextUtils.isEmpty(email) ? phone : email) diff --git a/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthError.java b/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthError.java index 0f8ef2597..206314b8f 100644 --- a/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthError.java +++ b/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthError.java @@ -2,6 +2,7 @@ import com.google.firebase.auth.FirebaseAuthException; +import androidx.annotation.NonNull; import androidx.annotation.RestrictTo; /** @@ -83,6 +84,8 @@ public enum FirebaseAuthError { ERROR_API_NOT_AVAILABLE("The API that you are calling is not available on devices without Google Play Services."), + ERROR_WEB_CONTEXT_CANCELED("The web operation was canceled by the user"), + ERROR_UNKNOWN("An unknown error occurred."); /** diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/BaselineTextInputLayout.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/BaselineTextInputLayout.java deleted file mode 100644 index 4e4d55e7b..000000000 --- a/auth/src/main/java/com/firebase/ui/auth/util/ui/BaselineTextInputLayout.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.firebase.ui.auth.util.ui; - -import android.content.Context; -import android.util.AttributeSet; -import android.widget.EditText; - -import com.google.android.material.textfield.TextInputLayout; - -import androidx.annotation.RestrictTo; - -@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -public class BaselineTextInputLayout extends TextInputLayout { - public BaselineTextInputLayout(Context context) { - super(context); - } - - public BaselineTextInputLayout(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BaselineTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public int getBaseline() { - EditText text = getEditText(); - return text == null ? super.getBaseline() : text.getPaddingTop() + text.getBaseline(); - } -} diff --git a/auth/src/main/java/com/firebase/ui/auth/viewmodel/email/WelcomeBackPasswordHandler.java b/auth/src/main/java/com/firebase/ui/auth/viewmodel/email/WelcomeBackPasswordHandler.java index 0d26017f3..d1a2c97ee 100644 --- a/auth/src/main/java/com/firebase/ui/auth/viewmodel/email/WelcomeBackPasswordHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/viewmodel/email/WelcomeBackPasswordHandler.java @@ -7,6 +7,7 @@ import com.firebase.ui.auth.data.model.Resource; import com.firebase.ui.auth.data.model.User; import com.firebase.ui.auth.data.remote.ProfileMerger; +import com.firebase.ui.auth.util.FirebaseAuthError; import com.firebase.ui.auth.util.data.AuthOperationManager; import com.firebase.ui.auth.util.data.TaskFailureLogger; import com.firebase.ui.auth.viewmodel.SignInViewModelBase; @@ -19,6 +20,8 @@ import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.EmailAuthProvider; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseAuthException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; diff --git a/auth/src/main/java/com/firebase/ui/auth/viewmodel/idp/SocialProviderResponseHandler.java b/auth/src/main/java/com/firebase/ui/auth/viewmodel/idp/SocialProviderResponseHandler.java index fbb54a0e4..40eacacbe 100644 --- a/auth/src/main/java/com/firebase/ui/auth/viewmodel/idp/SocialProviderResponseHandler.java +++ b/auth/src/main/java/com/firebase/ui/auth/viewmodel/idp/SocialProviderResponseHandler.java @@ -15,6 +15,7 @@ import com.firebase.ui.auth.ui.email.WelcomeBackEmailLinkPrompt; import com.firebase.ui.auth.ui.email.WelcomeBackPasswordPrompt; import com.firebase.ui.auth.ui.idp.WelcomeBackIdpPrompt; +import com.firebase.ui.auth.util.FirebaseAuthError; import com.firebase.ui.auth.util.data.AuthOperationManager; import com.firebase.ui.auth.util.data.ProviderUtils; import com.firebase.ui.auth.viewmodel.RequestCodes; @@ -24,6 +25,8 @@ import com.google.firebase.auth.AuthCredential; import com.google.firebase.auth.AuthResult; import com.google.firebase.auth.EmailAuthProvider; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.FirebaseAuthInvalidUserException; import com.google.firebase.auth.FirebaseAuthUserCollisionException; import com.google.firebase.auth.PhoneAuthProvider; @@ -78,12 +81,28 @@ public void onSuccess(AuthResult result) { .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { - if (e instanceof FirebaseAuthUserCollisionException) { + // For some reason disabled users can hit FirebaseAuthUserCollisionException + // so we have to handle this special case. + boolean isDisabledUser = (e instanceof FirebaseAuthInvalidUserException); + if (e instanceof FirebaseAuthException) { + FirebaseAuthException authEx = (FirebaseAuthException) e; + FirebaseAuthError fae = FirebaseAuthError.fromException(authEx); + if (fae == FirebaseAuthError.ERROR_USER_DISABLED) { + isDisabledUser = true; + } + } + + if (isDisabledUser) { + setResult(Resource.forFailure( + new FirebaseUiException(ErrorCodes.ERROR_USER_DISABLED) + )); + } else if (e instanceof FirebaseAuthUserCollisionException) { final String email = response.getEmail(); if (email == null) { setResult(Resource.forFailure(e)); return; } + // There can be a collision due to: // CASE 1: Anon user signing in with a credential that belongs to an // existing user. @@ -119,15 +138,6 @@ public void onFailure(@NonNull Exception e) { e)); } }); - } else if (e instanceof FirebaseAuthInvalidUserException) { - setResult(Resource.forFailure( - new FirebaseUiException( - ErrorCodes.ERROR_USER_DISABLED, - ErrorCodes.toFriendlyMessage( - ErrorCodes.ERROR_USER_DISABLED - ) - ) - )); } } }); diff --git a/auth/src/main/res/layout/fui_phone_layout.xml b/auth/src/main/res/layout/fui_phone_layout.xml index eb3fb775d..aa86cf726 100644 --- a/auth/src/main/res/layout/fui_phone_layout.xml +++ b/auth/src/main/res/layout/fui_phone_layout.xml @@ -27,7 +27,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintBaseline_toBaselineOf="@+id/phone_layout" /> - - +