- * For a general intro to the RecyclerView, see Creating
- * Lists.
- */
-@SuppressLint("RestrictedApi")
-public class RealtimeDbChatActivity extends AppCompatActivity
- implements FirebaseAuth.AuthStateListener {
- private static final String TAG = "RealtimeDatabaseDemo";
-
- /**
- * Get the last 50 chat messages.
- */
- @NonNull
- protected final Query sChatQuery =
- FirebaseDatabase.getInstance().getReference().child("chats").limitToLast(50);
-
- private ActivityChatBinding mBinding;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mBinding = ActivityChatBinding.inflate(getLayoutInflater());
- setContentView(mBinding.getRoot());
-
- mBinding.messagesList.setHasFixedSize(true);
- mBinding.messagesList.setLayoutManager(new LinearLayoutManager(this));
-
- ImeHelper.setImeOnDoneListener(mBinding.messageEdit, () -> onSendClick());
-
- mBinding.sendButton.setOnClickListener(view -> onSendClick());
- }
-
- @Override
- public void onStart() {
- super.onStart();
- if (isSignedIn()) {
- attachRecyclerViewAdapter();
- }
- FirebaseAuth.getInstance().addAuthStateListener(this);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- FirebaseAuth.getInstance().removeAuthStateListener(this);
- }
-
- @Override
- public void onAuthStateChanged(@NonNull FirebaseAuth auth) {
- mBinding.sendButton.setEnabled(isSignedIn());
- mBinding.messageEdit.setEnabled(isSignedIn());
-
- if (isSignedIn()) {
- attachRecyclerViewAdapter();
- } else {
- Toast.makeText(this, R.string.signing_in, Toast.LENGTH_SHORT).show();
- auth.signInAnonymously().addOnCompleteListener(new SignInResultNotifier(this));
- }
- }
-
- private boolean isSignedIn() {
- return FirebaseAuth.getInstance().getCurrentUser() != null;
- }
-
- private void attachRecyclerViewAdapter() {
- final RecyclerView.Adapter adapter = newAdapter();
-
- // Scroll to bottom on new messages
- adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
- @Override
- public void onItemRangeInserted(int positionStart, int itemCount) {
- mBinding.messagesList.smoothScrollToPosition(adapter.getItemCount());
- }
- });
-
- mBinding.messagesList.setAdapter(adapter);
- }
-
- public void onSendClick() {
- String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
- String name = "User " + uid.substring(0, 6);
-
- onAddMessage(new Chat(name, mBinding.messageEdit.getText().toString(), uid));
-
- mBinding.messageEdit.setText("");
- }
-
- @NonNull
- protected RecyclerView.Adapter newAdapter() {
- FirebaseRecyclerOptions options =
- new FirebaseRecyclerOptions.Builder()
- .setQuery(sChatQuery, Chat.class)
- .setLifecycleOwner(this)
- .build();
-
- return new FirebaseRecyclerAdapter(options) {
- @Override
- public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- return new ChatHolder(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.message, parent, false));
- }
-
- @Override
- protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) {
- holder.bind(model);
- }
-
- @Override
- public void onDataChanged() {
- // If there are no chat messages, show a view that invites the user to add a message.
- mBinding.emptyTextView.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
- }
- };
- }
-
- protected void onAddMessage(@NonNull Chat chat) {
- sChatQuery.getRef().push().setValue(chat, (error, reference) -> {
- if (error != null) {
- Log.e(TAG, "Failed to write message", error.toException());
- }
- });
- }
-}
diff --git a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java b/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java
deleted file mode 100644
index c55b2cd24..000000000
--- a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.firebase.uidemo.database.realtime;
-
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.firebase.ui.database.FirebaseRecyclerAdapter;
-import com.firebase.ui.database.FirebaseRecyclerOptions;
-import com.firebase.uidemo.R;
-import com.firebase.uidemo.database.ChatHolder;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.database.DatabaseReference;
-import com.google.firebase.database.FirebaseDatabase;
-
-import androidx.annotation.NonNull;
-
-public class RealtimeDbChatIndexActivity extends RealtimeDbChatActivity {
- private DatabaseReference mChatIndicesRef;
-
- @NonNull
- @Override
- protected FirebaseRecyclerAdapter newAdapter() {
- mChatIndicesRef = FirebaseDatabase.getInstance()
- .getReference()
- .child("chatIndices")
- .child(FirebaseAuth.getInstance().getCurrentUser().getUid());
-
- FirebaseRecyclerOptions options =
- new FirebaseRecyclerOptions.Builder()
- .setIndexedQuery(
- mChatIndicesRef.limitToFirst(50), sChatQuery.getRef(), Chat.class)
- .setLifecycleOwner(this)
- .build();
-
- return new FirebaseRecyclerAdapter(options) {
- @NonNull
- @Override
- public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
- return new ChatHolder(LayoutInflater.from(parent.getContext())
- .inflate(R.layout.message, parent, false));
- }
-
- @Override
- protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) {
- holder.bind(model);
- }
-
- @Override
- public void onDataChanged() {
- // If there are no chat messages, show a view that invites the user to add a message.
- findViewById(R.id.emptyTextView).setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE);
- }
- };
- }
-
- @Override
- protected void onAddMessage(@NonNull Chat chat) {
- DatabaseReference chatRef = sChatQuery.getRef().push();
- mChatIndicesRef.child(chatRef.getKey()).setValue(true);
- chatRef.setValue(chat);
- }
-}
diff --git a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java
deleted file mode 100644
index bf2f57a80..000000000
--- a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.firebase.uidemo.storage;
-
-import android.Manifest;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
-import com.firebase.uidemo.BuildConfig;
-import com.firebase.uidemo.R;
-import com.firebase.uidemo.databinding.ActivityImageBinding;
-import com.firebase.uidemo.util.SignInResultNotifier;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.storage.FirebaseStorage;
-import com.google.firebase.storage.StorageReference;
-import com.google.firebase.storage.UploadTask;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.UUID;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.appcompat.app.AppCompatActivity;
-import pub.devrel.easypermissions.AfterPermissionGranted;
-import pub.devrel.easypermissions.AppSettingsDialog;
-import pub.devrel.easypermissions.EasyPermissions;
-
-public class ImageActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks {
-
- private static final String TAG = "ImageDemo";
- private static final int RC_CHOOSE_PHOTO = 101;
- private static final int RC_IMAGE_PERMS = 102;
- private static final String PERMS = Manifest.permission.READ_EXTERNAL_STORAGE;
-
- private StorageReference mImageRef;
-
- private ActivityImageBinding mBinding;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mBinding = ActivityImageBinding.inflate(getLayoutInflater());
- setContentView(mBinding.getRoot());
-
- mBinding.buttonDownloadDirect.setOnClickListener(view -> {
- // Download directly from StorageReference using Glide
- // (See MyAppGlideModule for Loader registration)
- GlideApp.with(ImageActivity.this)
- .load(mImageRef)
- .centerCrop()
- .transition(DrawableTransitionOptions.withCrossFade())
- .into(mBinding.firstImage);
- });
-
- mBinding.buttonChoosePhoto.setOnClickListener(view -> choosePhoto());
-
- // By default, Cloud Storage files require authentication to read or write.
- // For this sample to function correctly, enable Anonymous Auth in the Firebase console:
- // https://console.firebase.google.com/project/_/authentication/providers
- FirebaseAuth.getInstance()
- .signInAnonymously()
- .addOnCompleteListener(new SignInResultNotifier(this));
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (requestCode == RC_CHOOSE_PHOTO) {
- if (resultCode == RESULT_OK) {
- Uri selectedImage = data.getData();
- uploadPhoto(selectedImage);
- } else {
- Toast.makeText(this, "No image chosen", Toast.LENGTH_SHORT).show();
- }
- } else if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE
- && EasyPermissions.hasPermissions(this, PERMS)) {
- choosePhoto();
- }
- }
-
- @AfterPermissionGranted(RC_IMAGE_PERMS)
- protected void choosePhoto() {
- if (!EasyPermissions.hasPermissions(this, PERMS)) {
- EasyPermissions.requestPermissions(this, getString(R.string.rational_image_perm),
- RC_IMAGE_PERMS, PERMS);
- return;
- }
-
- Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
- startActivityForResult(i, RC_CHOOSE_PHOTO);
- }
-
- private void uploadPhoto(Uri uri) {
- // Reset UI
- hideDownloadUI();
- Toast.makeText(this, "Uploading...", Toast.LENGTH_SHORT).show();
-
- // Upload to Cloud Storage
- String uuid = UUID.randomUUID().toString();
- mImageRef = FirebaseStorage.getInstance().getReference(uuid);
- mImageRef.putFile(uri)
- .addOnSuccessListener(this, taskSnapshot -> {
- if (BuildConfig.DEBUG) {
- Log.d(TAG, "uploadPhoto:onSuccess:" +
- taskSnapshot.getMetadata().getReference().getPath());
- }
- Toast.makeText(ImageActivity.this, "Image uploaded",
- Toast.LENGTH_SHORT).show();
-
- showDownloadUI();
- })
- .addOnFailureListener(this, e -> {
- Log.w(TAG, "uploadPhoto:onError", e);
- Toast.makeText(ImageActivity.this, "Upload failed",
- Toast.LENGTH_SHORT).show();
- });
- }
-
- private void hideDownloadUI() {
- mBinding.buttonDownloadDirect.setEnabled(false);
-
- mBinding.firstImage.setImageResource(0);
- mBinding.firstImage.setVisibility(View.INVISIBLE);
- }
-
- private void showDownloadUI() {
- mBinding.buttonDownloadDirect.setEnabled(true);
-
- mBinding.firstImage.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- @NonNull String[] permissions,
- @NonNull int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
- }
-
- @Override
- public void onPermissionsGranted(int requestCode, @NonNull List perms) {
- // See #choosePhoto with @AfterPermissionGranted
- }
-
- @Override
- public void onPermissionsDenied(int requestCode, @NonNull List perms) {
- if (EasyPermissions.somePermissionPermanentlyDenied(this,
- Collections.singletonList(PERMS))) {
- new AppSettingsDialog.Builder(this).build().show();
- }
- }
-}
diff --git a/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java b/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java
deleted file mode 100644
index bc1f11568..000000000
--- a/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.firebase.uidemo.storage;
-
-import android.content.Context;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.Registry;
-import com.bumptech.glide.annotation.GlideModule;
-import com.bumptech.glide.module.AppGlideModule;
-import com.firebase.ui.storage.images.FirebaseImageLoader;
-import com.google.firebase.storage.StorageReference;
-
-import java.io.InputStream;
-
-import androidx.annotation.NonNull;
-
-/**
- * Glide module to register {@link com.firebase.ui.storage.images.FirebaseImageLoader}.
- * See: http://bumptech.github.io/glide/doc/generatedapi.html
- */
-@GlideModule
-public class MyAppGlideModule extends AppGlideModule {
-
- @Override
- public void registerComponents(@NonNull Context context,
- @NonNull Glide glide,
- @NonNull Registry registry) {
- // Register FirebaseImageLoader to handle StorageReference
- registry.append(StorageReference.class, InputStream.class,
- new FirebaseImageLoader.Factory());
- }
-
-}
diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt
similarity index 85%
rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt
rename to app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt
index 994915613..ead7d1c46 100644
--- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt
+++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt
@@ -1,4 +1,4 @@
-package com.firebase.composeapp.ui.theme
+package com.firebase.uidemo.ui.theme
import androidx.compose.ui.graphics.Color
diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt
similarity index 97%
rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt
rename to app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt
index 08bd9441a..4d34bc26d 100644
--- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt
+++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt
@@ -1,4 +1,4 @@
-package com.firebase.composeapp.ui.theme
+package com.firebase.uidemo.ui.theme
import android.app.Activity
import android.os.Build
diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt
similarity index 95%
rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt
rename to app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt
index ea0657654..f93d7a7ce 100644
--- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt
+++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt
@@ -1,4 +1,4 @@
-package com.firebase.composeapp.ui.theme
+package com.firebase.uidemo.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
diff --git a/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java b/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java
deleted file mode 100644
index bb7545bdf..000000000
--- a/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.firebase.uidemo.util;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.uidemo.R;
-import com.google.firebase.auth.ActionCodeSettings;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-
-@SuppressLint("RestrictedApi")
-public final class ConfigurationUtils {
-
- private ConfigurationUtils() {
- throw new AssertionError("No instance for you!");
- }
-
- public static boolean isGoogleMisconfigured(@NonNull Context context) {
- return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals(
- context.getString(R.string.default_web_client_id));
- }
-
- public static boolean isFacebookMisconfigured(@NonNull Context context) {
- return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals(
- context.getString(R.string.facebook_application_id));
- }
-
- @NonNull
- public static List getConfiguredProviders(@NonNull Context context) {
- List providers = new ArrayList<>();
-
- if (!isGoogleMisconfigured(context)) {
- providers.add(new AuthUI.IdpConfig.GoogleBuilder().build());
- }
-
- if (!isFacebookMisconfigured(context)) {
- providers.add(new AuthUI.IdpConfig.FacebookBuilder().build());
- }
-
- ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder()
- .setAndroidPackageName("com.firebase.uidemo", true, null)
- .setHandleCodeInApp(true)
- .setUrl("https://google.com")
- .build();
-
- providers.add(new AuthUI.IdpConfig.EmailBuilder()
- .setAllowNewAccounts(true)
- .enableEmailLinkSignIn()
- .setActionCodeSettings(actionCodeSettings)
- .build());
-
- providers.add(new AuthUI.IdpConfig.TwitterBuilder().build());
- providers.add(new AuthUI.IdpConfig.PhoneBuilder().build());
- providers.add(new AuthUI.IdpConfig.MicrosoftBuilder().build());
- providers.add(new AuthUI.IdpConfig.YahooBuilder().build());
- providers.add(new AuthUI.IdpConfig.AppleBuilder().build());
-
- return providers;
- }
-}
diff --git a/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java b/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java
deleted file mode 100644
index ec3d96f0e..000000000
--- a/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.firebase.uidemo.util;
-
-import android.content.Context;
-import android.widget.Toast;
-
-import com.firebase.uidemo.R;
-import com.google.android.gms.tasks.OnCompleteListener;
-import com.google.android.gms.tasks.Task;
-import com.google.firebase.auth.AuthResult;
-
-import androidx.annotation.NonNull;
-
-/**
- * Notifies the user of sign in successes or failures beyond the lifecycle of an activity.
- */
-public class SignInResultNotifier implements OnCompleteListener {
- private Context mContext;
-
- public SignInResultNotifier(@NonNull Context context) {
- mContext = context.getApplicationContext();
- }
-
- @Override
- public void onComplete(@NonNull Task task) {
- if (task.isSuccessful()) {
- Toast.makeText(mContext, R.string.signed_in, Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(mContext, R.string.anonymous_auth_failed_msg, Toast.LENGTH_LONG).show();
- }
- }
-}
diff --git a/composeapp/src/main/res/drawable-hdpi/firebase_auth.png b/app/src/main/res/drawable-hdpi/firebase_auth.png
similarity index 100%
rename from composeapp/src/main/res/drawable-hdpi/firebase_auth.png
rename to app/src/main/res/drawable-hdpi/firebase_auth.png
diff --git a/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png
deleted file mode 100644
index b03b18b53..000000000
Binary files a/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png and /dev/null differ
diff --git a/composeapp/src/main/res/drawable-mdpi/firebase_auth.png b/app/src/main/res/drawable-mdpi/firebase_auth.png
similarity index 100%
rename from composeapp/src/main/res/drawable-mdpi/firebase_auth.png
rename to app/src/main/res/drawable-mdpi/firebase_auth.png
diff --git a/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png
deleted file mode 100644
index 820ba76f9..000000000
Binary files a/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png and /dev/null differ
diff --git a/composeapp/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from composeapp/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/composeapp/src/main/res/drawable-xhdpi/firebase_auth.png b/app/src/main/res/drawable-xhdpi/firebase_auth.png
similarity index 100%
rename from composeapp/src/main/res/drawable-xhdpi/firebase_auth.png
rename to app/src/main/res/drawable-xhdpi/firebase_auth.png
diff --git a/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png
deleted file mode 100644
index 351fa59fe..000000000
Binary files a/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png and /dev/null differ
diff --git a/composeapp/src/main/res/drawable-xxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxhdpi/firebase_auth.png
similarity index 100%
rename from composeapp/src/main/res/drawable-xxhdpi/firebase_auth.png
rename to app/src/main/res/drawable-xxhdpi/firebase_auth.png
diff --git a/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png
deleted file mode 100644
index 6a6374e30..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png and /dev/null differ
diff --git a/composeapp/src/main/res/drawable-xxxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxxhdpi/firebase_auth.png
similarity index 100%
rename from composeapp/src/main/res/drawable-xxxhdpi/firebase_auth.png
rename to app/src/main/res/drawable-xxxhdpi/firebase_auth.png
diff --git a/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png
deleted file mode 100644
index 9b68e8ade..000000000
Binary files a/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png and /dev/null differ
diff --git a/app/src/main/res/drawable/custom_bg_gradient.xml b/app/src/main/res/drawable/custom_bg_gradient.xml
deleted file mode 100644
index 58479ea3c..000000000
--- a/app/src/main/res/drawable/custom_bg_gradient.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_anon_user_48dp.xml b/app/src/main/res/drawable/ic_anon_user_48dp.xml
deleted file mode 100644
index 0fb12013f..000000000
--- a/app/src/main/res/drawable/ic_anon_user_48dp.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_chat_message_arrow.xml b/app/src/main/res/drawable/ic_chat_message_arrow.xml
deleted file mode 100644
index 5063dc73e..000000000
--- a/app/src/main/res/drawable/ic_chat_message_arrow.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_chat_message_background.xml b/app/src/main/res/drawable/ic_chat_message_background.xml
deleted file mode 100644
index c22c3d71a..000000000
--- a/app/src/main/res/drawable/ic_chat_message_background.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/composeapp/src/main/res/drawable/ic_discord_24dp.xml b/app/src/main/res/drawable/ic_discord_24dp.xml
similarity index 100%
rename from composeapp/src/main/res/drawable/ic_discord_24dp.xml
rename to app/src/main/res/drawable/ic_discord_24dp.xml
diff --git a/app/src/main/res/drawable/ic_googleg_color_144dp.xml b/app/src/main/res/drawable/ic_googleg_color_144dp.xml
deleted file mode 100644
index 820e04185..000000000
--- a/app/src/main/res/drawable/ic_googleg_color_144dp.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
diff --git a/composeapp/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from composeapp/src/main/res/drawable/ic_launcher_background.xml
rename to app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/composeapp/src/main/res/drawable/ic_line_logo_24dp.xml b/app/src/main/res/drawable/ic_line_logo_24dp.xml
similarity index 100%
rename from composeapp/src/main/res/drawable/ic_line_logo_24dp.xml
rename to app/src/main/res/drawable/ic_line_logo_24dp.xml
diff --git a/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml b/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml
deleted file mode 100644
index 52a234d53..000000000
--- a/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml
+++ /dev/null
@@ -1,190 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_anonymous_upgrade.xml b/app/src/main/res/layout/activity_anonymous_upgrade.xml
deleted file mode 100644
index 2677a1c71..000000000
--- a/app/src/main/res/layout/activity_anonymous_upgrade.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml
deleted file mode 100644
index 75d5ae7f8..000000000
--- a/app/src/main/res/layout/activity_chat.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_chooser.xml b/app/src/main/res/layout/activity_chooser.xml
deleted file mode 100644
index d17f0fc40..000000000
--- a/app/src/main/res/layout/activity_chooser.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_chooser_item.xml b/app/src/main/res/layout/activity_chooser_item.xml
deleted file mode 100644
index d6b8f841b..000000000
--- a/app/src/main/res/layout/activity_chooser_item.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_database_paging.xml b/app/src/main/res/layout/activity_database_paging.xml
deleted file mode 100644
index de654181c..000000000
--- a/app/src/main/res/layout/activity_database_paging.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_firestore_paging.xml b/app/src/main/res/layout/activity_firestore_paging.xml
deleted file mode 100644
index f9ac119ea..000000000
--- a/app/src/main/res/layout/activity_firestore_paging.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/activity_image.xml b/app/src/main/res/layout/activity_image.xml
deleted file mode 100644
index 8d3f414e9..000000000
--- a/app/src/main/res/layout/activity_image.xml
+++ /dev/null
@@ -1,57 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/auth_method_picker_custom_layout.xml b/app/src/main/res/layout/auth_method_picker_custom_layout.xml
deleted file mode 100644
index 7b2ab007a..000000000
--- a/app/src/main/res/layout/auth_method_picker_custom_layout.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/auth_ui_layout.xml b/app/src/main/res/layout/auth_ui_layout.xml
deleted file mode 100644
index a22375abb..000000000
--- a/app/src/main/res/layout/auth_ui_layout.xml
+++ /dev/null
@@ -1,340 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_item.xml b/app/src/main/res/layout/item_item.xml
deleted file mode 100644
index 199abe810..000000000
--- a/app/src/main/res/layout/item_item.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/message.xml b/app/src/main/res/layout/message.xml
deleted file mode 100644
index bdbf05376..000000000
--- a/app/src/main/res/layout/message.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/layout/signed_in_layout.xml b/app/src/main/res/layout/signed_in_layout.xml
deleted file mode 100644
index b2f8e219c..000000000
--- a/app/src/main/res/layout/signed_in_layout.xml
+++ /dev/null
@@ -1,158 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/menu_paging.xml b/app/src/main/res/menu/menu_paging.xml
deleted file mode 100644
index b2f2da4cb..000000000
--- a/app/src/main/res/menu/menu_paging.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
index 0c2a915e9..036d09bc5 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -1,5 +1,5 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 0c2a915e9..036d09bc5 100644
--- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -1,5 +1,5 @@
-
+
-
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index 04b550741..000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to app/src/main/res/mipmap-hdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
deleted file mode 100644
index 7e9b0a385..000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
rename to app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
deleted file mode 100644
index 1d4f701ca..000000000
Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-hdpi/ic_launcher_round.webp
rename to app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index a3363e785..000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to app/src/main/res/mipmap-mdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
deleted file mode 100644
index 94fcac935..000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
rename to app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
deleted file mode 100644
index 4ba4452dd..000000000
Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-mdpi/ic_launcher_round.webp
rename to app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index abb82e050..000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to app/src/main/res/mipmap-xhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
deleted file mode 100644
index ac42576fe..000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
rename to app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
deleted file mode 100644
index dcfad82dc..000000000
Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
rename to app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index 025df8b10..000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index 506272a03..000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
deleted file mode 100644
index 44ea258e7..000000000
Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
rename to app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index f64cb7c6b..000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
deleted file mode 100644
index ec5beec43..000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
deleted file mode 100644
index 2d0841dc4..000000000
Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ
diff --git a/composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
similarity index 100%
rename from composeapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
rename to app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
deleted file mode 100644
index 63fc81644..000000000
--- a/app/src/main/res/values-w820dp/dimens.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
- 64dp
-
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index a59a6e717..f8c6127d3 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,18 +1,10 @@
+
- #039BE5
- #0288D1
- #FFA000
-
-
- #E8F5E9
- #AED581
- #4CAF50
- #388E3C
- #AA00FF
-
- #E0E0E0
- #9e9e9e
-
- #FF5252
-
-
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/app/src/main/res/values/config.xml b/app/src/main/res/values/config.xml
deleted file mode 100644
index 514c0747d..000000000
--- a/app/src/main/res/values/config.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
- CHANGE-ME
-
- CHANGE-ME
- fbYOUR_APP_ID
-
- CHANGE-ME
- CHANGE-ME
-
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
deleted file mode 100644
index 47c822467..000000000
--- a/app/src/main/res/values/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
- 16dp
- 16dp
-
diff --git a/composeapp/src/main/res/values/ic_launcher_background.xml b/app/src/main/res/values/ic_launcher_background.xml
similarity index 100%
rename from composeapp/src/main/res/values/ic_launcher_background.xml
rename to app/src/main/res/values/ic_launcher_background.xml
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 88d0ed1d8..fe45ed065 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,129 +1,10 @@
- Firebase UI
+ FirebaseUI Demo
-
- Auth UI Demo
- Cloud Firestore Demo
- Cloud Firestore Paging Demo
- Realtime Database Demo
- Realtime Database Paging Demo
- Storage Image Demo
+ temp-test-aa342.firebaseapp.com
- Demonstrates the Firebase Auth UI flow, with customization options.
- Demonstrates upgrading an anonymous account using FirebaseUI.
- Demonstrates using a FirestoreRecyclerAdapter to load data from Cloud Firestore into a RecyclerView for a basic chat app.
- Demonstrates using a FirestorePagingAdapter to load/infinite scroll paged data from Cloud Firestore.
- Demonstrates using a FirebaseRecyclerAdapter to load data from Firebase Database into a RecyclerView for a basic chat app.
- Demonstrates using a FirebaseRecyclerPagingAdapter to load/infinite scroll paged data from Firebase Realtime Database.
- Demonstrates displaying an image from Cloud Storage using Glide.
-
-
- FirebaseUI Auth Demo
- Start
- Sign in silently
-
- Auth providers
- Google
- Facebook
- Twitter
- Email
- Email link
- Phone
- Anonymous
- Apple
- Microsoft
- Yahoo
- GitHub
-
- Layout
- Default
- Custom
-
- Theme
- Material Light Theme
- App Theme
- Green Theme
-
- Logo
- Firebase Auth
- Google
- None
-
- Terms of Service and Privacy Policy
- Google
- Firebase
- None
-
- Example extra Google scopes
- Drive File
- Youtube data
-
- Example extra Facebook permissions
- Friends
- Photos
-
- Other Options
- Enable Credential Manager\'s credential selector
- Allow new account creation
- Require first/last name with email accounts.
- Connect to auth emulator (localhost:9099).
-
- Configuration required - see README.md
- Google configuration missing
- Facebook configuration missing
-
- Sign in cancelled
- No internet connection
- Account is disabled by the administrator
- An unknown error occurred
-
- FirebaseUI Anonymous Account Linking
- Anonymous Sign In
- Launch Auth UI
- Resolve Merge Conflict
-
-
- You are signed in!
- Sign out
- Delete account
-
- Sign in failed
- Sign out failed
- Delete account failed
-
- User profile:
- Profile picture
- Providers used: %s
- IDP Token
- IDP Secret
-
-
- Sign in using this nice custom layout!
- Choose a provider
- Use your email
- Continue with Email
-
-
-
- Choose Image
- Upload
- Download
- Send
- Downloaded image
-
- This sample will read an image from local storage to upload to Cloud Storage.
-
-
- No messages. Start chatting at the bottom!
- Signing in…
- Signed In
-
- Anonymous authentication failed, various components of the demo will not work.
- Make sure your device is online and that Anonymous Auth is configured in your Firebase project
- (https://console.firebase.google.com/project/_/authentication/providers)
-
-
- Add Data
- Say something…
- Reached End of List
-
+
+ 1131506989188007
+ fb1131506989188007
+ e3968638d7751ba83063e2a78bc27e4e
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
deleted file mode 100644
index 75ae25893..000000000
--- a/app/src/main/res/values/styles.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/composeapp/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
similarity index 100%
rename from composeapp/src/main/res/values/themes.xml
rename to app/src/main/res/values/themes.xml
diff --git a/app/src/main/res/xml-v25/shortcuts.xml b/app/src/main/res/xml-v25/shortcuts.xml
deleted file mode 100644
index 988b81d73..000000000
--- a/app/src/main/res/xml-v25/shortcuts.xml
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/auth/MIGRATION.md b/auth/MIGRATION.md
new file mode 100644
index 000000000..ef0701ff4
--- /dev/null
+++ b/auth/MIGRATION.md
@@ -0,0 +1,465 @@
+# Migration Guide: FirebaseUI Auth 9.x to 10.x
+
+This guide helps you migrate from FirebaseUI Auth 9.x (View-based) to 10.x (Compose-based).
+
+## Overview
+
+FirebaseUI Auth 10.x is a complete rewrite built with Jetpack Compose and Material Design 3. The architecture has changed significantly from the View-based system to a modern, declarative UI framework.
+
+## Key Architectural Changes
+
+### 1. UI Framework
+- **9.x**: Android Views, Activities, Fragments
+- **10.x**: Jetpack Compose, Composables
+
+### 2. Configuration
+- **9.x**: Builder pattern (`createSignInIntentBuilder()`)
+- **10.x**: Kotlin DSL (`authUIConfiguration {}`)
+
+### 3. Providers
+- **9.x**: `IdpConfig.EmailBuilder().build()`
+- **10.x**: `AuthProvider.Email()`
+
+### 4. Flow Control
+- **9.x**: Activity-based with `startActivityForResult()` and `ActivityResultLauncher`
+- **10.x**: Composable screens with direct callbacks OR `AuthFlowController` for Activity-based apps
+
+### 5. Theming
+- **9.x**: XML theme resources (`R.style.AppTheme`)
+- **10.x**: `AuthUITheme` with Material 3 color schemes
+
+### 6. State Management
+- **9.x**: `AuthStateListener` callbacks
+- **10.x**: Reactive `Flow`
+
+## Migration Steps
+
+### Step 1: Update Dependencies
+
+**Old (9.x):**
+```kotlin
+dependencies {
+ implementation("com.firebaseui:firebase-ui-auth:9.1.1")
+}
+```
+
+**New (10.x):**
+```kotlin
+dependencies {
+ // FirebaseUI Auth
+ implementation("com.firebaseui:firebase-ui-auth:10.0.0")
+
+ // Required: Jetpack Compose
+ implementation(platform("androidx.compose:compose-bom:2024.01.00"))
+ implementation("androidx.compose.ui:ui")
+ implementation("androidx.compose.material3:material3")
+}
+```
+
+### Step 2: Migrate to Compose
+
+**Old (9.x) - Activity-based:**
+```java
+public class SignInActivity extends AppCompatActivity {
+ private final ActivityResultLauncher signInLauncher =
+ registerForActivityResult(
+ new ActivityResultContracts.StartActivityForResult(),
+ result -> {
+ IdpResponse response = IdpResponse.fromResultIntent(result.getData());
+
+ if (result.getResultCode() == RESULT_OK) {
+ FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
+ // User signed in
+ } else {
+ // Sign in failed
+ }
+ }
+ );
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_signin);
+
+ Intent signInIntent = AuthUI.getInstance()
+ .createSignInIntentBuilder()
+ .setAvailableProviders(Arrays.asList(
+ new AuthUI.IdpConfig.EmailBuilder().build(),
+ new AuthUI.IdpConfig.GoogleBuilder().build()
+ ))
+ .setTheme(R.style.AppTheme)
+ .build();
+
+ signInLauncher.launch(signInIntent);
+ }
+}
+```
+
+**New (10.x) - Compose-based:**
+```kotlin
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContent {
+ MyAppTheme {
+ val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Email(),
+ AuthProvider.Google()
+ )
+ theme = AuthUITheme.fromMaterialTheme()
+ }
+
+ FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result ->
+ val user = result.user
+ // User signed in
+ },
+ onSignInFailure = { exception ->
+ // Sign in failed
+ },
+ onSignInCancelled = {
+ finish()
+ }
+ )
+ }
+ }
+ }
+}
+```
+
+### Step 3: Update Provider Configuration
+
+**Old (9.x):**
+```java
+List providers = Arrays.asList(
+ new AuthUI.IdpConfig.EmailBuilder()
+ .setRequireName(true)
+ .build(),
+ new AuthUI.IdpConfig.GoogleBuilder()
+ .build(),
+ new AuthUI.IdpConfig.PhoneBuilder()
+ .build()
+);
+```
+
+**New (10.x):**
+```kotlin
+val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Email(
+ isDisplayNameRequired = true
+ ),
+ AuthProvider.Google(
+ scopes = listOf("email"),
+ serverClientId = "YOUR_CLIENT_ID"
+ ),
+ AuthProvider.Phone(
+ defaultCountryCode = "US"
+ )
+ )
+}
+```
+
+### Step 4: Update Theming
+
+**Old (9.x) - XML styles:**
+```xml
+
+```
+
+```java
+.setTheme(R.style.AppTheme)
+```
+
+**New (10.x) - Material 3:**
+```kotlin
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ theme = AuthUITheme(
+ colorScheme = lightColorScheme(
+ primary = Color(0xFF6200EE),
+ onPrimary = Color.White,
+ secondary = Color(0xFF03DAC6)
+ )
+ )
+}
+```
+
+Or inherit from your app theme:
+```kotlin
+MyAppTheme {
+ val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ theme = AuthUITheme.fromMaterialTheme()
+ }
+
+ FirebaseAuthScreen(configuration = configuration, ...)
+}
+```
+
+### Step 5: Update Sign Out
+
+**Old (9.x):**
+```java
+AuthUI.getInstance()
+ .signOut(this)
+ .addOnCompleteListener(task -> {
+ // User signed out
+ });
+```
+
+**New (10.x):**
+```kotlin
+lifecycleScope.launch {
+ FirebaseAuthUI.getInstance().signOut(context)
+ // User signed out
+}
+```
+
+### Step 6: Update Account Deletion
+
+**Old (9.x):**
+```java
+AuthUI.getInstance()
+ .delete(this)
+ .addOnCompleteListener(task -> {
+ if (task.isSuccessful()) {
+ // Account deleted
+ } else {
+ // Deletion failed
+ }
+ });
+```
+
+**New (10.x):**
+```kotlin
+lifecycleScope.launch {
+ try {
+ FirebaseAuthUI.getInstance().delete(context)
+ // Account deleted
+ } catch (e: Exception) {
+ // Deletion failed
+ }
+}
+```
+
+### Step 7: Auth State Observation
+
+**Old (9.x):**
+```java
+FirebaseAuth.getInstance().addAuthStateListener(firebaseAuth -> {
+ FirebaseUser user = firebaseAuth.getCurrentUser();
+ if (user != null) {
+ // User is signed in
+ } else {
+ // User is signed out
+ }
+});
+```
+
+**New (10.x):**
+```kotlin
+@Composable
+fun AuthGate() {
+ val authUI = remember { FirebaseAuthUI.getInstance() }
+ val authState by authUI.authStateFlow().collectAsState(initial = AuthState.Idle)
+
+ when (authState) {
+ is AuthState.Success -> {
+ // User is signed in
+ MainAppScreen()
+ }
+ else -> {
+ // Show authentication
+ FirebaseAuthScreen(...)
+ }
+ }
+}
+```
+
+## Provider-Specific Migration
+
+### Email Provider
+
+**Old (9.x):**
+```java
+new AuthUI.IdpConfig.EmailBuilder()
+ .setRequireName(true)
+ .setAllowNewAccounts(true)
+ .enableEmailLinkSignIn()
+ .setActionCodeSettings(actionCodeSettings)
+ .build()
+```
+
+**New (10.x):**
+```kotlin
+AuthProvider.Email(
+ isDisplayNameRequired = true,
+ isNewAccountsAllowed = true,
+ isEmailLinkSignInEnabled = true,
+ emailLinkActionCodeSettings = actionCodeSettings {
+ url = "https://example.com/auth"
+ handleCodeInApp = true
+ setAndroidPackageName(packageName, true, null)
+ }
+)
+```
+
+### Google Provider
+
+**Old (9.x):**
+```java
+new AuthUI.IdpConfig.GoogleBuilder()
+ .setScopes(Arrays.asList("email", "profile"))
+ .build()
+```
+
+**New (10.x):**
+```kotlin
+AuthProvider.Google(
+ scopes = listOf("email", "profile"),
+ serverClientId = "YOUR_CLIENT_ID"
+)
+```
+
+### Phone Provider
+
+**Old (9.x):**
+```java
+new AuthUI.IdpConfig.PhoneBuilder()
+ .setDefaultNumber("US", "+1 123-456-7890")
+ .build()
+```
+
+**New (10.x):**
+```kotlin
+AuthProvider.Phone(
+ defaultCountryCode = "US",
+ defaultNumber = "+11234567890"
+)
+```
+
+## Advanced Migration Scenarios
+
+### Custom UI (Activity-based apps that can't use Compose everywhere)
+
+If you have an existing Activity-based app and want to keep using Activities:
+
+**New (10.x) - Low-Level API:**
+```kotlin
+class AuthActivity : ComponentActivity() {
+ private lateinit var controller: AuthFlowController
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val authUI = FirebaseAuthUI.getInstance()
+ val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email(), AuthProvider.Google())
+ }
+
+ controller = authUI.createAuthFlow(configuration)
+
+ lifecycleScope.launch {
+ val state = controller.start()
+ when (state) {
+ is AuthState.Success -> {
+ startActivity(Intent(this, MainActivity::class.java))
+ finish()
+ }
+ is AuthState.Error -> {
+ // Handle error
+ }
+ else -> {
+ // Handle other states
+ }
+ }
+ }
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ controller.dispose()
+ }
+}
+```
+
+## Common Issues and Solutions
+
+### Issue: "Unresolved reference: authUIConfiguration"
+
+**Solution:** Ensure you have the correct import:
+```kotlin
+import com.firebase.ui.auth.compose.configuration.authUIConfiguration
+```
+
+### Issue: "ActivityResultLauncher is deprecated"
+
+**Solution:** In 10.x, you no longer need `ActivityResultLauncher`. Use direct callbacks with `FirebaseAuthScreen` or `AuthFlowController`.
+
+### Issue: "How do I customize the UI?"
+
+**Solution:** Use content slots for custom UI:
+```kotlin
+EmailAuthScreen(
+ configuration = emailConfig,
+ onSuccess = { /* ... */ },
+ onError = { /* ... */ },
+ onCancel = { /* ... */ }
+) { state ->
+ // Your custom UI here
+ CustomSignInUI(state)
+}
+```
+
+### Issue: "My XML themes aren't working"
+
+**Solution:** Convert XML themes to Kotlin code using `AuthUITheme`:
+```kotlin
+val configuration = authUIConfiguration {
+ theme = AuthUITheme(
+ colorScheme = lightColorScheme(
+ primary = Color(0xFF6200EE),
+ // ... other colors
+ )
+ )
+}
+```
+
+## Testing Your Migration
+
+1. **Build the app** - Ensure it compiles without errors
+2. **Test all auth flows** - Sign in, sign up, password reset
+3. **Test all providers** - Email, Google, Phone, etc.
+4. **Test sign out** - Verify users can sign out
+5. **Test account deletion** - Verify accounts can be deleted
+6. **Test error handling** - Verify errors are handled gracefully
+7. **Test theming** - Verify UI matches your design
+
+## Checklist
+
+- [ ] Updated dependency to `firebase-ui-auth:10.0.0`
+- [ ] Migrated to Jetpack Compose
+- [ ] Converted Activities to ComponentActivities with `setContent {}`
+- [ ] Replaced `createSignInIntentBuilder()` with `authUIConfiguration {}`
+- [ ] Updated all provider configurations
+- [ ] Converted XML themes to `AuthUITheme`
+- [ ] Updated error handling from result codes to exceptions
+- [ ] Removed `ActivityResultLauncher` code
+- [ ] Updated sign-out to use suspend functions
+- [ ] Updated account deletion to use suspend functions
+- [ ] Tested all authentication flows
+- [ ] Tested on multiple Android versions
+
+## Need Help?
+
+- [FirebaseUI Auth Documentation](https://github.com/firebase/FirebaseUI-Android/blob/master/auth/README.md)
+- [GitHub Issues](https://github.com/firebase/FirebaseUI-Android/issues)
+- [Stack Overflow](https://stackoverflow.com/questions/tagged/firebaseui)
diff --git a/auth/README.md b/auth/README.md
index b705b30d0..cbc97b386 100644
--- a/auth/README.md
+++ b/auth/README.md
@@ -1,815 +1,1468 @@
# FirebaseUI for Auth
-FirebaseUI is an open-source library that offers simple,
-customizable UI bindings on top of the core
-[Firebase](https://firebase.google.com) SDKs. It aims to eliminate boilerplate
-code and promote best practices (both user experience and security) for
-authentication.
-
-A simple API is provided for drop-in user authentication which handles the flow
-of signing in users with email addresses and passwords, phone numbers, and federated identity
-providers such as Google Sign-In, and Facebook Login. It is built on top of
-[Firebase Auth](https://firebase.google.com/docs/auth).
-
-The best practices embodied in FirebaseUI aim to maximize sign-in
-and sign-up conversion for your app. It integrates with
-[Credential Manager](https://developer.android.com/identity/sign-in/credential-manager)
-to store and retrieve credentials, enabling automatic and single-tap sign-in to
-your app for returning users. It also handles tricky use cases like
-account recovery and account linking that are security sensitive and
-difficult to implement correctly using the base APIs provided by Firebase Auth.
-
-FirebaseUI auth can be easily customized to fit with the rest of your app's
-visual style. As it is open source, you are also free to modify it to exactly
-fit your preferred user experience.
-
-Equivalent FirebaseUI auth libraries are also available for
-[iOS](https://github.com/firebase/firebaseui-ios/)
-and [Web](https://github.com/firebase/firebaseui-web/).
-
-## Table of contents
+FirebaseUI Auth is a modern, Compose-based authentication library that provides drop-in UI components for Firebase Authentication. It eliminates boilerplate code and promotes best practices for user authentication on Android.
+
+Built entirely with **Jetpack Compose** and **Material Design 3**, FirebaseUI Auth offers:
+
+- **Simple API** - Choose between high-level screens or low-level controllers for maximum flexibility
+- **12+ Authentication Methods** - Email/Password, Phone, Google, Facebook, Twitter, GitHub, Microsoft, Yahoo, Apple, Anonymous, and custom OAuth providers
+- **Multi-Factor Authentication** - SMS and TOTP (Time-based One-Time Password) with recovery codes
+- **Android Credential Manager** - Automatic credential saving and one-tap sign-in
+- **Material Design 3** - Beautiful, themeable UI components that integrate seamlessly with your app
+- **Localization Support** - Customizable strings for internationalization
+- **Security Best Practices** - Email verification, reauthentication, account linking, and more
+
+Equivalent FirebaseUI libraries are available for [iOS](https://github.com/firebase/firebaseui-ios/) and [Web](https://github.com/firebase/firebaseui-web/).
+
+## Table of Contents
1. [Demo](#demo)
-1. [Configuration](#configuration)
- 1. [Basics](#basics)
- 1. [Themes](#themes)
- 1. [Provider config](#identity-provider-configuration)
- 1. [Auth emulator config](#auth-emulator-configuration)
-1. [Usage instructions](#using-firebaseui-for-authentication)
- 1. [AuthUI sign-in](#authui-sign-in)
- 1. [Handling responses](#handling-the-sign-in-response)
- 1. [Sign out](#sign-out)
- 1. [Account deletion](#deleting-accounts)
- 1. [Upgrading Anonymous Users](#upgrading-anonymous-users)
-1. [UI Customization](#ui-customization)
- 1. [Custom layout](#custom-layout)
- 1. [Strings](#strings)
-1. [OAuth scopes](#oauth-scope-customization)
- 1. [Google](#google-1)
- 1. [Facebook](#facebook-1)
- 1. [Twitter](#twitter-1)
+1. [Setup](#setup)
+ 1. [Prerequisites](#prerequisites)
+ 1. [Installation](#installation)
+ 1. [Provider Configuration](#provider-configuration)
+1. [Quick Start](#quick-start)
+ 1. [Minimal Example](#minimal-example)
+ 1. [Check Authentication State](#check-authentication-state)
+1. [Core Concepts](#core-concepts)
+ 1. [FirebaseAuthUI](#firebaseauthui)
+ 1. [AuthUIConfiguration](#authuiconfiguration)
+ 1. [AuthFlowController](#authflowcontroller)
+ 1. [AuthState](#authstate)
+1. [Authentication Methods](#authentication-methods)
+ 1. [Email & Password](#email--password)
+ 1. [Phone Number](#phone-number)
+ 1. [Google Sign-In](#google-sign-in)
+ 1. [Facebook Login](#facebook-login)
+ 1. [Other OAuth Providers](#other-oauth-providers)
+ 1. [Anonymous Authentication](#anonymous-authentication)
+ 1. [Custom OAuth Provider](#custom-oauth-provider)
+1. [Usage Patterns](#usage-patterns)
+ 1. [High-Level API (Recommended)](#high-level-api-recommended)
+ 1. [Low-Level API (Advanced)](#low-level-api-advanced)
+ 1. [Custom UI with Slots](#custom-ui-with-slots)
+1. [Multi-Factor Authentication](#multi-factor-authentication)
+ 1. [MFA Configuration](#mfa-configuration)
+ 1. [MFA Enrollment](#mfa-enrollment)
+ 1. [MFA Challenge](#mfa-challenge)
+1. [Theming & Customization](#theming--customization)
+ 1. [Material Theme Integration](#material-theme-integration)
+ 1. [Custom Theme](#custom-theme)
+ 1. [Provider Button Styling](#provider-button-styling)
+1. [Advanced Features](#advanced-features)
+ 1. [Anonymous User Upgrade](#anonymous-user-upgrade)
+ 1. [Email Link Sign-In](#email-link-sign-in)
+ 1. [Password Validation Rules](#password-validation-rules)
+ 1. [Credential Manager Integration](#credential-manager-integration)
+ 1. [Sign Out & Account Deletion](#sign-out--account-deletion)
+1. [Localization](#localization)
+1. [Error Handling](#error-handling)
+1. [Migration Guide](#migration-guide)
## Demo
-
+
+
+## Setup
-## Configuration
+### Prerequisites
-As a pre-requisite, ensure your application is configured for use with
-Firebase: see the
-[Firebase documentation](https://firebase.google.com/docs/android/setup).
-Then, add the FirebaseUI auth library dependency. If your project uses
-Gradle, add the dependency:
+Ensure your application is configured for use with Firebase. See the [Firebase documentation](https://firebase.google.com/docs/android/setup) for setup instructions.
-```groovy
+**Minimum Requirements:**
+- Android SDK 21+ (Android 5.0 Lollipop)
+- Kotlin 1.9+
+- Jetpack Compose (Compiler 1.5+)
+- Firebase Auth 22.0.0+
+
+### Installation
+
+Add the FirebaseUI Auth library dependency to your `build.gradle.kts` (Module):
+
+```kotlin
dependencies {
- // ...
- implementation 'com.firebaseui:firebase-ui-auth:9.0.0'
+ // FirebaseUI for Auth
+ implementation("com.firebaseui:firebase-ui-auth:10.0.0")
+
+ // Required: Firebase Auth
+ implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
+ implementation("com.google.firebase:firebase-auth")
- // Required only if Facebook login support is required
- // Find the latest Facebook SDK releases here: https://github.com/facebook/facebook-android-sdk/blob/master/CHANGELOG.md
- implementation 'com.facebook.android:facebook-login:8.1.0'
+ // Required: Jetpack Compose
+ implementation(platform("androidx.compose:compose-bom:2024.01.00"))
+ implementation("androidx.compose.ui:ui")
+ implementation("androidx.compose.material3:material3")
+
+ // Optional: Facebook Login (if using FacebookAuthProvider)
+ implementation("com.facebook.android:facebook-login:16.3.0")
}
```
-FirebaseUI includes translations for all string resources. In order to
-ensure that you only get the translations relevant to your application, we recommend changing the
-`resConfigs` of your application module:
+**Localization Support:**
-```groovy
-android {
- // ...
+To optimize APK size, configure resource filtering for only the languages your app supports:
+```kotlin
+android {
defaultConfig {
- // ...
- resConfigs "en" // And any other languages you support
+ resourceConfigurations += listOf("en", "es", "fr") // Add your supported languages
}
}
```
-See the [Android documentation](https://developer.android.com/studio/build/shrink-code.html#unused-alt-resources)
-for more information.
-
-### Basics
+### Provider Configuration
-There are three main steps to adding FirebaseUI in your app:
+#### Google Sign-In
- 1. Build a sign in `Intent` using `AuthUI#createSignInIntentBuilder()`
- 2. Launch the `Intent` using an `ActivityResultLauncher`
- 3. Handle the result.
-
-```java
-private ActivityResultLauncher signInLauncher = registerForActivityResult(
- new FirebaseAuthUIActivityResultContract(),
- (result) -> {
- // Handle the FirebaseAuthUIAuthenticationResult
- // ...
- });
+Google Sign-In configuration is automatically provided by the [google-services Gradle plugin](https://developers.google.com/android/guides/google-services-plugin). Ensure you have enabled Google Sign-In in the [Firebase Console](https://console.firebase.google.com/project/_/authentication/providers).
-// ...
+#### Facebook Login
-private void startSignIn() {
- Intent signInIntent = AuthUI.getInstance()
- .createSignInIntentBuilder()
- // ... options ...
- .build();
+If using Facebook Login, add your Facebook App ID to `strings.xml`:
- signInLauncher.launch(signInIntent);
-}
+```xml
+
+ YOUR_FACEBOOK_APP_ID
+ fbYOUR_FACEBOOK_APP_ID
+
```
-### Themes
+See the [Facebook for Developers](https://developers.facebook.com/) documentation for setup instructions.
-As of version `8.0.0` FirebaseUI uses Material Design Components and themes. To use FirebaseUI seamlessly in your app you should provide a theme resource which provides Material Design color attributes ([read more here](https://material.io/blog/android-material-theme-color)).
+#### Other Providers
-At a minimum your theme should define the following attributes:
+Twitter, GitHub, Microsoft, Yahoo, and Apple providers require configuration in the Firebase Console but no additional Android-specific setup. See the [Firebase Auth documentation](https://firebase.google.com/docs/auth) for provider-specific instructions.
- * `colorPrimary`
- * `colorPrimaryVariant`
- * `colorAccent`
- * `android:statusBarColor` (API > 21) or `colorPrimaryDark` (API < 21)
+## Quick Start
-#### Using your app theme
+### Minimal Example
-To configure FirebaseUI to match your app's exising theme, simply pass your main theme attribute to `setTheme()`:
+Here's the simplest way to add authentication to your app with Email and Google Sign-In:
-This would then be used in the construction of the sign-in intent:
+```kotlin
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
-```java
-Intent signInIntent =
- AuthUI.getInstance(this).createSignInIntentBuilder()
- // ...
- .setTheme(R.style.AppTheme)
- .build())
+ setContent {
+ MyAppTheme {
+ val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Email(),
+ AuthProvider.Google()
+ )
+ }
+
+ FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result ->
+ Toast.makeText(this, "Welcome!", Toast.LENGTH_SHORT).show()
+ // Navigate to main app screen
+ },
+ onSignInFailure = { exception ->
+ Toast.makeText(this, "Error: ${exception.message}", Toast.LENGTH_SHORT).show()
+ },
+ onSignInCancelled = {
+ finish()
+ }
+ )
+ }
+ }
+ }
+}
```
-#### Using a custom theme
+That's it! This provides a complete authentication flow with:
+- ✅ Email/password sign-in and sign-up
+- ✅ Google Sign-In
+- ✅ Password reset
+- ✅ Display name collection
+- ✅ Credential Manager integration
+- ✅ Material Design 3 theming
+- ✅ Error handling
+
+### Check Authentication State
+
+Before showing the authentication UI, check if a user is already signed in:
+
+```kotlin
+class MainActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val authUI = FirebaseAuthUI.getInstance()
+
+ if (authUI.isSignedIn()) {
+ // User is already signed in, navigate to main app
+ startActivity(Intent(this, MainAppActivity::class.java))
+ finish()
+ } else {
+ // Show authentication UI
+ setContent {
+ FirebaseAuthScreen(/* ... */)
+ }
+ }
+ }
+}
+```
-For example, here is a simple green sign-in theme:
+Or observe authentication state changes reactively:
-```xml
-
+ when {
+ authState is AuthState.Success -> {
+ // User is signed in
+ MainAppScreen()
+ }
+ else -> {
+ // Show authentication
+ FirebaseAuthScreen(/* ... */)
+ }
+ }
+}
```
-With associated colors:
+## Core Concepts
-```xml
-#E8F5E9
-#4CAF50
-#388E3C
-#AA00FF
-```
+### FirebaseAuthUI
-This would then be used in the construction of the sign-in intent:
+`FirebaseAuthUI` is the central class that coordinates all authentication operations. It manages UI state and provides methods for signing in, signing up, and managing user accounts.
-```java
-Intent signinIntent =
- AuthUI.getInstance(this).createSignInIntentBuilder()
- // ...
- .setTheme(R.style.GreenTheme)
- .build();
+```kotlin
+// Get the default instance
+val authUI = FirebaseAuthUI.getInstance()
+
+// Or get an instance for a specific Firebase app
+val customApp = Firebase.app("secondary")
+val authUI = FirebaseAuthUI.getInstance(customApp)
+
+// Or create with custom auth (for multi-tenancy)
+val customAuth = Firebase.auth(customApp)
+val authUI = FirebaseAuthUI.create(auth = customAuth)
```
-### Identity provider configuration
+**Key Methods:**
-In order to use either Google, Facebook, Twitter, Microsoft, Apple, GitHub or Yahoo accounts with
-your app, ensure that these authentication methods are first configured in the Firebase console.
+| Method | Return Type | Description |
+|--------|-------------|-------------|
+| `isSignedIn()` | `Boolean` | Checks if a user is currently signed in |
+| `getCurrentUser()` | `FirebaseUser?` | Returns the current user, if signed in |
+| `authStateFlow()` | `Flow` | Observes authentication state changes |
+| `createAuthFlow(config)` | `AuthFlowController` | Creates a sign-in flow controller |
+| `signOut(context)` | `suspend fun` | Signs out the current user |
+| `delete(context)` | `suspend fun` | Deletes the current user account |
-#### Google
+### AuthUIConfiguration
-FirebaseUI client-side configuration for Google sign-in is then provided
-automatically by the
-[google-services gradle plugin](https://developers.google.com/android/guides/google-services-plugin).
+`AuthUIConfiguration` defines all settings for your authentication flow. Use the DSL builder function for easy configuration:
-#### Facebook
+```kotlin
+val configuration = authUIConfiguration {
+ // Required: List of authentication providers
+ providers = listOf(
+ AuthProvider.Email(),
+ AuthProvider.Google(),
+ AuthProvider.Phone()
+ )
-If support for Facebook Login is also required, define the
-resource string `facebook_application_id` to match the application ID in
-the [Facebook developer dashboard](https://developers.facebook.com):
+ // Optional: Theme configuration
+ theme = AuthUITheme.fromMaterialTheme()
-```xml
-
-
- APP_ID
-
- fbAPP_ID
-
-```
+ // Optional: Terms of Service and Privacy Policy URLs
+ tosUrl = "https://example.com/terms"
+ privacyPolicyUrl = "https://example.com/privacy"
-#### Microsoft, Apple, Twitter, GitHub and Yahoo
+ // Optional: App logo
+ logo = Icons.Default.AccountCircle
-No FirebaseUI configuration is required for these providers.
+ // Optional: Enable MFA (default: true)
+ isMfaEnabled = true
-We support the use of scopes and custom parameters for these providers. For example:
+ // Optional: Enable Credential Manager (default: true)
+ isCredentialManagerEnabled = true
-```java
-List scopes =
- new ArrayList() {
- {
- add("mail.read");
- add("calendars.read");
- }
- };
+ // Optional: Allow anonymous user upgrade (default: false)
+ isAnonymousUpgradeEnabled = true
-Map customParams = new HashMap<>();
-customParams.put("tenant", "TENANT_ID");
+ // Optional: Require display name on sign-up (default: true)
+ isDisplayNameRequired = true
-IdpConfig microsoftConfig = new IdpConfig.MicrosoftBuilder()
- .setScopes(scopes)
- .setCustomParameters(customParams)
- .build();
-selectedProviders.add(microsoftConfig);
-```
+ // Optional: Allow new email accounts (default: true)
+ isNewEmailAccountsAllowed = true
-Note: unlike other sign-in methods, signing in with these providers involves the use of a
-[Custom Chrome Tab](https://developer.chrome.com/multidevice/android/customtabs).
+ // Optional: Always show provider choice even with one provider (default: false)
+ isProviderChoiceAlwaysShown = false
-##### Twitter
+ // Optional: Custom string provider for localization
+ stringProvider = MyCustomStringProvider()
-You must enable the "Request email addresses from users" permission in the "Permissions" tab of your
-Twitter app.
+ // Optional: Locale override
+ locale = Locale.FRENCH
+}
+```
-### Auth emulator configuration
+### AuthFlowController
-As of version `7.0.0` FirebaseUI is compatible with the Firebase Authentication emulator:
-https://firebase.google.com/docs/emulator-suite
+`AuthFlowController` manages the lifecycle of an authentication flow programmatically. This is the low-level API for advanced use cases.
-Use the `useEmulator` method to point an AuthUI instance at the emulator:
+```kotlin
+val controller = authUI.createAuthFlow(configuration)
-```java
-AuthUI authUI = AuthUI.getInstance();
+lifecycleScope.launch {
+ // Start the flow
+ val state = controller.start()
-// "10.0.2.2" is the special host value for contacting "localhost" from within
-// the Android Emulator
-authUI.useEmulator("10.0.2.2", 9099);
-```
+ when (state) {
+ is AuthState.Success -> {
+ // Handle success
+ val user = state.result.user
+ }
+ is AuthState.Error -> {
+ // Handle error
+ Log.e(TAG, "Auth failed", state.exception)
+ }
+ is AuthState.Cancelled -> {
+ // User cancelled
+ }
+ else -> {
+ // Handle other states (RequiresMfa, RequiresEmailVerification, etc.)
+ }
+ }
+}
+
+// Cancel the flow if needed
+controller.cancel()
-By default Android blocks connections to `http://` endpoints such as the Auth emulator.
-To allow your app to communicate with the Auth emulator, use a [network security configuration](https://developer.android.com/training/articles/security-config)
-or set `android:usesCleartextTraffic="true"` in `AndroidManifest.xml`.
+// Clean up when done
+override fun onDestroy() {
+ super.onDestroy()
+ controller.dispose()
+}
+```
-## Using FirebaseUI for authentication
+### AuthState
-Before invoking the FirebaseUI authentication flow, your app should check
-whether a
-[user is already signed in](https://firebase.google.com/docs/auth/android/manage-users#get_the_currently_signed-in_user)
-from a previous session:
+`AuthState` represents the current state of authentication:
-```java
-FirebaseAuth auth = FirebaseAuth.getInstance();
-if (auth.getCurrentUser() != null) {
- // already signed in
-} else {
- // not signed in
+```kotlin
+sealed class AuthState {
+ object Idle : AuthState()
+ data class Loading(val message: String?) : AuthState()
+ data class Success(val result: AuthResult, val isNewUser: Boolean) : AuthState()
+ data class Error(val exception: AuthException, val isRecoverable: Boolean) : AuthState()
+ data class RequiresMfa(val resolver: MultiFactorResolver) : AuthState()
+ data class RequiresEmailVerification(val user: FirebaseUser) : AuthState()
+ data class RequiresProfileCompletion(val user: FirebaseUser) : AuthState()
+ object Cancelled : AuthState()
}
```
-The entry point to the authentication flow is the
-`com.firebase.ui.auth.AuthUI` class.
-If your application uses the default `FirebaseApp` instance, an AuthUI
-instance can be retrieved simply by calling `AuthUI.getInstance()`.
-If an alternative app instance is required, call
-`AuthUI.getInstance(app)` instead, passing the appropriate `FirebaseApp`
-instance.
+## Authentication Methods
-### AuthUI sign-in
+### Email & Password
-If a user is not currently signed in, as can be determined by checking
-`auth.getCurrentUser() != null` (where `auth` is the `FirebaseAuth` instance
-associated with your `FirebaseApp`), then the sign-in process can be started by
-creating a sign-in intent using `AuthUI.SignInIntentBuilder`. A builder instance
-can be retrieved by calling `createSignInIntentBuilder()` on the retrieved
-AuthUI instance.
+Configure email/password authentication with optional customization:
-The builder provides the following customization options for the authentication flow:
+```kotlin
+val emailProvider = AuthProvider.Email(
+ // Optional: Require display name (default: true)
+ isDisplayNameRequired = true,
-* The set of authentication providers can be specified.
-* The terms of service URL for your app can be specified, which is included as
- a link in the small-print of the account creation step for new users. If no
- terms of service URL is provided, the associated small-print is omitted.
-* A custom theme can be specified for the flow, which is applied to all the
- activities in the flow for consistent colors and typography.
+ // Optional: Enable email link sign-in (default: false)
+ isEmailLinkSignInEnabled = true,
-#### Sign-in examples
+ // Optional: Force email link on same device (default: true)
+ isEmailLinkForceSameDeviceEnabled = true,
-If no customization is required, and only email authentication is required, the sign-in flow
-can be started as follows:
+ // Optional: Action code settings for email link
+ emailLinkActionCodeSettings = actionCodeSettings {
+ url = "https://example.com/auth"
+ handleCodeInApp = true
+ setAndroidPackageName(packageName, true, null)
+ },
-```java
-// Get an instance of AuthUI based on the default app
-Intent signInIntent =
- AuthUI.getInstance().createSignInIntentBuilder().build();
+ // Optional: Allow new accounts (default: true)
+ isNewAccountsAllowed = true,
-signInLauncher.launch(signInIntent);
-```
+ // Optional: Minimum password length (default: 6)
+ minimumPasswordLength = 8,
-To kick off the FirebaseUI sign in flow, use an `ActivityResultLauncher` to launch the Intent you built.
-See the [response codes](#response-codes) section below for more details on receiving the results of the sign in flow.
+ // Optional: Custom password validation rules
+ passwordValidationRules = listOf(
+ PasswordRule.MinimumLength(8),
+ PasswordRule.RequireUppercase,
+ PasswordRule.RequireLowercase,
+ PasswordRule.RequireDigit,
+ PasswordRule.RequireSpecialCharacter
+ )
+)
-##### Adding providers
+val configuration = authUIConfiguration {
+ providers = listOf(emailProvider)
+}
+```
-You can enable sign-in providers like Google Sign-In or Facebook Log In by calling the
-`setAvailableProviders` method:
+### Phone Number
-```java
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setAvailableProviders(Arrays.asList(
- new AuthUI.IdpConfig.GoogleBuilder().build(),
- new AuthUI.IdpConfig.FacebookBuilder().build(),
- new AuthUI.IdpConfig.TwitterBuilder().build(),
- new AuthUI.IdpConfig.MicrosoftBuilder().build(),
- new AuthUI.IdpConfig.YahooBuilder().build(),
- new AuthUI.IdpConfig.AppleBuilder().build(),
- new AuthUI.IdpConfig.EmailBuilder().build(),
- new AuthUI.IdpConfig.PhoneBuilder().build(),
- new AuthUI.IdpConfig.AnonymousBuilder().build()))
- .build();
-```
-
-##### Configuring Email Link Sign In
-
-To use email link sign in, you will first need to enable it in the Firebase Console. Additionally, you will
-also have to enable Firebase Dynamic Links.
-
-You can enable email link sign in by calling the `enableEmailLinkSignIn` on an `EmailBuilder` instance. You will also need
-to provide a valid `ActionCodeSettings` object with `setHandleCodeInApp` set to true. Additionally, you need to allowlist the
-URL you pass to `setUrl`; you can do so in the Firebase Console (Authentication -> Sign in Methods -> Authorized domains).
+Configure phone number authentication with SMS verification:
-```java
+```kotlin
+val phoneProvider = AuthProvider.Phone(
+ // Optional: Default phone number in international format
+ defaultNumber = "+15551234567",
-ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder()
- .setAndroidPackageName(/*yourPackageName*/, /*installIfNotAvailable*/true, /*minimumVersion*/null)
- .setHandleCodeInApp(true)
- .setUrl("https://google.com") // This URL needs to be allowlisted
- .build();
+ // Optional: Default country code (ISO alpha-2 format)
+ defaultCountryCode = "US",
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setAvailableProviders(Arrays.asList(
- new AuthUI.IdpConfig.EmailBuilder().enableEmailLinkSignIn()
- .setActionCodeSettings(actionCodeSettings).build())
- .build();
-```
+ // Optional: Allowed countries
+ allowedCountries = listOf("US", "CA", "GB"),
-If you want to catch the link in a specific activity, please follow the steps outlined [here](https://firebase.google.com/docs/auth/android/email-link-auth).
-Otherwise, the link will redirect to your launcher activity.
+ // Optional: SMS code length (default: 6)
+ smsCodeLength = 6,
-Once you catch the deep link, you will need to call verify that we can handle it for you. If we can, you need to then
-pass it to us via `setEmailLink`.
+ // Optional: Timeout for SMS delivery in seconds (default: 60)
+ timeout = 60L,
-```java
-if (AuthUI.canHandleIntent(getIntent())) {
- if (getIntent().getExtras() == null) {
- return;
- }
- String link = getIntent().getData().toString();
- if (link != null) {
- Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setEmailLink(link)
- .setAvailableProviders(getAvailableProviders())
- .build();
- signInLauncher.launch(signInIntent);
- }
+ // Optional: Enable instant verification (default: true)
+ isInstantVerificationEnabled = true
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(phoneProvider)
}
```
-#### Cross device support
+### Google Sign-In
-We support cross device email link sign in for the normal flows. It is not supported with anonymous user upgrade. By default,
-cross device support is enabled. You can disable it by calling `setForceSameDevice` on the `EmailBuilder` instance.
+Configure Google Sign-In with optional scopes and server client ID:
-##### Adding a ToS and privacy policy
+```kotlin
+val googleProvider = AuthProvider.Google(
+ // Required: Scopes to request
+ scopes = listOf("https://www.googleapis.com/auth/drive.file"),
-A terms of service URL and privacy policy URL are generally required:
+ // Optional: Server client ID for backend authentication
+ serverClientId = "YOUR_SERVER_CLIENT_ID.apps.googleusercontent.com",
-```java
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setAvailableProviders(...)
- .setTosAndPrivacyPolicyUrls("https://superapp.example.com/terms-of-service.html",
- "https://superapp.example.com/privacy-policy.html")
- .build();
+ // Optional: Custom OAuth parameters
+ customParameters = mapOf("prompt" to "select_account")
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(googleProvider)
+}
```
-##### Credential Manager
+### Facebook Login
-By default, FirebaseUI uses [Credential Manager](https://developer.android.com/identity/sign-in/credential-manager)
-to store the user's credentials and automatically sign users into your app on subsequent attempts.
-Using Credential Manager is recommended to provide the best user experience, but in some cases you may want
-to disable Credential Manager for testing or development. To disable Credential Manager, you can use the
-`setCredentialManagerEnabled` method when building your sign-in Intent:
+Configure Facebook Login with optional permissions:
-```java
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setCredentialManagerEnabled(false)
- .build();
-```
+```kotlin
+val facebookProvider = AuthProvider.Facebook(
+ // Optional: Facebook application ID (reads from strings.xml if not provided)
+ applicationId = "YOUR_FACEBOOK_APP_ID",
-##### Phone number authentication customization
+ // Optional: Permissions to request (default: ["email", "public_profile"])
+ scopes = listOf("email", "public_profile", "user_friends"),
-###### Setting a default phone number
-When using the phone verification provider and the number is known in advance, it is possible to
-provide a default phone number (in international format) that will be used to prepopulate the
-country code and phone number input fields. The user is still able to edit the number if desired.
+ // Optional: Custom OAuth parameters
+ customParameters = mapOf("display" to "popup")
+)
-```java
-IdpConfig phoneConfigWithDefaultNumber = new IdpConfig.PhoneBuilder()
- .setDefaultNumber("+123456789")
- .build();
+val configuration = authUIConfiguration {
+ providers = listOf(facebookProvider)
+}
```
-Alternatively, you can set the default country (alpha-2 format) to be shown in the country selector.
-
-```java
-IdpConfig phoneConfigWithDefaultNumber = new IdpConfig.PhoneBuilder()
- .setDefaultCountryIso("ca")
- .build();
+### Other OAuth Providers
+
+FirebaseUI supports Twitter, GitHub, Microsoft, Yahoo, and Apple:
+
+```kotlin
+// Twitter
+val twitterProvider = AuthProvider.Twitter(
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("lang" to "en")
+)
+
+// GitHub
+val githubProvider = AuthProvider.Github(
+ // Optional: Scopes to request (default: ["user:email"])
+ scopes = listOf("user:email", "read:user"),
+
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("allow_signup" to "false")
+)
+
+// Microsoft
+val microsoftProvider = AuthProvider.Microsoft(
+ // Optional: Scopes to request (default: ["openid", "profile", "email"])
+ scopes = listOf("openid", "profile", "email", "User.Read"),
+
+ // Optional: Tenant ID for Azure Active Directory
+ tenant = "YOUR_TENANT_ID",
+
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("prompt" to "consent")
+)
+
+// Yahoo
+val yahooProvider = AuthProvider.Yahoo(
+ // Optional: Scopes to request (default: ["openid", "profile", "email"])
+ scopes = listOf("openid", "profile", "email"),
+
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("language" to "en-us")
+)
+
+// Apple
+val appleProvider = AuthProvider.Apple(
+ // Optional: Scopes to request (default: ["name", "email"])
+ scopes = listOf("name", "email"),
+
+ // Optional: Locale for the sign-in page
+ locale = "en_US",
+
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("ui_locales" to "en-US")
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(
+ twitterProvider,
+ githubProvider,
+ microsoftProvider,
+ yahooProvider,
+ appleProvider
+ )
+}
```
-It is also possible to set a default country code along with a national number if a specific country
-is your app's target audience. This will take precedence over the full default phone number if both
-are provided.
+### Anonymous Authentication
-```java
-IdpConfig phoneConfigWithDefaultNumber = new IdpConfig.PhoneBuilder()
- .setDefaultNumber("ca", "23456789")
- .build();
+Enable anonymous authentication to let users use your app without signing in:
+
+```kotlin
+val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Anonymous()
+ )
+
+ // Enable anonymous user upgrade
+ isAnonymousUpgradeEnabled = true
+}
```
-###### Limiting the list of available countries in the country selector
+### Custom OAuth Provider
-You can limit the countries shown in the country selector list. By default, all countries are shown.
+Support any OAuth provider configured in the Firebase Console:
-You can provide a list of countries to allowlist or blocklist. You can populate these lists with
-ISO (alpha-2) and E164 formatted country codes.
+```kotlin
+val lineProvider = AuthProvider.GenericOAuth(
+ // Required: Provider name
+ providerName = "LINE",
-```java
-List allowedCountries = new ArrayList();
-allowedCountries.add("+1");
-allowedCountries.add("gr");
+ // Required: Provider ID as configured in Firebase Console
+ providerId = "oidc.line",
-IdpConfig phoneConfigWithAllowedCountries = new IdpConfig.PhoneBuilder()
- .setAllowedCountries(allowedCountries)
- .build();
-```
-All countries with the country code +1 will be present in the selector as well as Greece ('gr').
+ // Required: Scopes to request
+ scopes = listOf("profile", "openid", "email"),
-You may want to exclude a few countries from the list and avoid creating a allowlist with
-many countries. You can instead provide a list of countries to blocklist. By doing so, all countries
-excluding the ones you provide will be in the selector.
+ // Required: Custom OAuth parameters
+ customParameters = mapOf("prompt" to "consent"),
-```java
-List blockedCountries = new ArrayList();
-blockedCountries.add("+1");
-blockedCountries.add("gr");
+ // Required: Button label
+ buttonLabel = "Sign in with LINE",
+
+ // Optional: Custom button icon
+ buttonIcon = AuthUIAsset.Resource(R.drawable.ic_line),
+
+ // Optional: Custom button background color
+ buttonColor = Color(0xFF06C755),
+
+ // Optional: Custom button content color
+ contentColor = Color.White
+)
-IdpConfig phoneConfigWithBlockedCountries = new IdpConfig.PhoneBuilder()
- .setBlockedCountries(blockedCountries)
- .build();
+val configuration = authUIConfiguration {
+ providers = listOf(lineProvider)
+}
```
-The country code selector will exclude all countries with a country code of +1 and Greece ('gr').
+## Usage Patterns
+
+### High-Level API (Recommended)
+
+The high-level API provides a complete, opinionated authentication experience with minimal code:
+
+```kotlin
+@Composable
+fun AuthenticationScreen() {
+ val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Email(),
+ AuthProvider.Google(),
+ AuthProvider.Facebook(),
+ AuthProvider.Phone()
+ )
+ tosUrl = "https://example.com/terms"
+ privacyPolicyUrl = "https://example.com/privacy"
+ logo = Icons.Default.Lock
+ }
-Note: You can't provide both a list of countries to allowlist and blocklist. If you do, a runtime
-exception will be thrown.
+ FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result ->
+ val user = result.user
+ val isNewUser = result.additionalUserInfo?.isNewUser ?: false
+
+ if (isNewUser) {
+ // First-time user
+ navigateToOnboarding()
+ } else {
+ // Returning user
+ navigateToHome()
+ }
+ },
+ onSignInFailure = { exception ->
+ when (exception) {
+ is AuthException.NetworkException -> {
+ showSnackbar("No internet connection")
+ }
+ is AuthException.TooManyRequestsException -> {
+ showSnackbar("Too many attempts. Please try again later.")
+ }
+ else -> {
+ showSnackbar("Authentication failed: ${exception.message}")
+ }
+ }
+ },
+ onSignInCancelled = {
+ navigateBack()
+ }
+ )
+}
+```
-This change is purely UI based. We do not restrict users from signing in with their phone number.
-They will simply be unable to choose their country in the selector, but there may be another country
-sharing the same country code (e.g. US and CA are +1).
+### Low-Level API (Advanced)
-#####
+For maximum control, use the `AuthFlowController`:
-### Handling the sign-in response
+```kotlin
+class AuthActivity : ComponentActivity() {
+ private lateinit var controller: AuthFlowController
-#### Response codes
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
-The authentication flow provides several response codes of which the most common are as follows:
-`Activity.RESULT_OK` if a user is signed in, `Activity.RESULT_CANCELED` if the user manually canceled the sign in,
-`ErrorCodes.NO_NETWORK` if sign in failed due to a lack of network connectivity,
-and `ErrorCodes.UNKNOWN_ERROR` for all other errors.
-Typically, the only recourse for most apps if sign in fails is to ask
-the user to sign in again later, or proceed with anonymous sign-in if supported.
+ val authUI = FirebaseAuthUI.getInstance()
+ val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email(), AuthProvider.Google())
+ }
-```java
-private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
- IdpResponse response = result.getIdpResponse();
+ controller = authUI.createAuthFlow(configuration)
- if (result.getResultCode() == RESULT_OK) {
- // Successfully signed in
- startActivity(SignedInActivity.createIntent(this, response));
- finish();
- } else {
- // Sign in failed
- if (response == null) {
- // User pressed back button
- showSnackbar(R.string.sign_in_cancelled);
- return;
+ lifecycleScope.launch {
+ val state = controller.start()
+ handleAuthState(state)
}
+ }
- if (response.getError().getErrorCode() == ErrorCodes.NO_NETWORK) {
- showSnackbar(R.string.no_internet_connection);
- return;
+ private fun handleAuthState(state: AuthState) {
+ when (state) {
+ is AuthState.Success -> {
+ // Successfully signed in
+ val user = state.result.user
+ startActivity(Intent(this, MainActivity::class.java))
+ finish()
+ }
+ is AuthState.Error -> {
+ // Handle error
+ AlertDialog.Builder(this)
+ .setTitle("Authentication Failed")
+ .setMessage(state.exception.message)
+ .setPositiveButton("OK", null)
+ .show()
+ }
+ is AuthState.RequiresMfa -> {
+ // User needs to complete MFA challenge
+ showMfaChallengeDialog(state.resolver)
+ }
+ is AuthState.RequiresEmailVerification -> {
+ // Email verification needed
+ showEmailVerificationScreen(state.user)
+ }
+ is AuthState.Cancelled -> {
+ // User cancelled authentication
+ finish()
+ }
+ else -> {
+ // Handle other states
+ }
}
+ }
- showSnackbar(R.string.unknown_error);
- Log.e(TAG, "Sign-in error: ", response.getError());
+ override fun onDestroy() {
+ super.onDestroy()
+ controller.dispose()
}
}
```
-Alternatively, you can register a listener for authentication state changes;
-see the Firebase Auth documentation to
-[get the currently signed-in user](https://firebase.google.com/docs/auth/android/manage-users#get_the_currently_signed-in_user)
-and [register an AuthStateListener](https://firebase.google.com/docs/reference/android/com/google/firebase/auth/FirebaseAuth.html#addAuthStateListener(com.google.firebase.auth.FirebaseAuth.AuthStateListener)).
+### Custom UI with Slots
+
+For complete UI control while keeping authentication logic, use content slots:
+
+```kotlin
+@Composable
+fun CustomEmailAuth() {
+ val emailConfig = AuthProvider.Email(
+ passwordValidationRules = listOf(
+ PasswordRule.MinimumLength(8),
+ PasswordRule.RequireDigit
+ )
+ )
+
+ EmailAuthScreen(
+ configuration = emailConfig,
+ onSuccess = { /* ... */ },
+ onError = { /* ... */ },
+ onCancel = { /* ... */ }
+ ) { state ->
+ // Custom UI with full control
+ when (state.mode) {
+ EmailAuthMode.SignIn -> {
+ CustomSignInUI(state)
+ }
+ EmailAuthMode.SignUp -> {
+ CustomSignUpUI(state)
+ }
+ EmailAuthMode.ResetPassword -> {
+ CustomResetPasswordUI(state)
+ }
+ }
+ }
+}
-Note: if you choose to use an `AuthStateListener`, make sure to unregister it before launching
-the FirebaseUI flow and re-register it after the flow returns. FirebaseUI performs auth operations
-internally which may trigger the listener before the flow is complete.
+@Composable
+fun CustomSignInUI(state: EmailAuthContentState) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(24.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = "Welcome Back!",
+ style = MaterialTheme.typography.headlineLarge
+ )
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ OutlinedTextField(
+ value = state.email,
+ onValueChange = state.onEmailChange,
+ label = { Text("Email") },
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ OutlinedTextField(
+ value = state.password,
+ onValueChange = state.onPasswordChange,
+ label = { Text("Password") },
+ visualTransformation = PasswordVisualTransformation(),
+ keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
+ modifier = Modifier.fillMaxWidth()
+ )
+
+ if (state.error != null) {
+ Text(
+ text = state.error!!,
+ color = MaterialTheme.colorScheme.error,
+ modifier = Modifier.padding(top = 8.dp)
+ )
+ }
-#### ID tokens
+ Spacer(modifier = Modifier.height(24.dp))
+
+ Button(
+ onClick = state.onSignInClick,
+ enabled = !state.isLoading,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ if (state.isLoading) {
+ CircularProgressIndicator(modifier = Modifier.size(24.dp))
+ } else {
+ Text("Sign In")
+ }
+ }
-To retrieve the ID token that the IDP returned, you can extract an `IdpResponse` from the result
-Intent.
+ TextButton(onClick = state.onGoToResetPassword) {
+ Text("Forgot Password?")
+ }
-```java
-private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
- if (result.getResultCode() == RESULT_OK) {
- // Successfully signed in
- IdpResponse response = result.getIdpResponse();
- startActivity(new Intent(this, WelcomeBackActivity.class)
- .putExtra("my_token", response.getIdpToken()));
+ TextButton(onClick = state.onGoToSignUp) {
+ Text("Create Account")
+ }
}
}
```
-Twitter also returns an AuthToken Secret which can be accessed with `response.getIdpSecret()`.
+Similarly, create custom phone authentication UI:
+
+```kotlin
+@Composable
+fun CustomPhoneAuth() {
+ val phoneConfig = AuthProvider.Phone(defaultCountryCode = "US")
+
+ PhoneAuthScreen(
+ configuration = phoneConfig,
+ onSuccess = { /* ... */ },
+ onError = { /* ... */ },
+ onCancel = { /* ... */ }
+ ) { state ->
+ when (state.step) {
+ PhoneAuthStep.EnterPhoneNumber -> {
+ CustomPhoneNumberInput(state)
+ }
+ PhoneAuthStep.EnterVerificationCode -> {
+ CustomVerificationCodeInput(state)
+ }
+ }
+ }
+}
+```
-#### User metadata
+## Multi-Factor Authentication
-While `IdpResponse` provides user information about a specific sign-in instance, it is usually
-preferable to find the user name, email, and other metadata directly from the currently signed-in
-`FirebaseUser` instance (`auth.getCurrentUser()`). For example, you could determine if the user
-who just signed in is an existing or new one by comparing the user's creation and last sign-in time:
+### MFA Configuration
-```java
-FirebaseUserMetadata metadata = auth.getCurrentUser().getMetadata();
-if (metadata.getCreationTimestamp() == metadata.getLastSignInTimestamp()) {
- // The user is new, show them a fancy intro screen!
-} else {
- // This is an existing user, show them a welcome back screen.
+Enable and configure Multi-Factor Authentication:
+
+```kotlin
+val mfaConfig = MfaConfiguration(
+ // Allowed MFA factors (default: [Sms, Totp])
+ allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp),
+
+ // Optional: Require MFA enrollment (default: false)
+ requireEnrollment = false,
+
+ // Optional: Enable recovery codes (default: true)
+ enableRecoveryCodes = true
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ isMfaEnabled = true
}
```
-### Sign out
-
-With the integrations provided by AuthUI, signing out a user is a multi-stage process:
+### MFA Enrollment
+
+Prompt users to enroll in MFA after sign-in:
+
+```kotlin
+@Composable
+fun MfaEnrollmentFlow() {
+ val currentUser = FirebaseAuth.getInstance().currentUser
+
+ if (currentUser != null) {
+ val mfaConfig = MfaConfiguration(
+ allowedFactors = listOf(MfaFactor.Sms, MfaFactor.Totp)
+ )
+
+ MfaEnrollmentScreen(
+ user = currentUser,
+ configuration = mfaConfig,
+ onEnrollmentComplete = {
+ Toast.makeText(context, "MFA enrolled successfully!", Toast.LENGTH_SHORT).show()
+ navigateToHome()
+ },
+ onSkip = {
+ navigateToHome()
+ }
+ )
+ }
+}
+```
-1. The user must be signed out of the FirebaseAuth instance.
-1. Credential Manager must be instructed to clear the current user credential state from
- all credential providers.
-1. If the current user signed in using either Google or Facebook, the user must
- also be signed out using the associated API for that authentication method.
- This typically ensures that the user will not be automatically signed-in
- using the current account when using that authentication method again from
- the authentication method picker, which would also prevent the user from
- switching between accounts on the same provider.
+Or with custom UI:
+
+```kotlin
+MfaEnrollmentScreen(
+ user = currentUser,
+ configuration = mfaConfig,
+ onEnrollmentComplete = { /* ... */ },
+ onSkip = { /* ... */ }
+) { state ->
+ when (state.step) {
+ MfaEnrollmentStep.SelectFactor -> {
+ CustomFactorSelectionUI(state)
+ }
+ MfaEnrollmentStep.ConfigureSms -> {
+ CustomSmsConfigurationUI(state)
+ }
+ MfaEnrollmentStep.ConfigureTotp -> {
+ CustomTotpConfigurationUI(state)
+ }
+ MfaEnrollmentStep.VerifyFactor -> {
+ CustomVerificationUI(state)
+ }
+ MfaEnrollmentStep.ShowRecoveryCodes -> {
+ CustomRecoveryCodesUI(state)
+ }
+ }
+}
+```
-In order to make this process easier, AuthUI provides a simple `signOut` method
-to encapsulate this behavior. The method returns a `Task` which is marked
-completed once all necessary sign-out operations are completed:
+### MFA Challenge
+
+Handle MFA challenges during sign-in. The challenge is automatically detected:
+
+```kotlin
+FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result ->
+ navigateToHome()
+ },
+ onSignInFailure = { exception ->
+ // MFA challenges are handled automatically by FirebaseAuthScreen
+ // But you can also handle them manually:
+ if (exception is AuthException.MfaRequiredException) {
+ showMfaChallengeScreen(exception.resolver)
+ }
+ }
+)
+```
-```java
-public void onClick(View v) {
-if (v.getId() == R.id.sign_out) {
- AuthUI.getInstance()
- .signOut(this)
- .addOnCompleteListener(new OnCompleteListener() {
- public void onComplete(@NonNull Task task) {
- // user is now signed out
- startActivity(new Intent(MyActivity.this, SignInActivity.class));
- finish();
+Or handle manually:
+
+```kotlin
+@Composable
+fun ManualMfaChallenge(resolver: MultiFactorResolver) {
+ MfaChallengeScreen(
+ resolver = resolver,
+ onChallengeComplete = { assertion ->
+ // Complete sign-in with the assertion
+ lifecycleScope.launch {
+ try {
+ val result = resolver.resolveSignIn(assertion)
+ navigateToHome()
+ } catch (e: Exception) {
+ showError(e)
+ }
}
- });
+ },
+ onCancel = {
+ navigateBack()
+ }
+ )
+}
+```
+
+## Theming & Customization
+
+### Material Theme Integration
+
+FirebaseUI automatically inherits your app's Material Theme:
+
+```kotlin
+@Composable
+fun App() {
+ MyAppTheme { // Your existing Material3 theme
+ val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ theme = AuthUITheme.fromMaterialTheme() // Inherits MyAppTheme
+ }
+
+ FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { /* ... */ }
+ )
}
}
```
-### Deleting accounts
+### Custom Theme
+
+Create a completely custom theme:
+
+```kotlin
+val customTheme = AuthUITheme(
+ colorScheme = darkColorScheme(
+ primary = Color(0xFF6200EE),
+ onPrimary = Color.White,
+ primaryContainer = Color(0xFF3700B3),
+ secondary = Color(0xFF03DAC6)
+ ),
+ typography = Typography(
+ displayLarge = TextStyle(fontSize = 57.sp, fontWeight = FontWeight.Bold),
+ bodyLarge = TextStyle(fontSize = 16.sp)
+ ),
+ shapes = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(8.dp),
+ large = RoundedCornerShape(16.dp)
+ )
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ theme = customTheme
+}
+```
-With the integrations provided by FirebaseUI Auth, deleting a user is a multi-stage process:
+### Provider Button Styling
+
+Customize individual provider button styling:
+
+```kotlin
+val customProviderStyles = mapOf(
+ "google.com" to AuthUITheme.ProviderStyle(
+ backgroundColor = Color.White,
+ contentColor = Color(0xFF757575),
+ iconTint = null, // Use original colors
+ shape = RoundedCornerShape(8.dp),
+ elevation = 4.dp
+ ),
+ "facebook.com" to AuthUITheme.ProviderStyle(
+ backgroundColor = Color(0xFF1877F2),
+ contentColor = Color.White,
+ shape = RoundedCornerShape(12.dp),
+ elevation = 0.dp
+ )
+)
+
+val customTheme = AuthUITheme.Default.copy(
+ providerStyles = customProviderStyles
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Google(), AuthProvider.Facebook())
+ theme = customTheme
+}
+```
-1. The user must be deleted from Firebase Auth.
-1. Credential Manager must be told to delete any existing Credentials for the user, so
- that they are not automatically prompted to sign in with a saved credential in the future.
+## Advanced Features
-This process is encapsulated by the `AuthUI.delete()` method, which returns a `Task` representing
-the entire operation:
+### Anonymous User Upgrade
-```java
-AuthUI.getInstance()
- .delete(this)
- .addOnCompleteListener(new OnCompleteListener() {
- @Override
- public void onComplete(@NonNull Task task) {
- if (task.isSuccessful()) {
- // Deletion succeeded
- } else {
- // Deletion failed
+Seamlessly upgrade anonymous users to permanent accounts:
+
+```kotlin
+// 1. Configure anonymous authentication with upgrade enabled
+val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Anonymous(),
+ AuthProvider.Email(),
+ AuthProvider.Google()
+ )
+ isAnonymousUpgradeEnabled = true
+}
+
+// 2. When user wants to create a permanent account, show auth UI
+// The library automatically upgrades the anonymous account if one exists
+FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result ->
+ // Anonymous account has been upgraded (if user was anonymous)!
+ Toast.makeText(this, "Account created!", Toast.LENGTH_SHORT).show()
+ }
+)
+```
+
+### Email Link Sign-In
+
+Enable passwordless email link authentication:
+
+```kotlin
+val emailProvider = AuthProvider.Email(
+ isEmailLinkSignInEnabled = true,
+ emailLinkActionCodeSettings = actionCodeSettings {
+ url = "https://example.com/auth"
+ handleCodeInApp = true
+ setAndroidPackageName(packageName, true, "12")
+ },
+ passwordValidationRules = emptyList()
+)
+
+val configuration = authUIConfiguration {
+ providers = listOf(emailProvider)
+}
+```
+
+**High-Level API** - Direct `FirebaseAuthScreen` usage:
+
+```kotlin
+// In your Activity that handles the deep link:
+override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val authUI = FirebaseAuthUI.getInstance()
+ val emailLink = if (authUI.canHandleIntent(intent)) {
+ intent.data?.toString()
+ } else {
+ null
+ }
+
+ if (emailLink != null) {
+ setContent {
+ FirebaseAuthScreen(
+ configuration = configuration,
+ emailLink = emailLink,
+ onSignInSuccess = { result ->
+ // Email link sign-in successful
+ },
+ onSignInFailure = { exception ->
+ // Handle error
+ },
+ onSignInCancelled = {
+ finish()
}
- }
- });
+ )
+ }
+ }
+}
```
-### Upgrading anonymous users
+**Low-Level API** - Using `AuthFlowController`:
-#### Enabling anonymous user upgrade
+```kotlin
+import com.firebase.ui.auth.compose.util.EmailLinkConstants
-When an anonymous user signs in or signs up with a permanent account, you want
-to be sure that the user can continue with what they were doing before signing up.
-For example, an anonymous user might have items in their shopping cart.
-At check-out, you prompt the user to sign in or sign up. After the user is
-signed in, the user's shopping cart should contain any items the user added
-while signed in anonymously.
+// In your Activity that handles the deep link:
+override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
-To support this behavior, FirebaseUI makes it easy to "upgrade" an anonymous
-account to a permanent account. To do so, simply call `enableAnonymousUsersAutoUpgrade()`
-when you configure the sign-in UI (this option is disabled by default).
+ val authUI = FirebaseAuthUI.getInstance()
+ val emailLink = if (authUI.canHandleIntent(intent)) {
+ intent.data?.toString()
+ } else {
+ null
+ }
-For example:
-```java
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .enableAnonymousUsersAutoUpgrade()
- ...
- .build();
+ if (emailLink != null) {
+ val controller = authUI.createAuthFlow(configuration)
+ val intent = controller.createIntent(this).apply {
+ putExtra(EmailLinkConstants.EXTRA_EMAIL_LINK, emailLink)
+ }
+ authLauncher.launch(intent)
+ }
+}
+
+// Handle result
+private val authLauncher = registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+) { result ->
+ when (result.resultCode) {
+ Activity.RESULT_OK -> {
+ // Email link sign-in successful
+ }
+ Activity.RESULT_CANCELED -> {
+ // Handle error or cancellation
+ }
+ }
+}
```
-With this enabled, FirebaseUI will link the credential on sign-in with the anonymous account
-using Firebase Auth's `linkWithCredential` method:
-```java
-FirebaseAuth.getInstance().getCurrentUser().linkWithCredential(permanentCredential);
+Add the intent filter to your `AndroidManifest.xml`:
+
+```xml
+
+
+
+
+
+
```
-#### Handling anonymous user upgrade merge conflicts
+### Password Validation Rules
+
+Enforce custom password requirements:
+
+```kotlin
+val emailProvider = AuthProvider.Email(
+ emailLinkActionCodeSettings = null,
+ minimumPasswordLength = 10,
+ passwordValidationRules = listOf(
+ PasswordRule.MinimumLength(10),
+ PasswordRule.RequireUppercase,
+ PasswordRule.RequireLowercase,
+ PasswordRule.RequireDigit,
+ PasswordRule.RequireSpecialCharacter,
+ PasswordRule.Custom(
+ regex = Regex("^(?!.*password).*$"),
+ errorMessage = "Password cannot contain the word 'password'"
+ )
+ )
+)
+```
-There is an issue when an anonymous user tries to upgrade to an existing Firebase user.
+### Credential Manager Integration
-For example, a user may have previously signed up with a Google credential on a different device.
-If they are signed in anonymously and they attempt to upgrade with the existing Google account,
-a `FirebaseAuthUserCollisionException` will be thrown by Firebase Auth as an existing user
-cannot be linked to another existing user. No two users can share the same credential. In this case,
-we need to merge the data from both users before we can upgrade the anonymous user.
+FirebaseUI automatically integrates with Android's Credential Manager API to save and retrieve credentials. This enables:
-The process of storing the anonymous users data, signing in with the credential, and copying the
-data over to the existing account is left to the developer.
+- **Automatic sign-in** for returning users
+- **One-tap sign-in** across apps
+- **Secure credential storage**
-When linking is unsuccessful due to user collision, an error with code
-`ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT` will be returned to the callback of your `ActivityResultLauncher`. A valid
-non-anonymous credential can be obtained from the `IdpResponse` via `getCredentialForLinking()`.
+Credential Manager is enabled by default. To disable:
-**Example:**
-```java
-private void onSignInResult(FirebaseAuthUIAuthenticationResult result) {
- IdpResponse response = result.getIdpResponse();
+```kotlin
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ isCredentialManagerEnabled = false
+}
+```
- if (result.getResultCode() == RESULT_OK) {
- // Successfully signed in
- // ...
- } else {
- // Sign in failed
- if (response.getError().getErrorCode() == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
- // Store relevant anonymous user data
- ...
- // Get the non-anonymous credential from the response
- AuthCredential nonAnonymousCredential = response.getCredentialForLinking();
- // Sign in with credential
- FirebaseAuth.getInstance().signInWithCredential(nonAnonymousCredential)
- .addOnSuccessListener(new OnSuccessListener() {
- @Override
- public void onSuccess(AuthResult result) {
- // Copy over anonymous user data to signed in user
- ...
+### Sign Out & Account Deletion
+
+**Sign Out:**
+
+```kotlin
+@Composable
+fun SettingsScreen() {
+ val context = LocalContext.current
+ val authUI = remember { FirebaseAuthUI.getInstance() }
+
+ Button(
+ onClick = {
+ lifecycleScope.launch {
+ authUI.signOut(context)
+ // User is signed out, navigate to auth screen
+ navigateToAuth()
+ }
+ }
+ ) {
+ Text("Sign Out")
+ }
+}
+```
+
+**Delete Account:**
+
+```kotlin
+Button(
+ onClick = {
+ lifecycleScope.launch {
+ try {
+ authUI.delete(context)
+ // Account deleted successfully
+ navigateToAuth()
+ } catch (e: Exception) {
+ when (e) {
+ is FirebaseAuthRecentLoginRequiredException -> {
+ // User needs to reauthenticate
+ showReauthenticationDialog()
+ }
+ else -> {
+ showError("Failed to delete account: ${e.message}")
}
- });
+ }
+ }
}
}
- updateUI();
+) {
+ Text("Delete Account")
}
```
-### Custom Layout
+## Localization
-The first screen shown in most cases is the auth method picker screen, where the user selects
-from a list of authentication methods. While customization in other screens of FirebaseUI is
-limited to themes, this screen can be fully customized with your own XML layout.
+FirebaseUI includes default English strings. To add custom localization:
-To customize the auth method picker screen, build an `AuthMethodPickerLayout` object and pass
-it to the `SignInIntentBuilder` before launching the AuthUI flow:
+```kotlin
+class SpanishStringProvider(context: Context) : AuthUIStringProvider {
+ override fun signInWithEmail() = "Iniciar sesión con correo"
+ override fun signInWithGoogle() = "Iniciar sesión con Google"
+ override fun signInWithFacebook() = "Iniciar sesión con Facebook"
+ override fun invalidEmail() = "Correo inválido"
+ override fun weakPassword() = "Contraseña débil"
+ // ... implement all other required methods
+}
-```java
-// You must provide a custom layout XML resource and configure at least one
-// provider button ID. It's important that that you set the button ID for every provider
-// that you have enabled.
-AuthMethodPickerLayout customLayout = new AuthMethodPickerLayout
- .Builder(R.layout.your_custom_layout_xml)
- .setGoogleButtonId(R.id.bar)
- .setEmailButtonId(R.id.foo)
- // ...
- .setTosAndPrivacyPolicyId(R.id.baz)
- .build();
-
-Intent signInIntent =
- AuthUI.getInstance(this).createSignInIntentBuilder()
- // ...
- .setAuthMethodPickerLayout(customLayout)
- .build();
+val configuration = authUIConfiguration {
+ providers = listOf(AuthProvider.Email())
+ stringProvider = SpanishStringProvider(context)
+ locale = Locale("es", "ES")
+}
```
-### Strings
-
-Ensure an `app_name` resource is defined your `strings.xml` file like so:
+Or override individual strings in your `strings.xml`:
```xml
- My App
-
+
+ Sign in with Google
+ Sign in with Email
+ Invalid email address
+
```
-If you wish to change other strings in the UI, the existing strings can be overridden
-by name in your application. See the module's [strings.xml](src/main/res/values/strings.xml) file
-and simply redefine a string to change it:
+## Error Handling
-```xml
-
-
- Creating your shiny new account...
-
+FirebaseUI provides a comprehensive exception hierarchy:
+
+```kotlin
+FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInFailure = { exception ->
+ when (exception) {
+ is AuthException.NetworkException -> {
+ showSnackbar("No internet connection. Please check your network.")
+ }
+ is AuthException.InvalidCredentialsException -> {
+ showSnackbar("Invalid email or password.")
+ }
+ is AuthException.UserNotFoundException -> {
+ showSnackbar("No account found with this email.")
+ }
+ is AuthException.WeakPasswordException -> {
+ showSnackbar("Password is too weak. Please use a stronger password.")
+ }
+ is AuthException.EmailAlreadyInUseException -> {
+ showSnackbar("An account already exists with this email.")
+ }
+ is AuthException.TooManyRequestsException -> {
+ showSnackbar("Too many attempts. Please try again later.")
+ }
+ is AuthException.MfaRequiredException -> {
+ // Handled automatically by FirebaseAuthScreen
+ // or show custom MFA challenge
+ }
+ is AuthException.AccountLinkingRequiredException -> {
+ // Account needs to be linked
+ showAccountLinkingDialog(exception)
+ }
+ is AuthException.AuthCancelledException -> {
+ // User cancelled the flow
+ navigateBack()
+ }
+ is AuthException.UnknownException -> {
+ showSnackbar("An unexpected error occurred: ${exception.message}")
+ Log.e(TAG, "Auth error", exception)
+ }
+ }
+ }
+)
+```
+
+Use the `ErrorRecoveryDialog` for automatic error handling:
+
+```kotlin
+var errorState by remember { mutableStateOf(null) }
+
+errorState?.let { error ->
+ ErrorRecoveryDialog(
+ error = error,
+ onRetry = {
+ // Retry the authentication
+ errorState = null
+ retryAuthentication()
+ },
+ onDismiss = {
+ errorState = null
+ },
+ onRecover = { exception ->
+ // Custom recovery logic for specific errors
+ when (exception) {
+ is AuthException.AccountLinkingRequiredException -> {
+ linkAccounts(exception)
+ }
+ }
+ }
+ )
+}
```
-**Note:** String resource names aren't considered part of the public API and might
-therefore change and break your app between library updates. We recommend looking
-at a diff of the `strings.xml` file before updating FirebaseUI.
+## Migration Guide
+
+### From FirebaseUI Auth 9.x (View-based)
-## OAuth scope customization
+The new Compose library has a completely different architecture. Here's how to migrate:
-### Google
-By default, FirebaseUI requests the `email` and `profile` scopes when using Google Sign-In. If you
-would like to request additional scopes from the user, call `setScopes` on the
-`AuthUI.IdpConfig.GoogleBuilder` when initializing FirebaseUI.
+**Old (9.x - View/Activity based):**
```java
-// For a list of all scopes, see:
-// https://developers.google.com/identity/protocols/googlescopes
-AuthUI.IdpConfig googleIdp = new AuthUI.IdpConfig.GoogleBuilder()
- .setScopes(Arrays.asList(Scopes.GAMES))
- .build();
+// Old approach with startActivityForResult
+Intent signInIntent = AuthUI.getInstance()
+ .createSignInIntentBuilder()
+ .setAvailableProviders(Arrays.asList(
+ new AuthUI.IdpConfig.EmailBuilder().build(),
+ new AuthUI.IdpConfig.GoogleBuilder().build()
+ ))
+ .setTheme(R.style.AppTheme)
+ .build();
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setAvailableProviders(Arrays.asList(googleIdp, ...))
- .build();
+signInLauncher.launch(signInIntent);
```
-### Facebook
+**New (10.x - Compose based):**
-By default, FirebaseUI requests the `email` and `public_profile` permissions when initiating
-Facebook Login. If you would like to request additional permissions from the user, call
-`setPermissions` on the `AuthUI.IdpConfig.FacebookBuilder` when initializing FirebaseUI.
+```kotlin
+// New approach with Composable
+val configuration = authUIConfiguration {
+ providers = listOf(
+ AuthProvider.Email(),
+ AuthProvider.Google()
+ )
+ theme = AuthUITheme.fromMaterialTheme()
+}
-```java
-// For a list of permissions see:
-// https://developers.facebook.com/docs/facebook-login/permissions
+FirebaseAuthScreen(
+ configuration = configuration,
+ onSignInSuccess = { result -> /* ... */ },
+ onSignInFailure = { exception -> /* ... */ },
+ onSignInCancelled = { /* ... */ }
+)
+```
-AuthUI.IdpConfig facebookIdp = new AuthUI.IdpConfig.FacebookBuilder()
- .setPermissions(Arrays.asList("user_friends"))
- .build();
+**Key Changes:**
-Intent signInIntent =
- AuthUI.getInstance()
- .createSignInIntentBuilder()
- .setAvailableProviders(Arrays.asList(facebookIdp, ...))
- .build();
-```
+1. **Pure Compose** - No more Activities or Intents, everything is Composable
+2. **Configuration DSL** - Use `authUIConfiguration {}` instead of `createSignInIntentBuilder()`
+3. **Provider Builders** - `AuthProvider.Email()` instead of `IdpConfig.EmailBuilder().build()`
+4. **Callbacks** - Direct callback parameters instead of `ActivityResultLauncher`
+5. **Theming** - `AuthUITheme` instead of `R.style` theme resources
+6. **State Management** - Reactive `Flow` instead of `AuthStateListener`
+
+**Migration Checklist:**
+
+- [ ] Update dependency to `firebase-ui-auth:10.0.0`
+- [ ] Convert Activities to Composables
+- [ ] Replace Intent-based flow with `FirebaseAuthScreen`
+- [ ] Update configuration from builder pattern to DSL
+- [ ] Replace theme resources with `AuthUITheme`
+- [ ] Update error handling from result codes to `AuthException`
+- [ ] Remove `ActivityResultLauncher` and use direct callbacks
+- [ ] Update sign-out/delete to use suspend functions
+
+For a complete migration example, see the [migration guide](MIGRATION.md).
+
+---
+
+## Contributing
+
+Contributions are welcome! Please read our [contribution guidelines](../CONTRIBUTING.md) before submitting PRs.
+
+## License
+
+FirebaseUI Auth is available under the [Apache 2.0 license](../LICENSE).
-### Twitter
+## Support
-Twitter permissions can only be configured through [Twitter's developer console](https://apps.twitter.com/).
+- [Firebase Documentation](https://firebase.google.com/docs/auth)
+- [GitHub Issues](https://github.com/firebase/FirebaseUI-Android/issues)
+- [Stack Overflow](https://stackoverflow.com/questions/tagged/firebaseui)
diff --git a/auth/demo.gif b/auth/demo.gif
index 9d844015c..4ef178890 100644
Binary files a/auth/demo.gif and b/auth/demo.gif differ
diff --git a/auth/src/main/AndroidManifest.xml b/auth/src/main/AndroidManifest.xml
index bcd60c4ca..7b62c07ac 100644
--- a/auth/src/main/AndroidManifest.xml
+++ b/auth/src/main/AndroidManifest.xml
@@ -7,8 +7,7 @@
-
-
+
@@ -37,78 +36,6 @@
android:name="com.facebook.sdk.ClientToken"
android:value="@string/facebook_client_token"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/AuthException.kt b/auth/src/main/java/com/firebase/ui/auth/AuthException.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/AuthException.kt
rename to auth/src/main/java/com/firebase/ui/auth/AuthException.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/AuthFlowController.kt b/auth/src/main/java/com/firebase/ui/auth/AuthFlowController.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/AuthFlowController.kt
rename to auth/src/main/java/com/firebase/ui/auth/AuthFlowController.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthMethodPickerLayout.java b/auth/src/main/java/com/firebase/ui/auth/AuthMethodPickerLayout.java
deleted file mode 100644
index 3644f2bcc..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/AuthMethodPickerLayout.java
+++ /dev/null
@@ -1,211 +0,0 @@
-package com.firebase.ui.auth;
-
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.google.firebase.auth.EmailAuthProvider;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.GithubAuthProvider;
-import com.google.firebase.auth.GoogleAuthProvider;
-import com.google.firebase.auth.PhoneAuthProvider;
-import com.google.firebase.auth.TwitterAuthProvider;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import androidx.annotation.IdRes;
-import androidx.annotation.LayoutRes;
-import androidx.annotation.NonNull;
-
-/**
- * Layout model to help customizing layout of the AuthMethodPickerActivity screen,
- * where the user is presented with a list of sign-in providers to choose from.
- *
- * To create a new instance, use {@link AuthMethodPickerLayout.Builder}.
- */
-public class AuthMethodPickerLayout implements Parcelable {
-
- @LayoutRes
- private int mainLayout;
-
- @IdRes
- private int tosPpView = -1;
-
- /**
- * PROVIDER_ID -> IdRes of the Button
- */
- private Map providersButton;
-
- private AuthMethodPickerLayout() {}
-
- private AuthMethodPickerLayout(@NonNull Parcel in) {
- this.mainLayout = in.readInt();
- this.tosPpView = in.readInt();
-
- Bundle buttonsBundle = in.readBundle(getClass().getClassLoader());
- this.providersButton = new HashMap<>();
- for (String key : buttonsBundle.keySet()) {
- this.providersButton.put(key, buttonsBundle.getInt(key));
- }
- }
-
- @LayoutRes
- public int getMainLayout() {
- return mainLayout;
- }
-
- @IdRes
- public int getTosPpView() {
- return tosPpView;
- }
-
- public Map getProvidersButton() {
- return providersButton;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int flags) {
- parcel.writeInt(mainLayout);
- parcel.writeInt(tosPpView);
-
- Bundle bundle = new Bundle();
- for (String key : providersButton.keySet()) {
- bundle.putInt(key, providersButton.get(key));
- }
- parcel.writeBundle(bundle);
- }
-
- public static final Creator CREATOR = new Creator() {
-
- @Override
- public AuthMethodPickerLayout createFromParcel(Parcel in) {
- return new AuthMethodPickerLayout(in);
- }
-
- @Override
- public AuthMethodPickerLayout[] newArray(int size) {
- return new AuthMethodPickerLayout[size];
- }
- };
-
- /**
- * Builder for {@link AuthMethodPickerLayout}.
- */
- public static class Builder {
-
- private Map providersMapping;
- private AuthMethodPickerLayout instance;
-
- /**
- * Create a new builder, specifying the ID of the XML layout resource to be sued.
- */
- public Builder(@LayoutRes int mainLayout) {
- instance = new AuthMethodPickerLayout();
- instance.mainLayout = mainLayout;
- providersMapping = new HashMap<>();
- }
-
- /**
- * Set the ID of the Google sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setGoogleButtonId(@IdRes int googleBtn) {
- providersMapping.put(GoogleAuthProvider.PROVIDER_ID, googleBtn);
- return this;
- }
-
- /**
- * Set the ID of the Facebook sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setFacebookButtonId(@IdRes int facebookBtn) {
- providersMapping.put(FacebookAuthProvider.PROVIDER_ID, facebookBtn);
- return this;
- }
-
- /**
- * Set the ID of the Twitter sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setTwitterButtonId(@IdRes int twitterBtn) {
- providersMapping.put(TwitterAuthProvider.PROVIDER_ID, twitterBtn);
- return this;
- }
-
- /**
- * Set the ID of the Email sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setEmailButtonId(@IdRes int emailButton) {
- providersMapping.put(EmailAuthProvider.PROVIDER_ID, emailButton);
- return this;
- }
-
- /**
- * Set the ID of the Phone Number sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setPhoneButtonId(@IdRes int phoneButton) {
- providersMapping.put(PhoneAuthProvider.PROVIDER_ID, phoneButton);
- return this;
- }
-
- /**
- * Set the ID of the Anonymous sign in button in the custom layout.
- */
- public AuthMethodPickerLayout.Builder setAnonymousButtonId(@IdRes int anonymousButton) {
- providersMapping.put(AuthUI.ANONYMOUS_PROVIDER, anonymousButton);
- return this;
- }
-
- public AuthMethodPickerLayout.Builder setGithubButtonId(
- @IdRes int githubButtonId) {
- providersMapping.put(GithubAuthProvider.PROVIDER_ID, githubButtonId);
- return this;
- }
-
- public AuthMethodPickerLayout.Builder setMicrosoftButtonId(
- @IdRes int microsoftButtonId) {
- providersMapping.put(AuthUI.MICROSOFT_PROVIDER, microsoftButtonId);
- return this;
- }
-
- public AuthMethodPickerLayout.Builder setAppleButtonId(
- @IdRes int appleButtonId) {
- providersMapping.put(AuthUI.APPLE_PROVIDER, appleButtonId);
- return this;
- }
-
- public AuthMethodPickerLayout.Builder setYahooButtonId(
- @IdRes int yahooButtonId) {
- providersMapping.put(AuthUI.YAHOO_PROVIDER, yahooButtonId);
- return this;
- }
-
- /**
- * Set the ID of a TextView where terms of service and privacy policy should be
- * displayed.
- */
- public AuthMethodPickerLayout.Builder setTosAndPrivacyPolicyId(@IdRes int tosPpView) {
- instance.tosPpView = tosPpView;
- return this;
- }
-
- public AuthMethodPickerLayout build() {
- if (providersMapping.isEmpty()) {
- throw new IllegalArgumentException("Must configure at least one button.");
- }
-
- for (String key : providersMapping.keySet()) {
- if (!AuthUI.SUPPORTED_PROVIDERS.contains(key)
- && !AuthUI.SUPPORTED_OAUTH_PROVIDERS.contains(key)) {
- throw new IllegalArgumentException("Unknown provider: " + key);
- }
- }
-
- instance.providersButton = providersMapping;
- return instance;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/AuthState.kt b/auth/src/main/java/com/firebase/ui/auth/AuthState.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/AuthState.kt
rename to auth/src/main/java/com/firebase/ui/auth/AuthState.kt
index a6e57deed..68bc06737 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/AuthState.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/AuthState.kt
@@ -16,7 +16,6 @@ package com.firebase.ui.auth.compose
import com.firebase.ui.auth.compose.AuthState.Companion.Cancelled
import com.firebase.ui.auth.compose.AuthState.Companion.Idle
-import com.google.firebase.auth.AuthCredential
import com.google.firebase.auth.AuthResult
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.auth.MultiFactorResolver
diff --git a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java b/auth/src/main/java/com/firebase/ui/auth/AuthUI.java
deleted file mode 100644
index b1ac3afbd..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/AuthUI.java
+++ /dev/null
@@ -1,1429 +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;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.CancellationSignal;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
-
-import com.facebook.login.LoginManager;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.GoogleApiUtils;
-import com.firebase.ui.auth.util.Preconditions;
-import com.firebase.ui.auth.util.data.PhoneNumberUtils;
-import com.firebase.ui.auth.util.data.ProviderAvailability;
-import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
-import com.google.android.gms.common.api.CommonStatusCodes;
-import com.google.android.gms.common.api.Scope;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.TaskCompletionSource;
-import com.google.android.gms.tasks.Tasks;
-import com.google.firebase.FirebaseApp;
-import com.google.firebase.auth.ActionCodeSettings;
-import com.google.firebase.auth.EmailAuthProvider;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.FirebaseAuthInvalidUserException;
-import com.google.firebase.auth.FirebaseUser;
-import com.google.firebase.auth.GithubAuthProvider;
-import com.google.firebase.auth.GoogleAuthProvider;
-import com.google.firebase.auth.PhoneAuthProvider;
-import com.google.firebase.auth.TwitterAuthProvider;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Executors;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringDef;
-import androidx.annotation.StyleRes;
-import androidx.credentials.ClearCredentialStateRequest;
-import androidx.credentials.CredentialManagerCallback;
-import androidx.credentials.exceptions.ClearCredentialException;
-
-/**
- * The entry point to the AuthUI authentication flow, and related utility methods. If your
- * application uses the default {@link FirebaseApp} instance, an AuthUI instance can be retrieved
- * simply by calling {@link AuthUI#getInstance()}. If an alternative app instance is in use, call
- * {@link AuthUI#getInstance(FirebaseApp)} instead, passing the appropriate app instance.
- *
- *
- * See the
- * README
- * for examples on how to get started with FirebaseUI Auth.
- */
-public final class AuthUI {
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static final String TAG = "AuthUI";
-
- /**
- * Provider for anonymous users.
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static final String ANONYMOUS_PROVIDER = "anonymous";
- public static final String EMAIL_LINK_PROVIDER = EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD;
-
- public static final String MICROSOFT_PROVIDER = "microsoft.com";
- public static final String YAHOO_PROVIDER = "yahoo.com";
- public static final String APPLE_PROVIDER = "apple.com";
-
- /**
- * Default value for logo resource, omits the logo from the {@link AuthMethodPickerActivity}.
- */
- public static final int NO_LOGO = -1;
-
- /**
- * The set of authentication providers supported in Firebase Auth UI.
- */
- public static final Set SUPPORTED_PROVIDERS =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- GoogleAuthProvider.PROVIDER_ID,
- FacebookAuthProvider.PROVIDER_ID,
- TwitterAuthProvider.PROVIDER_ID,
- GithubAuthProvider.PROVIDER_ID,
- EmailAuthProvider.PROVIDER_ID,
- PhoneAuthProvider.PROVIDER_ID,
- ANONYMOUS_PROVIDER,
- EMAIL_LINK_PROVIDER
- )));
-
- /**
- * The set of OAuth2.0 providers supported in Firebase Auth UI through Generic IDP (web flow).
- */
- public static final Set SUPPORTED_OAUTH_PROVIDERS =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- MICROSOFT_PROVIDER,
- YAHOO_PROVIDER,
- APPLE_PROVIDER,
- TwitterAuthProvider.PROVIDER_ID,
- GithubAuthProvider.PROVIDER_ID
- )));
-
- /**
- * The set of social authentication providers supported in Firebase Auth UI using their SDK.
- */
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static final Set SOCIAL_PROVIDERS =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
- GoogleAuthProvider.PROVIDER_ID,
- FacebookAuthProvider.PROVIDER_ID)));
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static final String UNCONFIGURED_CONFIG_VALUE = "CHANGE-ME";
-
- private static final IdentityHashMap INSTANCES = new IdentityHashMap<>();
-
- private static Context sApplicationContext;
-
- private final FirebaseApp mApp;
- private final FirebaseAuth mAuth;
-
- private String mEmulatorHost = null;
- private int mEmulatorPort = -1;
-
- private AuthUI(FirebaseApp app) {
- mApp = app;
- mAuth = FirebaseAuth.getInstance(mApp);
-
- try {
- mAuth.setFirebaseUIVersion(BuildConfig.VERSION_NAME);
- } catch (Exception e) {
- Log.e(TAG, "Couldn't set the FUI version.", e);
- }
- mAuth.useAppLanguage();
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @NonNull
- public static Context getApplicationContext() {
- return sApplicationContext;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static void setApplicationContext(@NonNull Context context) {
- sApplicationContext = Preconditions.checkNotNull(context, "App context cannot be null.")
- .getApplicationContext();
- }
-
- /**
- * Retrieves the {@link AuthUI} instance associated with the default app, as returned by {@code
- * FirebaseApp.getInstance()}.
- *
- * @throws IllegalStateException if the default app is not initialized.
- */
- @NonNull
- public static AuthUI getInstance() {
- return getInstance(FirebaseApp.getInstance());
- }
-
- /**
- * Retrieves the {@link AuthUI} instance associated the the specified app name.
- *
- * @throws IllegalStateException if the app is not initialized.
- */
- @NonNull
- public static AuthUI getInstance(@NonNull String appName) {
- return getInstance(FirebaseApp.getInstance(appName));
- }
-
- /**
- * Retrieves the {@link AuthUI} instance associated the the specified app.
- */
- @NonNull
- public static AuthUI getInstance(@NonNull FirebaseApp app) {
- String releaseUrl = "https://github.com/firebase/FirebaseUI-Android/releases/tag/6.2.0";
- String devWarning = "Beginning with FirebaseUI 6.2.0 you no longer need to include %s to " +
- "sign in with %s. Go to %s for more information";
- if (ProviderAvailability.IS_TWITTER_AVAILABLE) {
- Log.w(TAG, String.format(devWarning, "the TwitterKit SDK", "Twitter", releaseUrl));
- }
- if (ProviderAvailability.IS_GITHUB_AVAILABLE) {
- Log.w(TAG, String.format(devWarning, "com.firebaseui:firebase-ui-auth-github",
- "GitHub", releaseUrl));
- }
-
- AuthUI authUi;
- synchronized (INSTANCES) {
- authUi = INSTANCES.get(app);
- if (authUi == null) {
- authUi = new AuthUI(app);
- INSTANCES.put(app, authUi);
- }
- }
- return authUi;
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseApp getApp() {
- return mApp;
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseAuth getAuth() {
- return mAuth;
- }
-
- /**
- * Returns true if AuthUI can handle the intent.
- *
- * AuthUI handle the intent when the embedded data is an email link. If it is, you can then
- * specify the link in {@link SignInIntentBuilder#setEmailLink(String)} before starting AuthUI
- * and it will be handled immediately.
- */
- public static boolean canHandleIntent(@NonNull Intent intent) {
- if (intent == null || intent.getData() == null) {
- return false;
- }
- String link = intent.getData().toString();
- return FirebaseAuth.getInstance().isSignInWithEmailLink(link);
- }
-
- /**
- * Default theme used by {@link SignInIntentBuilder#setTheme(int)} if no theme customization is
- * required.
- */
- @StyleRes
- public static int getDefaultTheme() {
- return R.style.FirebaseUI_DefaultMaterialTheme;
- }
-
- /**
- * Signs the current user out, if one is signed in.
- *
- * @param context the context requesting the user be signed out
- * @return A task which, upon completion, signals that the user has been signed out ({@link
- * Task#isSuccessful()}, or that the sign-out attempt failed unexpectedly !{@link
- * Task#isSuccessful()}).
- */
- @NonNull
- public Task signOut(@NonNull Context context) {
- boolean playServicesAvailable = GoogleApiUtils.isPlayServicesAvailable(context);
- if (!playServicesAvailable) {
- Log.w(TAG, "Google Play services not available during signOut");
- }
- signOutIdps(context);
- Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
- return clearCredentialState(context, singleThreadExecutor).continueWith(task -> {
- task.getResult(); // Propagate exceptions if any.
- mAuth.signOut();
- return null;
- });
- }
-
- /**
- * Delete the user from FirebaseAuth.
- *
- *
Any associated saved credentials are not explicitly deleted with the new APIs.
- *
- * @param context the calling {@link Context}.
- */
- @NonNull
- public Task delete(@NonNull final Context context) {
- final FirebaseUser currentUser = mAuth.getCurrentUser();
- if (currentUser == null) {
- return Tasks.forException(new FirebaseAuthInvalidUserException(
- String.valueOf(CommonStatusCodes.SIGN_IN_REQUIRED),
- "No currently signed in user."));
- }
- signOutIdps(context);
- Executor singleThreadExecutor = Executors.newSingleThreadExecutor();
- return clearCredentialState(context, singleThreadExecutor).continueWithTask(task -> {
- task.getResult(); // Propagate exceptions if any.
- return currentUser.delete();
- });
- }
-
- /**
- * Connect to the Firebase Authentication emulator.
- * @see FirebaseAuth#useEmulator(String, int)
- */
- public void useEmulator(@NonNull String host, int port) {
- Preconditions.checkArgument(port >= 0, "Port must be >= 0");
- Preconditions.checkArgument(port <= 65535, "Port must be <= 65535");
- mEmulatorHost = host;
- mEmulatorPort = port;
-
- mAuth.useEmulator(host, port);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public boolean isUseEmulator() {
- return mEmulatorHost != null && mEmulatorPort >= 0;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public String getEmulatorHost() {
- return mEmulatorHost;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public int getEmulatorPort() {
- return mEmulatorPort;
- }
-
- private void signOutIdps(@NonNull Context context) {
- if (ProviderAvailability.IS_FACEBOOK_AVAILABLE) {
- LoginManager.getInstance().logOut();
- }
- }
-
- /**
- * A Task to clear the credential state in Credential Manager.
- * @param context
- * @param executor
- * @return
- */
- private Task clearCredentialState(
- @NonNull Context context,
- @NonNull Executor executor
- ) {
- TaskCompletionSource completionSource = new TaskCompletionSource<>();
-
- ClearCredentialStateRequest clearRequest = new ClearCredentialStateRequest();
- GoogleApiUtils.getCredentialManager(context)
- .clearCredentialStateAsync(
- clearRequest,
- new CancellationSignal(),
- executor,
- new CredentialManagerCallback<>() {
- @Override
- public void onResult(Void unused) {
- completionSource.setResult(unused);
- }
-
- @Override
- public void onError(@NonNull ClearCredentialException e) {
- completionSource.setException(e);
- }
- }
- );
- return completionSource.getTask();
- }
-
- /**
- * Starts the process of creating a sign in intent, with the mandatory application context
- * parameter.
- */
- @NonNull
- public SignInIntentBuilder createSignInIntentBuilder() {
- return new SignInIntentBuilder();
- }
-
- @StringDef({
- GoogleAuthProvider.PROVIDER_ID,
- FacebookAuthProvider.PROVIDER_ID,
- TwitterAuthProvider.PROVIDER_ID,
- GithubAuthProvider.PROVIDER_ID,
- EmailAuthProvider.PROVIDER_ID,
- PhoneAuthProvider.PROVIDER_ID,
- ANONYMOUS_PROVIDER,
- EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SupportedProvider {
- }
-
- /**
- * Configuration for an identity provider.
- */
- public static final class IdpConfig implements Parcelable {
- public static final Creator CREATOR = new Creator() {
- @Override
- public IdpConfig createFromParcel(Parcel in) {
- return new IdpConfig(in);
- }
-
- @Override
- public IdpConfig[] newArray(int size) {
- return new IdpConfig[size];
- }
- };
-
- private final String mProviderId;
- private final Bundle mParams;
-
- private IdpConfig(
- @SupportedProvider @NonNull String providerId,
- @NonNull Bundle params) {
- mProviderId = providerId;
- mParams = new Bundle(params);
- }
-
- private IdpConfig(Parcel in) {
- mProviderId = in.readString();
- mParams = in.readBundle(getClass().getClassLoader());
- }
-
- @NonNull
- @SupportedProvider
- public String getProviderId() {
- return mProviderId;
- }
-
- /**
- * @return provider-specific options
- */
- @NonNull
- public Bundle getParams() {
- return new Bundle(mParams);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel parcel, int i) {
- parcel.writeString(mProviderId);
- parcel.writeBundle(mParams);
- }
-
- @Override
- public final boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- IdpConfig config = (IdpConfig) o;
-
- return mProviderId.equals(config.mProviderId);
- }
-
- @Override
- public final int hashCode() {
- return mProviderId.hashCode();
- }
-
- @Override
- public String toString() {
- return "IdpConfig{" +
- "mProviderId='" + mProviderId + '\'' +
- ", mParams=" + mParams +
- '}';
- }
-
- /**
- * Base builder for all authentication providers.
- *
- * @see SignInIntentBuilder#setAvailableProviders(List)
- */
- public static class Builder {
- private final Bundle mParams = new Bundle();
- @SupportedProvider
- private String mProviderId;
-
- protected Builder(@SupportedProvider @NonNull String providerId) {
- if (!SUPPORTED_PROVIDERS.contains(providerId)
- && !SUPPORTED_OAUTH_PROVIDERS.contains(providerId)) {
- throw new IllegalArgumentException("Unknown provider: " + providerId);
- }
- mProviderId = providerId;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- @NonNull
- protected final Bundle getParams() {
- return mParams;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- protected void setProviderId(@NonNull String providerId) {
- mProviderId = providerId;
- }
-
- @CallSuper
- @NonNull
- public IdpConfig build() {
- return new IdpConfig(mProviderId, mParams);
- }
- }
-
- /**
- * {@link IdpConfig} builder for the email provider.
- */
- public static final class EmailBuilder extends Builder {
- public EmailBuilder() {
- super(EmailAuthProvider.PROVIDER_ID);
- }
-
- /**
- * Enables or disables creating new accounts in the email sign in flows.
- *
- * Account creation is enabled by default.
- */
- @NonNull
- public EmailBuilder setAllowNewAccounts(boolean allow) {
- getParams().putBoolean(ExtraConstants.ALLOW_NEW_EMAILS, allow);
- return this;
- }
-
- /**
- * Configures the requirement for the user to enter first and last name in the email
- * sign up flow.
- *
- * Name is required by default.
- */
- @NonNull
- public EmailBuilder setRequireName(boolean requireName) {
- getParams().putBoolean(ExtraConstants.REQUIRE_NAME, requireName);
- return this;
- }
-
- /**
- * Enables email link sign in instead of password based sign in. Once enabled, you must
- * pass a valid {@link ActionCodeSettings} object using
- * {@link #setActionCodeSettings(ActionCodeSettings)}
- *
- * You must enable Firebase Dynamic Links in the Firebase Console to use email link
- * sign in.
- *
- * @throws IllegalStateException if {@link ActionCodeSettings} is null or not
- * provided with email link enabled.
- */
- @NonNull
- public EmailBuilder enableEmailLinkSignIn() {
- setProviderId(EMAIL_LINK_PROVIDER);
- return this;
- }
-
- /**
- * Sets the {@link ActionCodeSettings} object to be used for email link sign in.
- *
- * {@link ActionCodeSettings#canHandleCodeInApp()} must be set to true, and a valid
- * continueUrl must be passed via {@link ActionCodeSettings.Builder#setUrl(String)}.
- * This URL must be allowlisted in the Firebase Console.
- *
- * @throws IllegalStateException if canHandleCodeInApp is set to false
- * @throws NullPointerException if ActionCodeSettings is null
- */
- @NonNull
- public EmailBuilder setActionCodeSettings(ActionCodeSettings actionCodeSettings) {
- getParams().putParcelable(ExtraConstants.ACTION_CODE_SETTINGS, actionCodeSettings);
- return this;
- }
-
- /**
- * Disables allowing email link sign in to occur across different devices.
- *
- * This cannot be disabled with anonymous upgrade.
- */
- @NonNull
- public EmailBuilder setForceSameDevice() {
- getParams().putBoolean(ExtraConstants.FORCE_SAME_DEVICE, true);
- return this;
- }
-
- /**
- * Sets a default sign in email, if the given email has been registered before, then
- * it will ask the user for password, if the given email it's not registered, then
- * it starts signing up the default email.
- */
- @NonNull
- public EmailBuilder setDefaultEmail(String email) {
- getParams().putString(ExtraConstants.DEFAULT_EMAIL, email);
- return this;
- }
-
- @Override
- public IdpConfig build() {
- if (super.mProviderId.equals(EMAIL_LINK_PROVIDER)) {
- ActionCodeSettings actionCodeSettings =
- getParams().getParcelable(ExtraConstants.ACTION_CODE_SETTINGS);
- Preconditions.checkNotNull(actionCodeSettings, "ActionCodeSettings cannot be " +
- "null when using email link sign in.");
- if (!actionCodeSettings.canHandleCodeInApp()) {
- // Pre-emptively fail if actionCodeSettings are misconfigured. This would
- // have happened when calling sendSignInLinkToEmail
- throw new IllegalStateException(
- "You must set canHandleCodeInApp in your ActionCodeSettings to " +
- "true for Email-Link Sign-in.");
- }
- }
- return super.build();
- }
- }
-
- /**
- * {@link IdpConfig} builder for the phone provider.
- */
- public static final class PhoneBuilder extends Builder {
- public PhoneBuilder() {
- super(PhoneAuthProvider.PROVIDER_ID);
- }
-
- /**
- * @param number the phone number in international format
- * @see #setDefaultNumber(String, String)
- */
- @NonNull
- public PhoneBuilder setDefaultNumber(@NonNull String number) {
- Preconditions.checkUnset(getParams(),
- "Cannot overwrite previously set phone number",
- ExtraConstants.PHONE,
- ExtraConstants.COUNTRY_ISO,
- ExtraConstants.NATIONAL_NUMBER);
- if (!PhoneNumberUtils.isValid(number)) {
- throw new IllegalStateException("Invalid phone number: " + number);
- }
-
- getParams().putString(ExtraConstants.PHONE, number);
-
- return this;
- }
-
- /**
- * Set the default phone number that will be used to populate the phone verification
- * sign-in flow.
- *
- * @param iso the phone number's country code
- * @param number the phone number in local format
- */
- @NonNull
- public PhoneBuilder setDefaultNumber(@NonNull String iso, @NonNull String number) {
- Preconditions.checkUnset(getParams(),
- "Cannot overwrite previously set phone number",
- ExtraConstants.PHONE,
- ExtraConstants.COUNTRY_ISO,
- ExtraConstants.NATIONAL_NUMBER);
- if (!PhoneNumberUtils.isValidIso(iso)) {
- throw new IllegalStateException("Invalid country iso: " + iso);
- }
-
- getParams().putString(ExtraConstants.COUNTRY_ISO, iso);
- getParams().putString(ExtraConstants.NATIONAL_NUMBER, number);
-
- return this;
- }
-
- /**
- * Set the default country code that will be used in the phone verification sign-in
- * flow.
- *
- * @param iso country iso
- */
- @NonNull
- public PhoneBuilder setDefaultCountryIso(@NonNull String iso) {
- Preconditions.checkUnset(getParams(),
- "Cannot overwrite previously set phone number",
- ExtraConstants.PHONE,
- ExtraConstants.COUNTRY_ISO,
- ExtraConstants.NATIONAL_NUMBER);
- if (!PhoneNumberUtils.isValidIso(iso)) {
- throw new IllegalStateException("Invalid country iso: " + iso);
- }
-
- getParams().putString(ExtraConstants.COUNTRY_ISO,
- iso.toUpperCase(Locale.getDefault()));
-
- return this;
- }
-
-
- /**
- * Sets the country codes available in the country code selector for phone
- * authentication. Takes as input a List of both country isos and codes.
- * This is not to be called with
- * {@link #setBlockedCountries(List)}.
- * If both are called, an exception will be thrown.
- *
- * Inputting an e-164 country code (e.g. '+1') will include all countries with
- * +1 as its code.
- * Example input: {'+52', 'us'}
- * For a list of country iso or codes, see Alpha-2 isos here:
- * https://en.wikipedia.org/wiki/ISO_3166-1
- * and e-164 codes here: https://en.wikipedia.org/wiki/List_of_country_calling_codes
- *
- * @param countries a non empty case insensitive list of country codes
- * and/or isos to be allowlisted
- * @throws IllegalArgumentException if an empty allowlist is provided.
- * @throws NullPointerException if a null allowlist is provided.
- */
- public PhoneBuilder setAllowedCountries(
- @NonNull List countries) {
- if (getParams().containsKey(ExtraConstants.BLOCKLISTED_COUNTRIES)) {
- throw new IllegalStateException(
- "You can either allowlist or blocklist country codes for phone " +
- "authentication.");
- }
-
- String message = "Invalid argument: Only non-%s allowlists are valid. " +
- "To specify no allowlist, do not call this method.";
- Preconditions.checkNotNull(countries, String.format(message, "null"));
- Preconditions.checkArgument(!countries.isEmpty(), String.format
- (message, "empty"));
-
- addCountriesToBundle(countries, ExtraConstants.ALLOWLISTED_COUNTRIES);
- return this;
- }
-
- /**
- * Sets the countries to be removed from the country code selector for phone
- * authentication. Takes as input a List of both country isos and codes.
- * This is not to be called with
- * {@link #setAllowedCountries(List)}.
- * If both are called, an exception will be thrown.
- *
- * Inputting an e-164 country code (e.g. '+1') will include all countries with
- * +1 as its code.
- * Example input: {'+52', 'us'}
- * For a list of country iso or codes, see Alpha-2 codes here:
- * https://en.wikipedia.org/wiki/ISO_3166-1
- * and e-164 codes here: https://en.wikipedia.org/wiki/List_of_country_calling_codes
- *
- * @param countries a non empty case insensitive list of country codes
- * and/or isos to be blocklisted
- * @throws IllegalArgumentException if an empty blocklist is provided.
- * @throws NullPointerException if a null blocklist is provided.
- */
- public PhoneBuilder setBlockedCountries(
- @NonNull List countries) {
- if (getParams().containsKey(ExtraConstants.ALLOWLISTED_COUNTRIES)) {
- throw new IllegalStateException(
- "You can either allowlist or blocklist country codes for phone " +
- "authentication.");
- }
-
- String message = "Invalid argument: Only non-%s blocklists are valid. " +
- "To specify no blocklist, do not call this method.";
- Preconditions.checkNotNull(countries, String.format(message, "null"));
- Preconditions.checkArgument(!countries.isEmpty(), String.format
- (message, "empty"));
-
- addCountriesToBundle(countries, ExtraConstants.BLOCKLISTED_COUNTRIES);
- return this;
- }
-
- @Override
- public IdpConfig build() {
- validateInputs();
- return super.build();
- }
-
- private void addCountriesToBundle(List CountryIsos, String CountryIsoType) {
- ArrayList uppercaseCodes = new ArrayList<>();
- for (String code : CountryIsos) {
- uppercaseCodes.add(code.toUpperCase(Locale.getDefault()));
- }
-
- getParams().putStringArrayList(CountryIsoType, uppercaseCodes);
- }
-
- private void validateInputs() {
- List allowedCountries = getParams().getStringArrayList(
- ExtraConstants.ALLOWLISTED_COUNTRIES);
- List blockedCountries = getParams().getStringArrayList(
- ExtraConstants.BLOCKLISTED_COUNTRIES);
-
- if (allowedCountries != null && blockedCountries != null) {
- throw new IllegalStateException(
- "You can either allowlist or blocked country codes for phone " +
- "authentication.");
- } else if (allowedCountries != null) {
- validateInputs(allowedCountries, true);
-
- } else if (blockedCountries != null) {
- validateInputs(blockedCountries, false);
- }
- }
-
- private void validateInputs(List countries, boolean allowed) {
- validateCountryInput(countries);
- validateDefaultCountryInput(countries, allowed);
- }
-
- private void validateCountryInput(List codes) {
- for (String code : codes) {
- if (!PhoneNumberUtils.isValidIso(code) && !PhoneNumberUtils.isValid(code)) {
- throw new IllegalArgumentException("Invalid input: You must provide a " +
- "valid country iso (alpha-2) or code (e-164). e.g. 'us' or '+1'.");
- }
- }
- }
-
- private void validateDefaultCountryInput(List codes, boolean allowed) {
- // A default iso/code can be set via #setDefaultCountryIso() or #setDefaultNumber()
- if (getParams().containsKey(ExtraConstants.COUNTRY_ISO) ||
- getParams().containsKey(ExtraConstants.PHONE)) {
-
- if (!validateDefaultCountryIso(codes, allowed)
- || !validateDefaultPhoneIsos(codes, allowed)) {
- throw new IllegalArgumentException("Invalid default country iso. Make " +
- "sure it is either part of the allowed list or that you "
- + "haven't blocked it.");
- }
- }
-
- }
-
- private boolean validateDefaultCountryIso(List codes, boolean allowed) {
- String defaultIso = getDefaultIso();
- return isValidDefaultIso(codes, defaultIso, allowed);
- }
-
- private boolean validateDefaultPhoneIsos(List codes, boolean allowed) {
- List phoneIsos = getPhoneIsosFromCode();
- for (String iso : phoneIsos) {
- if (isValidDefaultIso(codes, iso, allowed)) {
- return true;
- }
- }
- return phoneIsos.isEmpty();
- }
-
- private boolean isValidDefaultIso(List codes, String iso, boolean allowed) {
- if (iso == null) return true;
- boolean containsIso = containsCountryIso(codes, iso);
- return containsIso && allowed || !containsIso && !allowed;
-
- }
-
- private boolean containsCountryIso(List codes, String iso) {
- iso = iso.toUpperCase(Locale.getDefault());
- for (String code : codes) {
- if (PhoneNumberUtils.isValidIso(code)) {
- if (code.equals(iso)) {
- return true;
- }
- } else {
- List isos = PhoneNumberUtils.getCountryIsosFromCountryCode(code);
- if (isos.contains(iso)) {
- return true;
- }
- }
- }
- return false;
- }
-
- private List getPhoneIsosFromCode() {
- List isos = new ArrayList<>();
- String phone = getParams().getString(ExtraConstants.PHONE);
- if (phone != null && phone.startsWith("+")) {
- String countryCode = "+" + PhoneNumberUtils.getPhoneNumber(phone)
- .getCountryCode();
- List isosToAdd = PhoneNumberUtils.
- getCountryIsosFromCountryCode(countryCode);
- if (isosToAdd != null) {
- isos.addAll(isosToAdd);
- }
- }
- return isos;
- }
-
- private String getDefaultIso() {
- return getParams().containsKey(ExtraConstants.COUNTRY_ISO) ?
- getParams().getString(ExtraConstants.COUNTRY_ISO) : null;
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Google provider.
- */
- public static final class GoogleBuilder extends Builder {
- public GoogleBuilder() {
- super(GoogleAuthProvider.PROVIDER_ID);
- }
-
- private void validateWebClientId() {
- Preconditions.checkConfigured(getApplicationContext(),
- "Check your google-services plugin configuration, the" +
- " default_web_client_id string wasn't populated.",
- R.string.default_web_client_id);
- }
-
- /**
- * Set the scopes that your app will request when using Google sign-in. See all available
- * scopes.
- *
- * @param scopes additional scopes to be requested
- */
- @NonNull
- public GoogleBuilder setScopes(@NonNull List scopes) {
- GoogleSignInOptions.Builder builder =
- new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
- .requestEmail();
- for (String scope : scopes) {
- builder.requestScopes(new Scope(scope));
- }
- return setSignInOptions(builder.build());
- }
-
- /**
- * Set the {@link GoogleSignInOptions} to be used for Google sign-in. Standard
- * options like requesting the user's email will automatically be added.
- *
- * @param options sign-in options
- */
- @NonNull
- public GoogleBuilder setSignInOptions(@NonNull GoogleSignInOptions options) {
- Preconditions.checkUnset(getParams(),
- "Cannot overwrite previously set sign-in options.",
- ExtraConstants.GOOGLE_SIGN_IN_OPTIONS);
-
- GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(options);
-
- String clientId = options.getServerClientId();
- if (clientId == null) {
- validateWebClientId();
- clientId = getApplicationContext().getString(R.string.default_web_client_id);
- }
-
- // Warn the user that they are _probably_ doing the wrong thing if they
- // have not called requestEmail (see issue #1899 and #1621)
- boolean hasEmailScope = false;
- for (Scope s : options.getScopes()) {
- if ("email".equals(s.getScopeUri())) {
- hasEmailScope = true;
- break;
- }
- }
- if (!hasEmailScope) {
- Log.w(TAG, "The GoogleSignInOptions passed to setSignInOptions does not " +
- "request the 'email' scope. In most cases this is a mistake! " +
- "Call requestEmail() on the GoogleSignInOptions object.");
- }
-
- builder.requestIdToken(clientId);
- getParams().putParcelable(
- ExtraConstants.GOOGLE_SIGN_IN_OPTIONS, builder.build());
-
- return this;
- }
-
- @NonNull
- @Override
- public IdpConfig build() {
- if (!getParams().containsKey(ExtraConstants.GOOGLE_SIGN_IN_OPTIONS)) {
- validateWebClientId();
- setScopes(Collections.emptyList());
- }
-
- return super.build();
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Facebook provider.
- */
- public static final class FacebookBuilder extends Builder {
- private static final String TAG = "FacebookBuilder";
-
- public FacebookBuilder() {
- super(FacebookAuthProvider.PROVIDER_ID);
- if (!ProviderAvailability.IS_FACEBOOK_AVAILABLE) {
- throw new RuntimeException(
- "Facebook provider cannot be configured " +
- "without dependency. Did you forget to add " +
- "'com.facebook.android:facebook-login:VERSION' dependency?");
- }
- Preconditions.checkConfigured(getApplicationContext(),
- "Facebook provider unconfigured. Make sure to add a" +
- " `facebook_application_id` string. See the docs for more info:" +
- " https://github" +
- ".com/firebase/FirebaseUI-Android/blob/master/auth/README" +
- ".md#facebook",
- R.string.facebook_application_id);
- if (getApplicationContext().getString(R.string.facebook_login_protocol_scheme)
- .equals("fbYOUR_APP_ID")) {
- Log.w(TAG, "Facebook provider unconfigured for Chrome Custom Tabs.");
- }
- }
-
- /**
- * Specifies the additional permissions that the application will request in the
- * Facebook Login SDK. Available permissions can be found here.
- */
- @NonNull
- public FacebookBuilder setPermissions(@NonNull List permissions) {
- getParams().putStringArrayList(
- ExtraConstants.FACEBOOK_PERMISSIONS, new ArrayList<>(permissions));
- return this;
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Anonymous provider.
- */
- public static final class AnonymousBuilder extends Builder {
- public AnonymousBuilder() {
- super(ANONYMOUS_PROVIDER);
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Twitter provider.
- */
- public static final class TwitterBuilder extends GenericOAuthProviderBuilder {
- private static final String PROVIDER_NAME = "Twitter";
-
- public TwitterBuilder() {
- super(TwitterAuthProvider.PROVIDER_ID, PROVIDER_NAME,
- R.layout.fui_idp_button_twitter);
- }
- }
-
- /**
- * {@link IdpConfig} builder for the GitHub provider.
- */
- public static final class GitHubBuilder extends GenericOAuthProviderBuilder {
- private static final String PROVIDER_NAME = "Github";
-
- public GitHubBuilder() {
- super(GithubAuthProvider.PROVIDER_ID, PROVIDER_NAME,
- R.layout.fui_idp_button_github);
- }
-
- /**
- * Specifies the additional permissions to be requested.
- *
- *
Available permissions can be found
- * here.
- *
- * @deprecated Please use {@link #setScopes(List)} instead.
- */
- @Deprecated
- @NonNull
- public GitHubBuilder setPermissions(@NonNull List permissions) {
- setScopes(permissions);
- return this;
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Apple provider.
- */
- public static final class AppleBuilder extends GenericOAuthProviderBuilder {
- private static final String PROVIDER_NAME = "Apple";
-
- public AppleBuilder() {
- super(APPLE_PROVIDER, PROVIDER_NAME, R.layout.fui_idp_button_apple);
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Microsoft provider.
- */
- public static final class MicrosoftBuilder extends GenericOAuthProviderBuilder {
- private static final String PROVIDER_NAME = "Microsoft";
-
- public MicrosoftBuilder() {
- super(MICROSOFT_PROVIDER, PROVIDER_NAME, R.layout.fui_idp_button_microsoft);
- }
- }
-
- /**
- * {@link IdpConfig} builder for the Yahoo provider.
- */
- public static final class YahooBuilder extends GenericOAuthProviderBuilder {
- private static final String PROVIDER_NAME = "Yahoo";
-
- public YahooBuilder() {
- super(YAHOO_PROVIDER, PROVIDER_NAME, R.layout.fui_idp_button_yahoo);
- }
- }
-
- /**
- * {@link IdpConfig} builder for a Generic OAuth provider.
- */
- public static class GenericOAuthProviderBuilder extends Builder {
-
- public GenericOAuthProviderBuilder(@NonNull String providerId,
- @NonNull String providerName,
- int buttonId) {
- super(providerId);
-
- Preconditions.checkNotNull(providerId, "The provider ID cannot be null.");
- Preconditions.checkNotNull(providerName, "The provider name cannot be null.");
-
- getParams().putString(
- ExtraConstants.GENERIC_OAUTH_PROVIDER_ID, providerId);
- getParams().putString(
- ExtraConstants.GENERIC_OAUTH_PROVIDER_NAME, providerName);
- getParams().putInt(
- ExtraConstants.GENERIC_OAUTH_BUTTON_ID, buttonId);
-
- }
-
- @NonNull
- public GenericOAuthProviderBuilder setScopes(@NonNull List scopes) {
- getParams().putStringArrayList(
- ExtraConstants.GENERIC_OAUTH_SCOPES, new ArrayList<>(scopes));
- return this;
- }
-
- @NonNull
- public GenericOAuthProviderBuilder setCustomParameters(
- @NonNull Map customParameters) {
- getParams().putSerializable(
- ExtraConstants.GENERIC_OAUTH_CUSTOM_PARAMETERS,
- new HashMap<>(customParameters));
- return this;
- }
- }
- }
-
- /**
- * Base builder for both {@link SignInIntentBuilder}.
- */
- @SuppressWarnings(value = "unchecked")
- private abstract class AuthIntentBuilder {
- final List mProviders = new ArrayList<>();
- IdpConfig mDefaultProvider = null;
- int mLogo = NO_LOGO;
- int mTheme = getDefaultTheme();
- String mTosUrl;
- String mPrivacyPolicyUrl;
- boolean mAlwaysShowProviderChoice = false;
- boolean mLockOrientation = false;
- boolean mEnableCredentials = true;
- AuthMethodPickerLayout mAuthMethodPickerLayout = null;
- ActionCodeSettings mPasswordSettings = null;
-
- /**
- * Specifies the theme to use for the application flow. If no theme is specified, a
- * default theme will be used.
- */
- @NonNull
- public T setTheme(@StyleRes int theme) {
- mTheme = Preconditions.checkValidStyle(
- mApp.getApplicationContext(),
- theme,
- "theme identifier is unknown or not a style definition");
- return (T) this;
- }
-
- /**
- * Specifies the logo to use for the {@link AuthMethodPickerActivity}. If no logo is
- * specified, none will be used.
- */
- @NonNull
- public T setLogo(@DrawableRes int logo) {
- mLogo = logo;
- return (T) this;
- }
-
- /**
- * Specifies the terms-of-service URL for the application.
- *
- * @deprecated Please use {@link #setTosAndPrivacyPolicyUrls(String, String)} For the Tos
- * link to be displayed a Privacy Policy url must also be provided.
- */
- @NonNull
- @Deprecated
- public T setTosUrl(@Nullable String tosUrl) {
- mTosUrl = tosUrl;
- return (T) this;
- }
-
- /**
- * Specifies the privacy policy URL for the application.
- *
- * @deprecated Please use {@link #setTosAndPrivacyPolicyUrls(String, String)} For the
- * Privacy Policy link to be displayed a Tos url must also be provided.
- */
- @NonNull
- @Deprecated
- public T setPrivacyPolicyUrl(@Nullable String privacyPolicyUrl) {
- mPrivacyPolicyUrl = privacyPolicyUrl;
- return (T) this;
- }
-
- @NonNull
- public T setTosAndPrivacyPolicyUrls(@NonNull String tosUrl,
- @NonNull String privacyPolicyUrl) {
- Preconditions.checkNotNull(tosUrl, "tosUrl cannot be null");
- Preconditions.checkNotNull(privacyPolicyUrl, "privacyPolicyUrl cannot be null");
- mTosUrl = tosUrl;
- mPrivacyPolicyUrl = privacyPolicyUrl;
- return (T) this;
- }
-
- /**
- * Specifies the set of supported authentication providers. At least one provider must
- * be specified. There may only be one instance of each provider. Anonymous provider cannot
- * be the only provider specified.
- *
- *
If no providers are explicitly specified by calling this method, then the email
- * provider is the default supported provider.
- *
- * @param idpConfigs a list of {@link IdpConfig}s, where each {@link IdpConfig} contains the
- * configuration parameters for the IDP.
- * @throws IllegalStateException if anonymous provider is the only specified provider.
- * @see IdpConfig
- */
- @NonNull
- public T setAvailableProviders(@NonNull List idpConfigs) {
- Preconditions.checkNotNull(idpConfigs, "idpConfigs cannot be null");
- if (idpConfigs.size() == 1 &&
- idpConfigs.get(0).getProviderId().equals(ANONYMOUS_PROVIDER)) {
- throw new IllegalStateException("Sign in as guest cannot be the only sign in " +
- "method. In this case, sign the user in anonymously your self; " +
- "no UI is needed.");
- }
-
- mProviders.clear();
-
- for (IdpConfig config : idpConfigs) {
- if (mProviders.contains(config)) {
- throw new IllegalArgumentException("Each provider can only be set once. "
- + config.getProviderId()
- + " was set twice.");
- } else {
- mProviders.add(config);
- }
- }
-
- return (T) this;
- }
-
- /**
- * Specifies the default authentication provider, bypassing the provider selection screen.
- * The provider here must already be included via {@link #setAvailableProviders(List)}, and
- * this method is incompatible with {@link #setAlwaysShowSignInMethodScreen(boolean)}.
- *
- * @param config the default {@link IdpConfig} to use.
- */
- @NonNull
- public T setDefaultProvider(@Nullable IdpConfig config) {
- if (config != null) {
- if (!mProviders.contains(config)) {
- throw new IllegalStateException(
- "Default provider not in available providers list.");
- }
- if (mAlwaysShowProviderChoice) {
- throw new IllegalStateException(
- "Can't set default provider and always show provider choice.");
- }
- }
- mDefaultProvider = config;
- return (T) this;
- }
-
- /**
- * Enables or disables the use of Credential Manager for Passwords credential selector
- *
- *
Is enabled by default.
- *
- * @param enableCredentials enables credential selector before signup
- */
- @NonNull
- public T setCredentialManagerEnabled(boolean enableCredentials) {
- mEnableCredentials = enableCredentials;
- return (T) this;
- }
-
- /**
- * Set a custom layout for the AuthMethodPickerActivity screen.
- * See {@link AuthMethodPickerLayout}.
- *
- * @param authMethodPickerLayout custom layout descriptor object.
- */
- @NonNull
- public T setAuthMethodPickerLayout(@NonNull AuthMethodPickerLayout authMethodPickerLayout) {
- mAuthMethodPickerLayout = authMethodPickerLayout;
- 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) {
- if (alwaysShow && mDefaultProvider != null) {
- throw new IllegalStateException(
- "Can't show provider choice with a default provider.");
- }
- mAlwaysShowProviderChoice = alwaysShow;
- return (T) this;
- }
-
- /**
- * Enable or disables the orientation for small devices to be locked in
- * Portrait orientation
- *
- *
This is false by default.
- *
- * @param lockOrientation if true, force the activities to be in Portrait orientation.
- */
- @NonNull
- public T setLockOrientation(boolean lockOrientation) {
- mLockOrientation = lockOrientation;
- return (T) this;
- }
-
- /**
- * Set custom settings for the RecoverPasswordActivity.
- *
- * @param passwordSettings to allow additional state via a continue URL.
- */
- @NonNull
- public T setResetPasswordSettings(ActionCodeSettings passwordSettings) {
- mPasswordSettings = passwordSettings;
- return (T) this;
- }
-
- @CallSuper
- @NonNull
- public Intent build() {
- if (mProviders.isEmpty()) {
- mProviders.add(new IdpConfig.EmailBuilder().build());
- }
-
- return KickoffActivity.createIntent(mApp.getApplicationContext(), getFlowParams());
- }
-
- protected abstract FlowParameters getFlowParams();
- }
-
- /**
- * Builder for the intent to start the user authentication flow.
- */
- public final class SignInIntentBuilder extends AuthIntentBuilder {
-
- private String mEmailLink;
- private boolean mEnableAnonymousUpgrade;
-
- private SignInIntentBuilder() {
- super();
- }
-
- /**
- * Specifies the email link to be used for sign in. When set, a sign in attempt will be
- * made immediately.
- */
- @NonNull
- public SignInIntentBuilder setEmailLink(@NonNull final String emailLink) {
- mEmailLink = emailLink;
- return this;
- }
-
- /**
- * Enables upgrading anonymous accounts to full accounts during the sign-in flow.
- * This is disabled by default.
- *
- * @throws IllegalStateException when you attempt to enable anonymous user upgrade
- * without forcing the same device flow for email link sign in.
- */
- @NonNull
- public SignInIntentBuilder enableAnonymousUsersAutoUpgrade() {
- mEnableAnonymousUpgrade = true;
- validateEmailBuilderConfig();
- return this;
- }
-
- private void validateEmailBuilderConfig() {
- for (int i = 0; i < mProviders.size(); i++) {
- IdpConfig config = mProviders.get(i);
- if (config.getProviderId().equals(EMAIL_LINK_PROVIDER)) {
- boolean emailLinkForceSameDevice =
- config.getParams().getBoolean(ExtraConstants.FORCE_SAME_DEVICE, true);
- if (!emailLinkForceSameDevice) {
- throw new IllegalStateException("You must force the same device flow " +
- "when using email link sign in with anonymous user upgrade");
- }
- }
- }
- }
-
- @Override
- protected FlowParameters getFlowParams() {
- return new FlowParameters(
- mApp.getName(),
- mProviders,
- mDefaultProvider,
- mTheme,
- mLogo,
- mTosUrl,
- mPrivacyPolicyUrl,
- mEnableCredentials,
- mEnableAnonymousUpgrade,
- mAlwaysShowProviderChoice,
- mLockOrientation,
- mEmailLink,
- mPasswordSettings,
- mAuthMethodPickerLayout);
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ErrorCodes.java b/auth/src/main/java/com/firebase/ui/auth/ErrorCodes.java
deleted file mode 100644
index a39869d6e..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ErrorCodes.java
+++ /dev/null
@@ -1,144 +0,0 @@
-package com.firebase.ui.auth;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Error codes for failed sign-in attempts.
- */
-public final class ErrorCodes {
- /**
- * An unknown error has occurred.
- */
- public static final int UNKNOWN_ERROR = 0;
- /**
- * Sign in failed due to lack of network connection.
- */
- public static final int NO_NETWORK = 1;
- /**
- * A required update to Play Services was cancelled by the user.
- */
- public static final int PLAY_SERVICES_UPDATE_CANCELLED = 2;
- /**
- * A sign-in operation couldn't be completed due to a developer error.
- */
- public static final int DEVELOPER_ERROR = 3;
- /**
- * An external sign-in provider error occurred.
- */
- public static final int PROVIDER_ERROR = 4;
- /**
- * Anonymous account linking failed.
- */
- public static final int ANONYMOUS_UPGRADE_MERGE_CONFLICT = 5;
- /**
- * Signing in with a different email in the WelcomeBackIdp flow or email link flow.
- */
- public static final int EMAIL_MISMATCH_ERROR = 6;
- /**
- * Attempting to sign in with an invalid email link.
- */
- public static final int INVALID_EMAIL_LINK_ERROR = 7;
-
- /**
- * Attempting to open an email link from a different device.
- */
- public static final int EMAIL_LINK_WRONG_DEVICE_ERROR = 8;
-
- /**
- * We need to prompt the user for their email.
- * */
- public static final int EMAIL_LINK_PROMPT_FOR_EMAIL_ERROR = 9;
-
- /**
- * Cross device linking flow - we need to ask the user if they want to continue linking or
- * just sign in.
- * */
- public static final int EMAIL_LINK_CROSS_DEVICE_LINKING_ERROR = 10;
-
- /**
- * Attempting to open an email link from the same device, with anonymous upgrade enabled,
- * but the underlying anonymous user has been changed.
- */
- public static final int EMAIL_LINK_DIFFERENT_ANONYMOUS_USER_ERROR = 11;
-
- /**
- * Attempting to auth with account that is currently disabled in the Firebase console.
- */
- public static final int ERROR_USER_DISABLED = 12;
-
- /**
- * Recoverable error occurred during the Generic IDP flow.
- */
- public static final int ERROR_GENERIC_IDP_RECOVERABLE_ERROR = 13;
-
- private ErrorCodes() {
- throw new AssertionError("No instance for you!");
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static String toFriendlyMessage(@Code int code) {
- switch (code) {
- case UNKNOWN_ERROR:
- return "Unknown error";
- case NO_NETWORK:
- return "No internet connection";
- case PLAY_SERVICES_UPDATE_CANCELLED:
- return "Play Services update cancelled";
- case DEVELOPER_ERROR:
- return "Developer error";
- case PROVIDER_ERROR:
- return "Provider error";
- case ANONYMOUS_UPGRADE_MERGE_CONFLICT:
- return "User account merge conflict";
- case EMAIL_MISMATCH_ERROR:
- return "You are are attempting to sign in a different email than previously " +
- "provided";
- case INVALID_EMAIL_LINK_ERROR:
- return "You are are attempting to sign in with an invalid email link";
- case EMAIL_LINK_PROMPT_FOR_EMAIL_ERROR:
- return "Please enter your email to continue signing in";
- case EMAIL_LINK_WRONG_DEVICE_ERROR:
- return "You must open the email link on the same device.";
- case EMAIL_LINK_CROSS_DEVICE_LINKING_ERROR:
- return "You must determine if you want to continue linking or complete the sign in";
- case EMAIL_LINK_DIFFERENT_ANONYMOUS_USER_ERROR:
- return "The session associated with this sign-in request has either expired or " +
- "was cleared";
- case ERROR_USER_DISABLED:
- return "The user account has been disabled by an administrator.";
- case ERROR_GENERIC_IDP_RECOVERABLE_ERROR:
- return "Generic IDP recoverable error.";
- default:
- throw new IllegalArgumentException("Unknown code: " + code);
- }
- }
-
- /**
- * Valid codes that can be returned from {@link FirebaseUiException#getErrorCode()}.
- */
- @IntDef({
- UNKNOWN_ERROR,
- NO_NETWORK,
- PLAY_SERVICES_UPDATE_CANCELLED,
- DEVELOPER_ERROR,
- PROVIDER_ERROR,
- ANONYMOUS_UPGRADE_MERGE_CONFLICT,
- EMAIL_MISMATCH_ERROR,
- INVALID_EMAIL_LINK_ERROR,
- EMAIL_LINK_WRONG_DEVICE_ERROR,
- EMAIL_LINK_PROMPT_FOR_EMAIL_ERROR,
- EMAIL_LINK_CROSS_DEVICE_LINKING_ERROR,
- EMAIL_LINK_DIFFERENT_ANONYMOUS_USER_ERROR,
- ERROR_USER_DISABLED,
- ERROR_GENERIC_IDP_RECOVERABLE_ERROR
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface Code {
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthActivity.kt b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthActivity.kt
similarity index 96%
rename from auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthActivity.kt
rename to auth/src/main/java/com/firebase/ui/auth/FirebaseAuthActivity.kt
index d98779777..72b6cc807 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthActivity.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthActivity.kt
@@ -21,13 +21,11 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
-import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.getValue
import androidx.lifecycle.lifecycleScope
import com.firebase.ui.auth.compose.configuration.AuthUIConfiguration
import com.firebase.ui.auth.compose.configuration.theme.AuthUITheme
import com.firebase.ui.auth.compose.ui.screens.FirebaseAuthScreen
-import com.google.firebase.auth.FirebaseUser
+import com.firebase.ui.auth.compose.util.EmailLinkConstants
import kotlinx.coroutines.launch
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
@@ -94,6 +92,9 @@ class FirebaseAuthActivity : ComponentActivity() {
authUI = FirebaseAuthUI.getInstance()
+ // Extract email link if present
+ val emailLink = intent.getStringExtra(EmailLinkConstants.EXTRA_EMAIL_LINK)
+
// Observe auth state to automatically finish when done
lifecycleScope.launch {
authUI.authStateFlow().collect { state ->
@@ -133,6 +134,7 @@ class FirebaseAuthActivity : ComponentActivity() {
FirebaseAuthScreen(
authUI = authUI,
configuration = configuration,
+ emailLink = emailLink,
onSignInSuccess = { authResult ->
// State flow will handle finishing
},
diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthAnonymousUpgradeException.java b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthAnonymousUpgradeException.java
deleted file mode 100644
index a5139261b..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthAnonymousUpgradeException.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.firebase.ui.auth;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class FirebaseAuthAnonymousUpgradeException extends Exception {
-
- private IdpResponse mResponse;
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseAuthAnonymousUpgradeException(@ErrorCodes.Code int code,
- @NonNull IdpResponse response) {
- super(ErrorCodes.toFriendlyMessage(code));
- mResponse = response;
- }
-
- public IdpResponse getResponse() {
- return mResponse;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
index 7f952212d..07c0a9713 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
@@ -69,7 +69,7 @@ import java.util.concurrent.ConcurrentHashMap
*/
class FirebaseAuthUI private constructor(
val app: FirebaseApp,
- val auth: FirebaseAuth
+ val auth: FirebaseAuth,
) {
private val _authStateFlow = MutableStateFlow(AuthState.Idle)
@@ -573,5 +573,7 @@ class FirebaseAuthUI private constructor(
internal fun getCacheSize(): Int {
return instanceCache.size
}
+
+ const val UNCONFIGURED_CONFIG_VALUE: String = "CHANGE-ME"
}
}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java
deleted file mode 100644
index 1edbd3a8e..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUIActivityResultContract.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.firebase.ui.auth;
-
-import android.content.Context;
-import android.content.Intent;
-
-import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult;
-
-import androidx.activity.result.contract.ActivityResultContract;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * A {@link ActivityResultContract} describing that the caller can launch authentication flow with a
- * {@link Intent} and is guaranteed to receive a {@link FirebaseAuthUIAuthenticationResult} as
- * result. The given input intent must be created using a
- * {@link com.firebase.ui.auth.AuthUI.SignInIntentBuilder} in order to guarantee a successful
- * launch of the authentication flow.
- */
-public class FirebaseAuthUIActivityResultContract extends
- ActivityResultContract {
-
- @NonNull
- @Override
- public Intent createIntent(@NonNull Context context, Intent input) {
- return input;
- }
-
- @Override
- @NonNull
- public FirebaseAuthUIAuthenticationResult parseResult(int resultCode, @Nullable Intent intent) {
- return new FirebaseAuthUIAuthenticationResult(resultCode, IdpResponse.fromResultIntent(intent));
- }
-
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/FirebaseUIComposeRegistrar.kt b/auth/src/main/java/com/firebase/ui/auth/FirebaseUIComposeRegistrar.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/FirebaseUIComposeRegistrar.kt
rename to auth/src/main/java/com/firebase/ui/auth/FirebaseUIComposeRegistrar.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseUiException.java b/auth/src/main/java/com/firebase/ui/auth/FirebaseUiException.java
deleted file mode 100644
index 2578adad3..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/FirebaseUiException.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.firebase.ui.auth;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Base class for all FirebaseUI exceptions.
- */
-public class FirebaseUiException extends Exception {
- private final int mErrorCode;
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseUiException(@ErrorCodes.Code int code) {
- this(code, ErrorCodes.toFriendlyMessage(code));
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseUiException(@ErrorCodes.Code int code, @NonNull String message) {
- super(message);
- mErrorCode = code;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseUiException(@ErrorCodes.Code int code, @NonNull Throwable cause) {
- this(code, ErrorCodes.toFriendlyMessage(code), cause);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseUiException(@ErrorCodes.Code int code,
- @NonNull String message,
- @NonNull Throwable cause) {
- super(message, cause);
- mErrorCode = code;
- }
-
- /**
- * @return error code associated with this exception
- * @see com.firebase.ui.auth.ErrorCodes
- */
- @ErrorCodes.Code
- public final int getErrorCode() {
- return mErrorCode;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseUiUserCollisionException.java b/auth/src/main/java/com/firebase/ui/auth/FirebaseUiUserCollisionException.java
deleted file mode 100644
index 41511f51f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/FirebaseUiUserCollisionException.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.firebase.ui.auth;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import com.google.firebase.auth.AuthCredential;
-
-/**
- * Internal exception which holds the necessary data to complete sign-in in the event of a
- * recoverable error.
- */
-public class FirebaseUiUserCollisionException extends Exception {
-
- private final int mErrorCode;
- private final String mProviderId;
- private final String mEmail;
- private final AuthCredential mCredential;
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public FirebaseUiUserCollisionException(@ErrorCodes.Code int code,
- @NonNull String message,
- @NonNull String providerId,
- @NonNull String email,
- @NonNull AuthCredential credential) {
- super(message);
- mErrorCode = code;
- mProviderId = providerId;
- mEmail = email;
- mCredential = credential;
- }
-
- @NonNull
- public String getProviderId() {
- return mProviderId;
- }
-
- @NonNull
- public String getEmail() {
- return mEmail;
- }
-
- @NonNull
- public AuthCredential getCredential() {
- return mCredential;
- }
-
- /**
- * @return error code associated with this exception
- * @see ErrorCodes
- */
- @ErrorCodes.Code
- public final int getErrorCode() {
- return mErrorCode;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java b/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java
deleted file mode 100644
index 337188acc..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/IdpResponse.java
+++ /dev/null
@@ -1,401 +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;
-
-import android.annotation.SuppressLint;
-import android.content.Intent;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.google.firebase.auth.AuthCredential;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.GoogleAuthProvider;
-import com.google.firebase.auth.TwitterAuthProvider;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.ObjectOutputStream;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * A container that encapsulates the result of authenticating with an Identity Provider.
- */
-public class IdpResponse implements Parcelable {
- public static final Creator CREATOR = new Creator() {
- @Override
- public IdpResponse createFromParcel(Parcel in) {
- return new IdpResponse(
- in.readParcelable(User.class.getClassLoader()),
- in.readString(),
- in.readString(),
- in.readInt() == 1,
- (FirebaseUiException) in.readSerializable(),
- in.readParcelable(AuthCredential.class.getClassLoader())
- );
- }
-
- @Override
- public IdpResponse[] newArray(int size) {
- return new IdpResponse[size];
- }
- };
-
- private final User mUser;
- private final AuthCredential mPendingCredential;
-
- private final String mToken;
- private final String mSecret;
- private final boolean mIsNewUser;
-
- private final FirebaseUiException mException;
-
- private IdpResponse(@NonNull FirebaseUiException e) {
- this(null, null, null, false, e, null);
- }
-
- private IdpResponse(
- @NonNull User user,
- @Nullable String token,
- @Nullable String secret,
- @Nullable AuthCredential pendingCredential,
- boolean isNewUser) {
- this(user, token, secret, isNewUser, null, pendingCredential);
- }
-
- private IdpResponse(AuthCredential credential, FirebaseUiException e) {
- this(null, null, null, false, e, credential);
- }
-
- private IdpResponse(
- User user,
- String token,
- String secret,
- boolean isNewUser,
- FirebaseUiException e,
- AuthCredential credential) {
- mUser = user;
- mToken = token;
- mSecret = secret;
- mIsNewUser = isNewUser;
- mException = e;
- mPendingCredential = credential;
- }
-
- /**
- * Extract the {@link IdpResponse} from the flow's result intent.
- *
- * @param resultIntent The intent which {@code onActivityResult} was called with.
- * @return The IdpResponse containing the token(s) from signing in with the Idp
- */
- @Nullable
- public static IdpResponse fromResultIntent(@Nullable Intent resultIntent) {
- if (resultIntent != null) {
- return resultIntent.getParcelableExtra(ExtraConstants.IDP_RESPONSE);
- } else {
- return null;
- }
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static IdpResponse from(@NonNull Exception e) {
- if (e instanceof FirebaseUiException) {
- return new IdpResponse((FirebaseUiException) e);
- } else if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- return ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- } else if (e instanceof FirebaseUiUserCollisionException) {
- FirebaseUiUserCollisionException collisionException
- = (FirebaseUiUserCollisionException) e;
- // Lint complains about providerId not being
- // in the pre-defined set of constants
- @SuppressLint("WrongConstant") User user = new User.Builder(
- collisionException.getProviderId(),
- collisionException.getEmail())
- .build();
-
- return new IdpResponse(user,
- /* token= */ null,
- /* secret= */ null,
- /* isNewUser= */ false,
- new FirebaseUiException(collisionException.getErrorCode(),
- collisionException.getMessage()),
- collisionException.getCredential());
- } else {
- FirebaseUiException wrapped
- = new FirebaseUiException(ErrorCodes.UNKNOWN_ERROR, e.getMessage());
- wrapped.setStackTrace(e.getStackTrace());
- return new IdpResponse(wrapped);
- }
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static Intent getErrorIntent(@NonNull Exception e) {
- return from(e).toIntent();
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public IdpResponse withResult(AuthResult result) {
- return mutate().setNewUser(result.getAdditionalUserInfo().isNewUser()).build();
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public Intent toIntent() {
- return new Intent().putExtra(ExtraConstants.IDP_RESPONSE, this);
- }
-
- @NonNull
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public Builder mutate() {
- if (!isSuccessful()) {
- throw new IllegalStateException("Cannot mutate an unsuccessful response.");
- }
- return new Builder(this);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public boolean isSuccessful() {
- return mException == null;
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public User getUser() {
- return mUser;
- }
-
- /**
- * Get the type of provider. e.g. {@link GoogleAuthProvider#PROVIDER_ID}
- */
- @Nullable
- public String getProviderType() {
- return mUser != null ? mUser.getProviderId() : null;
- }
-
- /**
- * Returns true if this user has just signed up, false otherwise.
- */
- public boolean isNewUser() {
- return mIsNewUser;
- }
-
- /**
- * Get the email used to sign in.
- */
- @Nullable
- public String getEmail() {
- return mUser != null ? mUser.getEmail() : null;
- }
-
- /**
- * Get the phone number used to sign in.
- */
- @Nullable
- public String getPhoneNumber() {
- return mUser != null ? mUser.getPhoneNumber() : null;
- }
-
- /**
- * Get the token received as a result of logging in with the specified IDP
- */
- @Nullable
- public String getIdpToken() {
- return mToken;
- }
-
- /**
- * Twitter only. Return the token secret received as a result of logging in with Twitter.
- */
- @Nullable
- public String getIdpSecret() {
- return mSecret;
- }
-
- /**
- * Get the error for a failed sign in.
- */
- @Nullable
- public FirebaseUiException getError() {
- return mException;
- }
-
- @Nullable
- public AuthCredential getCredentialForLinking() {
- return mPendingCredential;
- }
-
- @Nullable
- public boolean hasCredentialForLinking() {
- return mPendingCredential != null;
- }
-
- public boolean isRecoverableErrorResponse() {
- // In a recoverable error flow, both a valid credential that can be used to sign-in and
- // the email keying that account is returned.
- return mPendingCredential != null || getEmail() != null;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mUser, flags);
- dest.writeString(mToken);
- dest.writeString(mSecret);
- dest.writeInt(mIsNewUser ? 1 : 0);
-
- ObjectOutputStream oos = null;
- try {
- oos = new ObjectOutputStream(new ByteArrayOutputStream());
- oos.writeObject(mException);
-
- // Success! The entire exception tree is serializable.
- dest.writeSerializable(mException);
- } catch (IOException e) {
- // Somewhere down the line, the exception is holding on to an object that isn't
- // serializable so default to some exception. It's the best we can do in this case.
- FirebaseUiException fake = new FirebaseUiException(ErrorCodes.UNKNOWN_ERROR,
- "Exception serialization error, forced wrapping. " +
- "Original: " + mException +
- ", original cause: " + mException.getCause());
- fake.setStackTrace(mException.getStackTrace());
- dest.writeSerializable(fake);
- } finally {
- if (oos != null) {
- try {
- oos.close();
- } catch (IOException ignored) {
- }
- }
- }
-
- dest.writeParcelable(mPendingCredential, 0);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- IdpResponse response = (IdpResponse) o;
-
- return (mUser == null ? response.mUser == null : mUser.equals(response.mUser))
- && (mToken == null ? response.mToken == null : mToken.equals(response.mToken))
- && (mSecret == null ? response.mSecret == null : mSecret.equals(response.mSecret))
- && (mIsNewUser == response.mIsNewUser)
- && (mException == null ? response.mException == null : mException.equals(response
- .mException))
- && (mPendingCredential == null ? response.mPendingCredential == null :
- mPendingCredential.getProvider().equals(response.mPendingCredential.getProvider()));
- }
-
- @Override
- public int hashCode() {
- int result = mUser == null ? 0 : mUser.hashCode();
- result = 31 * result + (mToken == null ? 0 : mToken.hashCode());
- result = 31 * result + (mSecret == null ? 0 : mSecret.hashCode());
- result = 31 * result + (mIsNewUser ? 1 : 0);
- result = 31 * result + (mException == null ? 0 : mException.hashCode());
- result = 31 * result + (mPendingCredential == null ? 0 : mPendingCredential.getProvider()
- .hashCode());
- return result;
- }
-
- @Override
- public String toString() {
- return "IdpResponse{" +
- "mUser=" + mUser +
- ", mToken='" + mToken + '\'' +
- ", mSecret='" + mSecret + '\'' +
- ", mIsNewUser='" + mIsNewUser + '\'' +
- ", mException=" + mException +
- ", mPendingCredential=" + mPendingCredential +
- '}';
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static class Builder {
- private User mUser;
- private AuthCredential mPendingCredential;
- private String mToken;
- private String mSecret;
- private boolean mIsNewUser;
-
- public Builder() {}
-
- public Builder(@NonNull User user) {
- mUser = user;
- }
-
- public Builder(@NonNull IdpResponse response) {
- mUser = response.mUser;
- mToken = response.mToken;
- mSecret = response.mSecret;
- mIsNewUser = response.mIsNewUser;
- mPendingCredential = response.mPendingCredential;
- }
-
- public Builder setNewUser(boolean newUser) {
- mIsNewUser = newUser;
- return this;
- }
-
- public Builder setToken(String token) {
- mToken = token;
- return this;
- }
-
- public Builder setSecret(String secret) {
- mSecret = secret;
- return this;
- }
-
- public Builder setPendingCredential(AuthCredential credential) {
- mPendingCredential = credential;
- return this;
- }
-
- public IdpResponse build() {
- if (mPendingCredential != null && mUser == null) {
- return new IdpResponse(mPendingCredential, new FirebaseUiException(ErrorCodes
- .ANONYMOUS_UPGRADE_MERGE_CONFLICT));
- }
-
- String providerId = mUser.getProviderId();
-
- if (AuthUI.SOCIAL_PROVIDERS.contains(providerId) && TextUtils.isEmpty(mToken)) {
- throw new IllegalStateException(
- "Token cannot be null when using a non-email provider.");
- }
- if (providerId.equals(TwitterAuthProvider.PROVIDER_ID)
- && TextUtils.isEmpty(mSecret)) {
- throw new IllegalStateException(
- "Secret cannot be null when using the Twitter provider.");
- }
-
- return new IdpResponse(mUser, mToken, mSecret, mPendingCredential, mIsNewUser);
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java b/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java
deleted file mode 100644
index 3fa619bef..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/KickoffActivity.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.firebase.ui.auth;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.UserCancellationException;
-import com.firebase.ui.auth.data.remote.SignInKickstarter;
-import com.firebase.ui.auth.ui.InvisibleActivityBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.google.android.gms.common.GoogleApiAvailability;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.Tasks;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class KickoffActivity extends InvisibleActivityBase {
- public static SignInKickstarter mKickstarter;
-
- public static Intent createIntent(Context context, FlowParameters flowParams) {
- return createBaseIntent(context, KickoffActivity.class, flowParams);
- }
-
- @Override
- protected void onCreate(@Nullable final Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mKickstarter = new ViewModelProvider(this).get(SignInKickstarter.class);
- mKickstarter.init(getFlowParams());
- mKickstarter.getOperation().observe(this, new ResourceObserver<>(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- finish(RESULT_OK, response.toIntent());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof UserCancellationException) {
- finish(RESULT_CANCELED, null);
- } else if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse res = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(RESULT_CANCELED, new Intent().putExtra(ExtraConstants.IDP_RESPONSE,
- res));
- } else {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
- });
-
- Task checkPlayServicesTask = getFlowParams().isPlayServicesRequired()
- ? GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(this)
- : Tasks.forResult((Void) null);
-
- checkPlayServicesTask
- .addOnSuccessListener(this, aVoid -> {
- if (savedInstanceState != null) {
- return;
- }
-
- mKickstarter.start();
- })
- .addOnFailureListener(this, e -> finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
- ErrorCodes.PLAY_SERVICES_UPDATE_CANCELLED, e))));
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
-
- if (requestCode == RequestCodes.EMAIL_FLOW
- && (resultCode == RequestCodes.EMAIL_LINK_WRONG_DEVICE_FLOW
- || resultCode == RequestCodes.EMAIL_LINK_INVALID_LINK_FLOW)) {
- invalidateEmailLink();
- }
-
- mKickstarter.onActivityResult(requestCode, resultCode, data);
- }
-
- public void invalidateEmailLink() {
- FlowParameters flowParameters = getFlowParams();
- flowParameters.emailLink = null;
- setIntent(getIntent().putExtra(ExtraConstants.FLOW_PARAMS,
- flowParameters));
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/AuthUIConfiguration.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/AuthUIConfiguration.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/AuthUIConfiguration.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/AuthUIConfiguration.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/MfaConfiguration.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/MfaConfiguration.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/MfaConfiguration.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/MfaConfiguration.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/MfaFactor.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/MfaFactor.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/MfaFactor.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/MfaFactor.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/PasswordRule.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/PasswordRule.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/PasswordRule.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/PasswordRule.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AuthProvider.kt
similarity index 98%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AuthProvider.kt
index 097cf9c0a..f4a9ceed6 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AuthProvider.kt
@@ -30,10 +30,10 @@ import com.firebase.ui.auth.compose.configuration.AuthUIConfiguration
import com.firebase.ui.auth.compose.configuration.AuthUIConfigurationDsl
import com.firebase.ui.auth.compose.configuration.PasswordRule
import com.firebase.ui.auth.compose.configuration.theme.AuthUIAsset
+import com.firebase.ui.auth.util.ContinueUrlBuilder
+import com.firebase.ui.auth.util.PhoneNumberUtils
import com.firebase.ui.auth.util.Preconditions
-import com.firebase.ui.auth.util.data.ContinueUrlBuilder
-import com.firebase.ui.auth.util.data.PhoneNumberUtils
-import com.firebase.ui.auth.util.data.ProviderAvailability
+import com.firebase.ui.auth.util.ProviderAvailability
import com.google.android.gms.auth.api.identity.AuthorizationRequest
import com.google.android.gms.auth.api.identity.Identity
import com.google.android.gms.common.api.Scope
@@ -326,10 +326,7 @@ abstract class AuthProvider(open val providerId: String, open val providerName:
}
allowedCountries?.forEach { code ->
- check(
- PhoneNumberUtils.isValidIso(code) ||
- PhoneNumberUtils.isValid(code)
- ) {
+ check(PhoneNumberUtils.isValidIso(code)) {
"Invalid input: You must provide a valid country iso (alpha-2) " +
"or code (e-164). e.g. 'us' or '+1'. Invalid code: $code"
}
@@ -489,21 +486,6 @@ abstract class AuthProvider(open val providerId: String, open val providerName:
*/
val serverClientId: String?,
- /**
- * Requests an ID token. Default to true.
- */
- val requestIdToken: Boolean = true,
-
- /**
- * Requests the user's profile information. Defaults to true.
- */
- val requestProfile: Boolean = true,
-
- /**
- * Requests the user's email address. Defaults to true.
- */
- val requestEmail: Boolean = true,
-
/**
* A map of custom OAuth parameters.
*/
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
index 4e9206b93..0ebf95956 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
@@ -24,8 +24,8 @@ import com.firebase.ui.auth.compose.configuration.AuthUIConfiguration
import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider.Companion.canUpgradeAnonymous
import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider.Companion.mergeProfile
import com.firebase.ui.auth.compose.util.EmailLinkPersistenceManager
-import com.firebase.ui.auth.util.data.EmailLinkParser
-import com.firebase.ui.auth.util.data.SessionUtils
+import com.firebase.ui.auth.util.EmailLinkParser
+import com.firebase.ui.auth.util.SessionUtils
import com.google.firebase.FirebaseApp
import com.google.firebase.auth.ActionCodeSettings
import com.google.firebase.auth.AuthCredential
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProvider.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProviderSample.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProviderSample.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProviderSample.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProviderSample.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/DefaultAuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/DefaultAuthUIStringProvider.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/AuthUIAsset.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/theme/AuthUIAsset.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/AuthUIAsset.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/theme/AuthUIAsset.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/AuthUITheme.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/theme/AuthUITheme.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/AuthUITheme.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/theme/AuthUITheme.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/ProviderStyleDefaults.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/theme/ProviderStyleDefaults.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/theme/ProviderStyleDefaults.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/theme/ProviderStyleDefaults.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/EmailValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/EmailValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/EmailValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/EmailValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/FieldValidationStatus.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/FieldValidationStatus.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/FieldValidationStatus.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/FieldValidationStatus.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/FieldValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/FieldValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/FieldValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/FieldValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/GeneralFieldValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/GeneralFieldValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/GeneralFieldValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/GeneralFieldValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/PasswordValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/PasswordValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/PasswordValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/PasswordValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/PhoneNumberValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/PhoneNumberValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/PhoneNumberValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/PhoneNumberValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/VerificationCodeValidator.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/validators/VerificationCodeValidator.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/configuration/validators/VerificationCodeValidator.kt
rename to auth/src/main/java/com/firebase/ui/auth/configuration/validators/VerificationCodeValidator.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/credentialmanager/PasswordCredential.kt b/auth/src/main/java/com/firebase/ui/auth/credentialmanager/PasswordCredential.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/credentialmanager/PasswordCredential.kt
rename to auth/src/main/java/com/firebase/ui/auth/credentialmanager/PasswordCredential.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/credentialmanager/PasswordCredentialHandler.kt b/auth/src/main/java/com/firebase/ui/auth/credentialmanager/PasswordCredentialHandler.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/credentialmanager/PasswordCredentialHandler.kt
rename to auth/src/main/java/com/firebase/ui/auth/credentialmanager/PasswordCredentialHandler.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/data/Countries.kt b/auth/src/main/java/com/firebase/ui/auth/data/Countries.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/data/Countries.kt
rename to auth/src/main/java/com/firebase/ui/auth/data/Countries.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/data/CountryData.kt b/auth/src/main/java/com/firebase/ui/auth/data/CountryData.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/data/CountryData.kt
rename to auth/src/main/java/com/firebase/ui/auth/data/CountryData.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/client/AuthUiInitProvider.java b/auth/src/main/java/com/firebase/ui/auth/data/client/AuthUiInitProvider.java
deleted file mode 100644
index d90e875d6..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/client/AuthUiInitProvider.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.firebase.ui.auth.data.client;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.pm.ProviderInfo;
-import android.database.Cursor;
-import android.net.Uri;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.util.Preconditions;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class AuthUiInitProvider extends ContentProvider {
- @Override
- public void attachInfo(Context context, ProviderInfo info) {
- Preconditions.checkNotNull(info, "AuthUiInitProvider ProviderInfo cannot be null.");
- if ("com.firebase.ui.auth.authuiinitprovider".equals(info.authority)) {
- throw new IllegalStateException("Incorrect provider authority in manifest. Most" +
- " likely due to a missing applicationId variable in application's build.gradle.");
- } else {
- super.attachInfo(context, info);
- }
- }
-
- @Override
- public boolean onCreate() {
- AuthUI.setApplicationContext(getContext());
- return false;
- }
-
- @Override
- public Cursor query(Uri uri,
- String[] projection,
- String selection,
- String[] selectionArgs,
- String sortOrder) {
- return null;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
- return 0;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/CountryInfo.java b/auth/src/main/java/com/firebase/ui/auth/data/model/CountryInfo.java
deleted file mode 100644
index d245146eb..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/CountryInfo.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- *
- */
-package com.firebase.ui.auth.data.model;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.text.Collator;
-import java.util.Locale;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class CountryInfo implements Comparable, Parcelable {
-
- public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- @Override
- public CountryInfo createFromParcel(Parcel source) {
- return new CountryInfo(source);
- }
-
- @Override
- public CountryInfo[] newArray(int size) {
- return new CountryInfo[size];
- }
- };
-
- private final Collator mCollator;
- private final Locale mLocale;
- private final int mCountryCode;
-
- public CountryInfo(Locale locale, int countryCode) {
- mCollator = Collator.getInstance(Locale.getDefault());
- mCollator.setStrength(Collator.PRIMARY);
- mLocale = locale;
- mCountryCode = countryCode;
- }
-
- protected CountryInfo(Parcel in) {
- mCollator = Collator.getInstance(Locale.getDefault());
- mCollator.setStrength(Collator.PRIMARY);
-
- mLocale = (Locale) in.readSerializable();
- mCountryCode = in.readInt();
- }
-
- public static String localeToEmoji(Locale locale) {
- String countryCode = locale.getCountry();
- // 0x41 is Letter A
- // 0x1F1E6 is Regional Indicator Symbol Letter A
- // Example :
- // firstLetter U => 20 + 0x1F1E6
- // secondLetter S => 18 + 0x1F1E6
- // See: https://en.wikipedia.org/wiki/Regional_Indicator_Symbol
- int firstLetter = Character.codePointAt(countryCode, 0) - 0x41 + 0x1F1E6;
- int secondLetter = Character.codePointAt(countryCode, 1) - 0x41 + 0x1F1E6;
- return new String(Character.toChars(firstLetter)) + new String(Character.toChars
- (secondLetter));
- }
-
- public Locale getLocale() {
- return mLocale;
- }
-
- public int getCountryCode() {
- return mCountryCode;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- final CountryInfo that = (CountryInfo) o;
-
- return mCountryCode == that.mCountryCode
- && (mLocale != null ? mLocale.equals(that.mLocale) : that.mLocale == null);
- }
-
- @Override
- public int hashCode() {
- int result = mLocale != null ? mLocale.hashCode() : 0;
- result = 31 * result + mCountryCode;
- return result;
- }
-
- @Override
- public String toString() {
- return localeToEmoji(mLocale) + " " + mLocale.getDisplayCountry() + " +" + mCountryCode;
- }
-
- public String toShortString() {
- return localeToEmoji(mLocale) + " +" + mCountryCode;
- }
-
- @Override
- public int compareTo(CountryInfo info) {
- Locale defaultLocale = Locale.getDefault();
- return mCollator.compare(
- mLocale.getDisplayCountry().toUpperCase(defaultLocale),
- info.mLocale.getDisplayCountry().toUpperCase(defaultLocale));
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeSerializable(mLocale);
- dest.writeInt(mCountryCode);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java b/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java
deleted file mode 100644
index eea7deabc..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/FirebaseAuthUIAuthenticationResult.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract;
-import com.firebase.ui.auth.IdpResponse;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-/**
- * Result of launching a {@link FirebaseAuthUIActivityResultContract}
- */
-public class FirebaseAuthUIAuthenticationResult {
-
- @Nullable
- private final IdpResponse idpResponse;
- @NonNull
- private final Integer resultCode;
-
- public FirebaseAuthUIAuthenticationResult(@NonNull Integer resultCode, @Nullable IdpResponse idpResponse) {
- this.idpResponse = idpResponse;
- this.resultCode = resultCode;
- }
-
- /**
- * The contained {@link IdpResponse} returned from the Firebase library
- */
- @Nullable
- public IdpResponse getIdpResponse() {
- return idpResponse;
- }
-
- /**
- * The result code of the received activity result
- *
- * @see android.app.Activity.RESULT_CANCELED
- * @see android.app.Activity.RESULT_OK
- */
- @NonNull
- public Integer getResultCode() {
- return resultCode;
- }
-
- @Override
- public int hashCode() {
- int result = idpResponse == null ? 0 : idpResponse.hashCode();
- result = 31 * result + resultCode.hashCode();
- return result;
- }
-
- @Override
- public String toString() {
- return "FirebaseAuthUIAuthenticationResult{" +
- "idpResponse=" + idpResponse +
- ", resultCode='" + resultCode +
- '}';
- }
-}
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
deleted file mode 100644
index c6dac950a..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/FlowParameters.java
+++ /dev/null
@@ -1,223 +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.data.model;
-
-import android.content.Intent;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-
-import com.firebase.ui.auth.AuthMethodPickerLayout;
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.AuthUI.IdpConfig;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.Preconditions;
-import com.google.firebase.auth.ActionCodeSettings;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-import java.util.Collections;
-import java.util.List;
-
-import androidx.annotation.DrawableRes;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StyleRes;
-
-/**
- * Encapsulates the core parameters and data captured during the authentication flow, in a
- * serializable manner, in order to pass data between activities.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class FlowParameters implements Parcelable {
-
- public static final Creator CREATOR = new Creator() {
- @Override
- public FlowParameters createFromParcel(Parcel in) {
- String appName = in.readString();
- List providerInfo = in.createTypedArrayList(IdpConfig.CREATOR);
- IdpConfig defaultProvider = in.readParcelable(IdpConfig.class.getClassLoader());
- int themeId = in.readInt();
- int logoId = in.readInt();
- String termsOfServiceUrl = in.readString();
- String privacyPolicyUrl = in.readString();
- boolean enableCredentials = in.readInt() != 0;
- boolean enableHints = in.readInt() != 0;
- boolean enableAnonymousUpgrade = in.readInt() != 0;
- boolean alwaysShowProviderChoice = in.readInt() != 0;
- boolean lockOrientation = in.readInt() != 0;
- String emailLink = in.readString();
- ActionCodeSettings passwordResetSettings = in.readParcelable(ActionCodeSettings.class.getClassLoader());
- AuthMethodPickerLayout customLayout = in.readParcelable(AuthMethodPickerLayout.class.getClassLoader());
-
- return new FlowParameters(
- appName,
- providerInfo,
- defaultProvider,
- themeId,
- logoId,
- termsOfServiceUrl,
- privacyPolicyUrl,
- enableCredentials,
- enableAnonymousUpgrade,
- alwaysShowProviderChoice,
- lockOrientation,
- emailLink,
- passwordResetSettings,
- customLayout);
- }
-
- @Override
- public FlowParameters[] newArray(int size) {
- return new FlowParameters[size];
- }
- };
-
- @NonNull
- public final String appName;
-
- @NonNull
- public final List providers;
-
- @Nullable
- public final IdpConfig defaultProvider;
-
- @StyleRes
- public final int themeId;
-
- @DrawableRes
- public final int logoId;
-
- @Nullable
- public final String termsOfServiceUrl;
-
- @Nullable
- public final String privacyPolicyUrl;
-
- @Nullable
- public String emailLink;
-
- @Nullable
- public final ActionCodeSettings passwordResetSettings;
-
- public final boolean enableCredentials;
- public final boolean enableAnonymousUpgrade;
- public final boolean alwaysShowProviderChoice;
- public final boolean lockOrientation;
-
- @Nullable
- public final AuthMethodPickerLayout authMethodPickerLayout;
-
- public FlowParameters(
- @NonNull String appName,
- @NonNull List providers,
- @Nullable IdpConfig defaultProvider,
- @StyleRes int themeId,
- @DrawableRes int logoId,
- @Nullable String termsOfServiceUrl,
- @Nullable String privacyPolicyUrl,
- boolean enableCredentials,
- boolean enableAnonymousUpgrade,
- boolean alwaysShowProviderChoice,
- boolean lockOrientation,
- @Nullable String emailLink,
- @Nullable ActionCodeSettings passwordResetSettings,
- @Nullable AuthMethodPickerLayout authMethodPickerLayout) {
- this.appName = Preconditions.checkNotNull(appName, "appName cannot be null");
- this.providers = Collections.unmodifiableList(
- Preconditions.checkNotNull(providers, "providers cannot be null"));
- this.defaultProvider = defaultProvider;
- this.themeId = themeId;
- this.logoId = logoId;
- this.termsOfServiceUrl = termsOfServiceUrl;
- this.privacyPolicyUrl = privacyPolicyUrl;
- this.enableCredentials = enableCredentials;
- this.enableAnonymousUpgrade = enableAnonymousUpgrade;
- this.alwaysShowProviderChoice = alwaysShowProviderChoice;
- this.lockOrientation = lockOrientation;
- this.emailLink = emailLink;
- this.passwordResetSettings = passwordResetSettings;
- this.authMethodPickerLayout = authMethodPickerLayout;
- }
-
- /**
- * Extract FlowParameters from an Intent.
- */
- public static FlowParameters fromIntent(Intent intent) {
- return intent.getParcelableExtra(ExtraConstants.FLOW_PARAMS);
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(appName);
- dest.writeTypedList(providers);
- dest.writeParcelable(defaultProvider, flags);
- dest.writeInt(themeId);
- dest.writeInt(logoId);
- dest.writeString(termsOfServiceUrl);
- dest.writeString(privacyPolicyUrl);
- dest.writeInt(enableCredentials ? 1 : 0);
- dest.writeInt(enableAnonymousUpgrade ? 1 : 0);
- dest.writeInt(alwaysShowProviderChoice ? 1 : 0);
- dest.writeInt(lockOrientation ? 1 : 0);
- dest.writeString(emailLink);
- dest.writeParcelable(passwordResetSettings, flags);
- dest.writeParcelable(authMethodPickerLayout, flags);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public boolean isSingleProviderFlow() {
- return providers.size() == 1;
- }
-
- public boolean isTermsOfServiceUrlProvided() {
- return !TextUtils.isEmpty(termsOfServiceUrl);
- }
-
- public boolean isPrivacyPolicyUrlProvided() {
- return !TextUtils.isEmpty(privacyPolicyUrl);
- }
-
- public boolean isAnonymousUpgradeEnabled() {
- return enableAnonymousUpgrade;
- }
-
- public boolean isPlayServicesRequired() {
- // Play services only required for Google Sign In and the Credentials API
- return isProviderEnabled(GoogleAuthProvider.PROVIDER_ID)
- || enableCredentials;
- }
-
- public boolean isProviderEnabled(@AuthUI.SupportedProvider String provider) {
- for (AuthUI.IdpConfig idp : providers) {
- if (idp.getProviderId().equals(provider)) {
- return true;
- }
- }
-
- return false;
- }
-
- public boolean shouldShowProviderChoice() {
- return defaultProvider == null && (!isSingleProviderFlow() || alwaysShowProviderChoice);
- }
-
- public IdpConfig getDefaultOrFirstProvider() {
- return defaultProvider != null ? defaultProvider : providers.get(0);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/IntentRequiredException.java b/auth/src/main/java/com/firebase/ui/auth/data/model/IntentRequiredException.java
deleted file mode 100644
index 8203f21c9..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/IntentRequiredException.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import android.content.Intent;
-
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class IntentRequiredException extends FirebaseUiException {
- private final Intent mIntent;
- private final int mRequestCode;
-
- public IntentRequiredException(@NonNull Intent intent, int requestCode) {
- super(ErrorCodes.UNKNOWN_ERROR);
- mIntent = intent;
- mRequestCode = requestCode;
- }
-
- @NonNull
- public Intent getIntent() {
- return mIntent;
- }
-
- public int getRequestCode() {
- return mRequestCode;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/PendingIntentRequiredException.java b/auth/src/main/java/com/firebase/ui/auth/data/model/PendingIntentRequiredException.java
deleted file mode 100644
index fddd85a95..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/PendingIntentRequiredException.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import android.app.PendingIntent;
-import android.content.IntentSender;
-
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PendingIntentRequiredException extends FirebaseUiException {
- private final PendingIntent mPendingIntent;
- private final IntentSender mIntentSender;
- private final int mRequestCode;
-
- /**
- * Constructor for cases when a PendingIntent is available.
- *
- * @param pendingIntent The PendingIntent required to complete the operation.
- * @param requestCode The associated request code.
- */
- public PendingIntentRequiredException(@NonNull PendingIntent pendingIntent, int requestCode) {
- super(ErrorCodes.UNKNOWN_ERROR);
- mPendingIntent = pendingIntent;
- mIntentSender = null;
- mRequestCode = requestCode;
- }
-
- /**
- * Constructor for cases when an IntentSender is available.
- *
- * @param intentSender The IntentSender required to complete the operation.
- * @param requestCode The associated request code.
- */
- public PendingIntentRequiredException(@NonNull IntentSender intentSender, int requestCode) {
- super(ErrorCodes.UNKNOWN_ERROR);
- mIntentSender = intentSender;
- mPendingIntent = null;
- mRequestCode = requestCode;
- }
-
- /**
- * Returns the PendingIntent, if available.
- *
- * @return The PendingIntent or null if not available.
- */
- public PendingIntent getPendingIntent() {
- return mPendingIntent;
- }
-
- /**
- * Returns the IntentSender, if available.
- *
- * @return The IntentSender or null if not available.
- */
- public IntentSender getIntentSender() {
- return mIntentSender;
- }
-
- public int getRequestCode() {
- return mRequestCode;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumber.java b/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumber.java
deleted file mode 100644
index be3c6c25a..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumber.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- */
-package com.firebase.ui.auth.data.model;
-
-import android.text.TextUtils;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class PhoneNumber {
- private static final PhoneNumber EMPTY_PHONE_NUMBER = new PhoneNumber("", "", "");
-
- private final String mPhoneNumber;
- private final String mCountryIso;
- private final String mCountryCode;
-
- public PhoneNumber(String phoneNumber, String countryIso, String countryCode) {
- mPhoneNumber = phoneNumber;
- mCountryIso = countryIso;
- mCountryCode = countryCode;
- }
-
- /**
- * Returns an empty instance of this class
- */
- public static PhoneNumber emptyPhone() {
- return EMPTY_PHONE_NUMBER;
- }
-
- public static boolean isValid(PhoneNumber phoneNumber) {
- return phoneNumber != null
- && !EMPTY_PHONE_NUMBER.equals(phoneNumber)
- && !TextUtils.isEmpty(phoneNumber.getPhoneNumber())
- && !TextUtils.isEmpty(phoneNumber.getCountryCode())
- && !TextUtils.isEmpty(phoneNumber.getCountryIso());
- }
-
- public static boolean isCountryValid(PhoneNumber phoneNumber) {
- return phoneNumber != null
- && !EMPTY_PHONE_NUMBER.equals(phoneNumber)
- && !TextUtils.isEmpty(phoneNumber.getCountryCode())
- && !TextUtils.isEmpty(phoneNumber.getCountryIso());
- }
-
- /**
- * Returns country code
- */
- public String getCountryCode() {
- return mCountryCode;
- }
-
- /**
- * Returns phone number without country code
- */
- public String getPhoneNumber() {
- return mPhoneNumber;
- }
-
- /**
- * Returns 2 char country ISO
- */
- public String getCountryIso() {
- return mCountryIso;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumberVerificationRequiredException.java b/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumberVerificationRequiredException.java
deleted file mode 100644
index 9093b018e..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/PhoneNumberVerificationRequiredException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Represents an error in which the phone number couldn't be automatically verified and must
- * therefore be manually verified by the client by sending an SMS code.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PhoneNumberVerificationRequiredException extends FirebaseUiException {
- private final String mPhoneNumber;
-
- /**
- * @param number the phone number requiring verification, formatted with a country code prefix
- */
- public PhoneNumberVerificationRequiredException(@NonNull String number) {
- super(ErrorCodes.PROVIDER_ERROR, "Phone number requires verification.");
- mPhoneNumber = number;
- }
-
- /**
- * @return the phone number requiring verification
- */
- @NonNull
- public String getPhoneNumber() {
- return mPhoneNumber;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/Resource.java b/auth/src/main/java/com/firebase/ui/auth/data/model/Resource.java
deleted file mode 100644
index 3d95bd139..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/Resource.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * Base state model object.
- *
- * This state can either be successful or not. In either case, it must be complete to represent
- * these states.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class Resource {
- private final State mState;
- private final T mValue;
- private final Exception mException;
-
- private boolean mIsUsed;
-
- private Resource(State state, T value, Exception exception) {
- mState = state;
- mValue = value;
- mException = exception;
- }
-
- /**
- * Creates a successful resource containing a value.
- */
- @NonNull
- public static Resource forSuccess(@NonNull T value) {
- return new Resource<>(State.SUCCESS, value, null);
- }
-
- /**
- * Creates a failed resource with an exception.
- */
- @NonNull
- public static Resource forFailure(@NonNull Exception e) {
- return new Resource<>(State.FAILURE, null, e);
- }
-
- /**
- * Creates a resource in the loading state, without a value or an exception.
- */
- @NonNull
- public static Resource forLoading() {
- return new Resource<>(State.LOADING, null, null);
- }
-
- @NonNull
- public State getState() {
- return mState;
- }
-
- @Nullable
- public final Exception getException() {
- mIsUsed = true;
- return mException;
- }
-
- @Nullable
- public T getValue() {
- mIsUsed = true;
- return mValue;
- }
-
- public boolean isUsed() {
- return mIsUsed;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Resource> resource = (Resource>) o;
-
- return mState == resource.mState
- && (mValue == null ? resource.mValue == null : mValue.equals(resource.mValue))
- && (mException == null ? resource.mException == null : mException.equals(resource.mException));
- }
-
- @Override
- public int hashCode() {
- int result = mState.hashCode();
- result = 31 * result + (mValue == null ? 0 : mValue.hashCode());
- result = 31 * result + (mException == null ? 0 : mException.hashCode());
- return result;
- }
-
- @Override
- public String toString() {
- return "Resource{" +
- "mState=" + mState +
- ", mValue=" + mValue +
- ", mException=" + mException +
- '}';
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/State.java b/auth/src/main/java/com/firebase/ui/auth/data/model/State.java
deleted file mode 100644
index 0bab403b1..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/State.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public enum State {
- SUCCESS, FAILURE, LOADING
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/User.java b/auth/src/main/java/com/firebase/ui/auth/data/model/User.java
deleted file mode 100644
index 3979b2a8f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/User.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.util.ExtraConstants;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class User implements Parcelable {
- public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
- @Override
- public User createFromParcel(Parcel in) {
- return new User(
- in.readString(),
- in.readString(),
- in.readString(),
- in.readString(),
- in.readParcelable(Uri.class.getClassLoader()));
- }
-
- @Override
- public User[] newArray(int size) {
- return new User[size];
- }
- };
-
- private final String mProviderId;
- private final String mEmail;
- private final String mPhoneNumber;
- private final String mName;
- private final Uri mPhotoUri;
-
- private User(String providerId, String email, String phoneNumber, String name, Uri photoUri) {
- mProviderId = providerId;
- mEmail = email;
- mPhoneNumber = phoneNumber;
- mName = name;
- mPhotoUri = photoUri;
- }
-
- public static User getUser(Intent intent) {
- return intent.getParcelableExtra(ExtraConstants.USER);
- }
-
- public static User getUser(Bundle arguments) {
- return arguments.getParcelable(ExtraConstants.USER);
- }
-
- @NonNull
- @AuthUI.SupportedProvider
- public String getProviderId() {
- return mProviderId;
- }
-
- @Nullable
- public String getEmail() {
- return mEmail;
- }
-
- @Nullable
- public String getPhoneNumber() {
- return mPhoneNumber;
- }
-
- @Nullable
- public String getName() {
- return mName;
- }
-
- @Nullable
- public Uri getPhotoUri() {
- return mPhotoUri;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- User user = (User) o;
-
- return mProviderId.equals(user.mProviderId)
- && (mEmail == null ? user.mEmail == null : mEmail.equals(user.mEmail))
- && (mPhoneNumber == null ? user.mPhoneNumber == null : mPhoneNumber.equals(user.mPhoneNumber))
- && (mName == null ? user.mName == null : mName.equals(user.mName))
- && (mPhotoUri == null ? user.mPhotoUri == null : mPhotoUri.equals(user.mPhotoUri));
- }
-
- @Override
- public int hashCode() {
- int result = mProviderId.hashCode();
- result = 31 * result + (mEmail == null ? 0 : mEmail.hashCode());
- result = 31 * result + (mPhoneNumber == null ? 0 : mPhoneNumber.hashCode());
- result = 31 * result + (mName == null ? 0 : mName.hashCode());
- result = 31 * result + (mPhotoUri == null ? 0 : mPhotoUri.hashCode());
- return result;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "mProviderId='" + mProviderId + '\'' +
- ", mEmail='" + mEmail + '\'' +
- ", mPhoneNumber='" + mPhoneNumber + '\'' +
- ", mName='" + mName + '\'' +
- ", mPhotoUri=" + mPhotoUri +
- '}';
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(mProviderId);
- dest.writeString(mEmail);
- dest.writeString(mPhoneNumber);
- dest.writeString(mName);
- dest.writeParcelable(mPhotoUri, flags);
- }
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public static class Builder {
- private String mProviderId;
- private String mEmail;
- private String mPhoneNumber;
- private String mName;
- private Uri mPhotoUri;
-
- public Builder(@AuthUI.SupportedProvider @NonNull String providerId,
- @Nullable String email) {
- mProviderId = providerId;
- mEmail = email;
- }
-
- public Builder setPhoneNumber(String phoneNumber) {
- mPhoneNumber = phoneNumber;
- return this;
- }
-
- public Builder setName(String name) {
- mName = name;
- return this;
- }
-
- public Builder setPhotoUri(Uri photoUri) {
- mPhotoUri = photoUri;
- return this;
- }
-
- public User build() {
- return new User(mProviderId, mEmail, mPhoneNumber, mName, mPhotoUri);
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/model/UserCancellationException.java b/auth/src/main/java/com/firebase/ui/auth/data/model/UserCancellationException.java
deleted file mode 100644
index 8c8280948..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/model/UserCancellationException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package com.firebase.ui.auth.data.model;
-
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class UserCancellationException extends FirebaseUiException {
- public UserCancellationException() {
- super(ErrorCodes.UNKNOWN_ERROR);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/AnonymousSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/AnonymousSignInHandler.java
deleted file mode 100644
index daf873700..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/AnonymousSignInHandler.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-import android.content.Intent;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.firebase.FirebaseApp;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.FirebaseAuth;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class AnonymousSignInHandler extends SingleProviderSignInHandler {
-
- @VisibleForTesting
- public FirebaseAuth mAuth;
-
- public AnonymousSignInHandler(Application application) {
- super(application, AuthUI.ANONYMOUS_PROVIDER);
- }
-
- @Override
- protected void onCreate() {
- mAuth = getAuth();
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- setResult(Resource.forLoading());
-
- // Calling signInAnonymously() will always return the same anonymous user if already
- // available. This is enforced by the client SDK.
- mAuth.signInAnonymously()
- .addOnSuccessListener(result -> setResult(Resource.forSuccess(initResponse(
- result.getAdditionalUserInfo().isNewUser()))))
- .addOnFailureListener(e -> setResult(Resource.forFailure(e)));
-
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {}
-
- private IdpResponse initResponse(boolean isNewUser) {
- return new IdpResponse.Builder(
- new User.Builder(AuthUI.ANONYMOUS_PROVIDER, null)
- .build())
- .setNewUser(isNewUser)
- .build();
- }
-
- // TODO: We need to centralize the auth logic. ProviderSignInBase classes were originally
- // meant to only retrieve remote provider data.
- private FirebaseAuth getAuth() {
- return AuthUI.getInstance(getArguments().appName).getAuth();
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/EmailSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/EmailSignInHandler.java
deleted file mode 100644
index d0b846468..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/EmailSignInHandler.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-import android.content.Intent;
-
-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.Resource;
-import com.firebase.ui.auth.data.model.UserCancellationException;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.firebase.ui.auth.ui.email.EmailActivity;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.firebase.auth.EmailAuthCredential;
-import com.google.firebase.auth.EmailAuthProvider;
-import com.google.firebase.auth.FirebaseAuth;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailSignInHandler extends SingleProviderSignInHandler {
- public EmailSignInHandler(Application application) {
- super(application, EmailAuthProvider.PROVIDER_ID);
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- activity.startActivityForResult(
- EmailActivity.createIntent(activity, activity.getFlowParams()),
- RequestCodes.EMAIL_FLOW);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (resultCode == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
- // The activity deals with this case. This conflict is handled by the developer.
- } else if (requestCode == RequestCodes.EMAIL_FLOW) {
- IdpResponse response = IdpResponse.fromResultIntent(data);
- if (response == null) {
- setResult(Resource.forFailure(new UserCancellationException()));
- } else {
- setResult(Resource.forSuccess(response));
- }
- }
- }
-}
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
deleted file mode 100644
index c89dfb9a7..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/FacebookSignInHandler.java
+++ /dev/null
@@ -1,167 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import com.facebook.CallbackManager;
-import com.facebook.FacebookCallback;
-import com.facebook.FacebookException;
-import com.facebook.FacebookRequestError;
-import com.facebook.GraphRequest;
-import com.facebook.GraphResponse;
-import com.facebook.WebDialog;
-import com.facebook.login.LoginManager;
-import com.facebook.login.LoginResult;
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-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;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.FirebaseAuth;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class FacebookSignInHandler extends SingleProviderSignInHandler {
- private static final String EMAIL = "email";
- private static final String PUBLIC_PROFILE = "public_profile";
-
- private List mPermissions;
-
- private final FacebookCallback mCallback = new Callback();
- private final CallbackManager mCallbackManager = CallbackManager.Factory.create();
-
- public FacebookSignInHandler(Application application) {
- super(application, FacebookAuthProvider.PROVIDER_ID);
- }
-
- private static IdpResponse createIdpResponse(
- LoginResult result, @Nullable String email, String name, Uri photoUri) {
- return new IdpResponse.Builder(
- new User.Builder(FacebookAuthProvider.PROVIDER_ID, email)
- .setName(name)
- .setPhotoUri(photoUri)
- .build())
- .setToken(result.getAccessToken().getToken())
- .build();
- }
-
- @Override
- protected void onCreate() {
- List permissions = getArguments().getParams()
- .getStringArrayList(ExtraConstants.FACEBOOK_PERMISSIONS);
- permissions = new ArrayList<>(
- permissions == null ? Collections.emptyList() : permissions);
-
- // Ensure we have email and public_profile permissions
- if (!permissions.contains(EMAIL)) { permissions.add(EMAIL); }
- if (!permissions.contains(PUBLIC_PROFILE)) { permissions.add(PUBLIC_PROFILE); }
-
- mPermissions = permissions;
-
- LoginManager.getInstance().registerCallback(mCallbackManager, mCallback);
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- WebDialog.setWebDialogTheme(activity.getFlowParams().themeId);
- LoginManager.getInstance().logInWithReadPermissions(activity, mPermissions);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- mCallbackManager.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- protected void onCleared() {
- super.onCleared();
- LoginManager.getInstance().unregisterCallback(mCallbackManager);
- }
-
- private class Callback implements FacebookCallback {
- @Override
- public void onSuccess(LoginResult result) {
- setResult(Resource.forLoading());
-
- GraphRequest request = GraphRequest.newMeRequest(result.getAccessToken(),
- new ProfileRequest(result));
-
- Bundle parameters = new Bundle();
- parameters.putString("fields", "id,name,email,picture");
- request.setParameters(parameters);
- request.executeAsync();
- }
-
- @Override
- public void onCancel() {
- setResult(Resource.forFailure(new UserCancellationException()));
- }
-
- @Override
- public void onError(FacebookException e) {
- setResult(Resource.forFailure(new FirebaseUiException(
- ErrorCodes.PROVIDER_ERROR, e)));
- }
- }
-
- private class ProfileRequest implements GraphRequest.GraphJSONObjectCallback {
- private final LoginResult mResult;
-
- public ProfileRequest(LoginResult result) {
- mResult = result;
- }
-
- @Override
- public void onCompleted(JSONObject object, GraphResponse response) {
- FacebookRequestError error = response.getError();
- if (error != null) {
- setResult(Resource.forFailure(new FirebaseUiException(
- ErrorCodes.PROVIDER_ERROR, error.getException())));
- return;
- }
- if (object == null) {
- setResult(Resource.forFailure(new FirebaseUiException(
- ErrorCodes.PROVIDER_ERROR, "Facebook graph request failed")));
- return;
- }
-
- String email = null;
- String name = null;
- Uri photoUri = null;
-
- try {
- email = object.getString("email");
- } catch (JSONException ignored) {}
- try {
- name = object.getString("name");
- } catch (JSONException ignored) {}
- try {
- photoUri = Uri.parse(object.getJSONObject("picture")
- .getJSONObject("data")
- .getString("url"));
- } catch (JSONException ignored) {}
-
- setResult(Resource.forSuccess(createIdpResponse(mResult, email, name, photoUri)));
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpAnonymousUpgradeLinkingHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpAnonymousUpgradeLinkingHandler.java
deleted file mode 100644
index 7838c681c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpAnonymousUpgradeLinkingHandler.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.firebase.ui.auth.util.data.AuthOperationManager;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.OAuthCredential;
-import com.google.firebase.auth.OAuthProvider;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class GenericIdpAnonymousUpgradeLinkingHandler extends GenericIdpSignInHandler {
-
- public GenericIdpAnonymousUpgradeLinkingHandler(Application application) {
- super(application);
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- setResult(Resource.forLoading());
-
- FlowParameters flowParameters = activity.getFlowParams();
- OAuthProvider provider = buildOAuthProvider(providerId, auth);
- if (flowParameters != null
- && AuthOperationManager.getInstance().canUpgradeAnonymous(auth, flowParameters)) {
- handleAnonymousUpgradeLinkingFlow(activity, provider, flowParameters);
- return;
- }
-
- handleNormalSignInFlow(auth, activity, provider);
- }
-
- private void handleAnonymousUpgradeLinkingFlow(final HelperActivityBase activity,
- final OAuthProvider provider,
- final FlowParameters flowParameters) {
- final boolean useEmulator = activity.getAuthUI().isUseEmulator();
- AuthOperationManager.getInstance().safeGenericIdpSignIn(activity, provider, flowParameters)
- .addOnSuccessListener(authResult -> {
- // Pass the credential so we can sign-in on the after the merge
- // conflict is resolved.
- handleSuccess(
- useEmulator,
- provider.getProviderId(),
- authResult.getUser(), (OAuthCredential) authResult.getCredential(),
- /* setPendingCredential= */true);
- })
- .addOnFailureListener(e -> setResult(Resource.forFailure(e)));
-
- }
-}
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
deleted file mode 100644
index 13ba2b851..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/GenericIdpSignInHandler.java
+++ /dev/null
@@ -1,293 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
-import com.firebase.ui.auth.FirebaseUiException;
-import com.firebase.ui.auth.FirebaseUiUserCollisionException;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.FlowParameters;
-
-import android.app.Application;
-import android.content.Intent;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-
-import com.firebase.ui.auth.AuthUI;
-
-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.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;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.android.gms.tasks.OnFailureListener;
-import com.google.android.gms.tasks.OnSuccessListener;
-import com.google.firebase.auth.AuthCredential;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.FacebookAuthProvider;
-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.GoogleAuthProvider;
-import com.google.firebase.auth.OAuthCredential;
-import com.google.firebase.auth.OAuthProvider;
-
-import java.util.HashMap;
-import java.util.List;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class GenericIdpSignInHandler extends ProviderSignInBase {
-
- public GenericIdpSignInHandler(Application application) {
- super(application);
- }
-
- @NonNull
- public static AuthUI.IdpConfig getGenericGoogleConfig() {
- return new AuthUI.IdpConfig.GenericOAuthProviderBuilder(
- GoogleAuthProvider.PROVIDER_ID,
- "Google",
- R.layout.fui_idp_button_google
- ).build();
- }
-
- @NonNull
- public static AuthUI.IdpConfig getGenericFacebookConfig() {
- return new AuthUI.IdpConfig.GenericOAuthProviderBuilder(
- FacebookAuthProvider.PROVIDER_ID,
- "Facebook",
- R.layout.fui_idp_button_facebook
- ).build();
- }
-
- @Override
- public final void startSignIn(@NonNull HelperActivityBase activity) {
- setResult(Resource.forLoading());
- startSignIn(activity.getAuth(), activity, getArguments().getProviderId());
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- setResult(Resource.forLoading());
-
- FlowParameters flowParameters = activity.getFlowParams();
- OAuthProvider provider = buildOAuthProvider(providerId, auth);
- if (flowParameters != null
- && AuthOperationManager.getInstance().canUpgradeAnonymous(auth, flowParameters)) {
- handleAnonymousUpgradeFlow(auth, activity, provider, flowParameters);
- return;
- }
-
- handleNormalSignInFlow(auth, activity, provider);
- }
-
- protected void handleNormalSignInFlow(final FirebaseAuth auth,
- final HelperActivityBase activity,
- final OAuthProvider provider) {
- final boolean useEmulator = activity.getAuthUI().isUseEmulator();
- auth.startActivityForSignInWithProvider(activity, provider)
- .addOnSuccessListener(
- authResult -> handleSuccess(
- useEmulator,
- provider.getProviderId(),
- authResult.getUser(),
- (OAuthCredential) authResult.getCredential(),
- authResult.getAdditionalUserInfo().isNewUser()))
- .addOnFailureListener(
- e -> {
- 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));
- }
- });
-
- }
-
-
- private void handleAnonymousUpgradeFlow(final FirebaseAuth auth,
- final HelperActivityBase activity,
- final OAuthProvider provider,
- final FlowParameters flowParameters) {
- final boolean useEmulator = activity.getAuthUI().isUseEmulator();
- auth.getCurrentUser()
- .startActivityForLinkWithProvider(activity, provider)
- .addOnSuccessListener(
- authResult -> handleSuccess(
- useEmulator,
- provider.getProviderId(),
- authResult.getUser(),
- (OAuthCredential) authResult.getCredential(),
- authResult.getAdditionalUserInfo().isNewUser()))
- .addOnFailureListener(
- e -> {
- if (!(e instanceof FirebaseAuthUserCollisionException)) {
- setResult(Resource.forFailure(e));
- return;
- }
-
- FirebaseAuthUserCollisionException collisionException =
- (FirebaseAuthUserCollisionException) e;
- final AuthCredential credential =
- collisionException.getUpdatedCredential();
- final String email =
- collisionException.getEmail();
-
- // Case 1: Anonymous user trying to link with an existing user
- // Case 2: Anonymous user trying to link with a provider keyed
- // by an email that already belongs to an existing account
- // (linking flow)
- ProviderUtils.fetchSortedProviders(auth, flowParameters, email)
- .addOnSuccessListener(providers -> {
- if (providers.isEmpty()) {
- String errorMessage =
- "Unable to complete the linkingflow -" +
- " the user is using " +
- "unsupported providers.";
- setResult(Resource.forFailure(
- new FirebaseUiException(
- ErrorCodes.DEVELOPER_ERROR,
- errorMessage)));
- return;
- }
-
- if (providers.contains(provider.getProviderId())) {
- // Case 1
- handleMergeFailure(credential);
- } else {
- // Case 2 - linking flow to be handled by
- // SocialProviderResponseHandler
- setResult(Resource.forFailure(
- new FirebaseUiUserCollisionException(
- ErrorCodes.ERROR_GENERIC_IDP_RECOVERABLE_ERROR,
- "Recoverable error.",
- provider.getProviderId(),
- email,
- credential)));
- }
- });
- });
- }
-
- public OAuthProvider buildOAuthProvider(String providerId, FirebaseAuth auth) {
- OAuthProvider.Builder providerBuilder =
- OAuthProvider.newBuilder(providerId, auth);
-
- List scopes =
- getArguments().getParams().getStringArrayList(ExtraConstants.GENERIC_OAUTH_SCOPES);
-
- // This unchecked cast is safe, this extra is put in as a serializable
- // in AuthUI.setCustomParameters
- HashMap customParams =
- (HashMap) getArguments().getParams()
- .getSerializable(ExtraConstants.GENERIC_OAUTH_CUSTOM_PARAMETERS);
-
- if (scopes != null) {
- providerBuilder.setScopes(scopes);
- }
- if (customParams != null) {
- providerBuilder.addCustomParameters(customParams);
- }
-
- return providerBuilder.build();
- }
-
- protected void handleSuccess(boolean isUseEmulator,
- @NonNull String providerId,
- @NonNull FirebaseUser user,
- @NonNull OAuthCredential credential,
- boolean isNewUser,
- boolean setPendingCredential) {
-
- String accessToken = credential.getAccessToken();
- // noinspection ConstantConditions
- if (accessToken == null && isUseEmulator) {
- accessToken = "fake_access_token";
- }
-
- String secret = credential.getSecret();
- if (secret == null && isUseEmulator) {
- secret = "fake_secret";
- }
-
- IdpResponse.Builder response = new IdpResponse.Builder(
- new User.Builder(
- providerId, user.getEmail())
- .setName(user.getDisplayName())
- .setPhotoUri(user.getPhotoUrl())
- .build())
- .setToken(accessToken)
- .setSecret(secret);
-
- if (setPendingCredential) {
- response.setPendingCredential(credential);
- }
- response.setNewUser(isNewUser);
-
- setResult(Resource.forSuccess(response.build()));
- }
-
- protected void handleSuccess(boolean isUseEmulator,
- @NonNull String providerId,
- @NonNull FirebaseUser user,
- @NonNull OAuthCredential credential,
- boolean isNewUser) {
- handleSuccess(isUseEmulator, providerId, user, credential, isNewUser, /* setPendingCredential= */true);
- }
-
-
- protected void handleMergeFailure(@NonNull AuthCredential credential) {
- IdpResponse failureResponse = new IdpResponse.Builder()
- .setPendingCredential(credential).build();
- setResult(Resource.forFailure(new FirebaseAuthAnonymousUpgradeException(
- ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT,
- failureResponse)));
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode == RequestCodes.GENERIC_IDP_SIGN_IN_FLOW) {
- IdpResponse response = IdpResponse.fromResultIntent(data);
- if (response == null) {
- setResult(Resource.forFailure(new UserCancellationException()));
- } else {
- setResult(Resource.forSuccess(response));
- }
- }
- }
-
- @VisibleForTesting
- public void initializeForTesting(AuthUI.IdpConfig idpConfig) {
- setArguments(idpConfig);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/GoogleSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/GoogleSignInHandler.java
deleted file mode 100644
index e494a4cb6..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/GoogleSignInHandler.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-import android.content.Intent;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.ErrorCodes;
-import com.firebase.ui.auth.FirebaseUiException;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.IntentRequiredException;
-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;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.android.gms.auth.api.signin.GoogleSignIn;
-import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
-import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
-import com.google.android.gms.auth.api.signin.GoogleSignInStatusCodes;
-import com.google.android.gms.common.api.ApiException;
-import com.google.android.gms.common.api.CommonStatusCodes;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class GoogleSignInHandler extends SingleProviderSignInHandler {
- private static final String TAG = "GoogleSignInHandler";
-
- private AuthUI.IdpConfig mConfig;
- @Nullable private String mEmail;
-
- public GoogleSignInHandler(Application application) {
- super(application, GoogleAuthProvider.PROVIDER_ID);
- }
-
- private static IdpResponse createIdpResponse(GoogleSignInAccount account) {
- return new IdpResponse.Builder(
- new User.Builder(GoogleAuthProvider.PROVIDER_ID, account.getEmail())
- .setName(account.getDisplayName())
- .setPhotoUri(account.getPhotoUrl())
- .build())
- .setToken(account.getIdToken())
- .build();
- }
-
- @Override
- protected void onCreate() {
- Params params = getArguments();
- mConfig = params.config;
- mEmail = params.email;
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- start();
- }
-
- private void start() {
- setResult(Resource.forLoading());
- setResult(Resource.forFailure(new IntentRequiredException(
- GoogleSignIn.getClient(getApplication(), getSignInOptions()).getSignInIntent(),
- RequestCodes.GOOGLE_PROVIDER)));
- }
-
- private GoogleSignInOptions getSignInOptions() {
- GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(
- mConfig.getParams().getParcelable(
- ExtraConstants.GOOGLE_SIGN_IN_OPTIONS));
-
- if (!TextUtils.isEmpty(mEmail)) {
- builder.setAccountName(mEmail);
- }
-
- return builder.build();
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode != RequestCodes.GOOGLE_PROVIDER) { return; }
-
- try {
- GoogleSignInAccount account = GoogleSignIn.getSignedInAccountFromIntent(data)
- .getResult(ApiException.class);
- setResult(Resource.forSuccess(createIdpResponse(account)));
- } catch (ApiException e) {
- if (e.getStatusCode() == CommonStatusCodes.INVALID_ACCOUNT) {
- // If we get INVALID_ACCOUNT, it means the pre-set account was not available on the
- // device so set the email to null and launch the sign-in picker.
- mEmail = null;
- start();
- } else if (e.getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_CURRENTLY_IN_PROGRESS) {
- // Hack for https://github.com/googlesamples/google-services/issues/345
- // Google remembers the account so the picker doesn't appear twice for the user.
- start();
- } else if (e.getStatusCode() == GoogleSignInStatusCodes.SIGN_IN_CANCELLED) {
- setResult(Resource.forFailure(new UserCancellationException()));
- } else {
- if (e.getStatusCode() == CommonStatusCodes.DEVELOPER_ERROR) {
- Log.w(TAG, "Developer error: this application is misconfigured. " +
- "Check your SHA1 and package name in the Firebase console.");
- }
- setResult(Resource.forFailure(new FirebaseUiException(
- ErrorCodes.PROVIDER_ERROR,
- "Code: " + e.getStatusCode() + ", message: " + e.getMessage())));
- }
- }
- }
-
- public static final class Params {
- private final AuthUI.IdpConfig config;
- @Nullable private final String email;
-
- public Params(AuthUI.IdpConfig config) {
- this(config, null);
- }
-
- public Params(AuthUI.IdpConfig config, @Nullable String email) {
- this.config = config;
- this.email = email;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/PhoneSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/PhoneSignInHandler.java
deleted file mode 100644
index a93ee1b94..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/PhoneSignInHandler.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-import android.content.Intent;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.data.model.UserCancellationException;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.firebase.ui.auth.ui.phone.PhoneActivity;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.PhoneAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PhoneSignInHandler extends SingleProviderSignInHandler {
- public PhoneSignInHandler(Application application) {
- super(application, PhoneAuthProvider.PROVIDER_ID);
- }
-
- @Override
- public void startSignIn(@NonNull FirebaseAuth auth,
- @NonNull HelperActivityBase activity,
- @NonNull String providerId) {
- activity.startActivityForResult(
- PhoneActivity.createIntent(
- activity, activity.getFlowParams(), getArguments().getParams()),
- RequestCodes.PHONE_FLOW);
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode == RequestCodes.PHONE_FLOW) {
- IdpResponse response = IdpResponse.fromResultIntent(data);
- if (response == null) {
- setResult(Resource.forFailure(new UserCancellationException()));
- } else {
- setResult(Resource.forSuccess(response));
- }
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/ProfileMerger.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/ProfileMerger.java
deleted file mode 100644
index 2eb19bd0c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/ProfileMerger.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.util.data.TaskFailureLogger;
-import com.google.android.gms.tasks.Continuation;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.Tasks;
-import com.google.firebase.auth.AuthResult;
-import com.google.firebase.auth.FirebaseUser;
-import com.google.firebase.auth.UserProfileChangeRequest;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-/**
- * Merges an existing account's profile with the new user's profile.
- *
- * Note: This operation always returns a successful task to minimize login interruptions.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ProfileMerger implements Continuation> {
- private static final String TAG = "ProfileMerger";
-
- private final IdpResponse mResponse;
-
- public ProfileMerger(IdpResponse response) {
- mResponse = response;
- }
-
- @Override
- public Task then(@NonNull Task task) {
- final AuthResult authResult = task.getResult();
- FirebaseUser firebaseUser = authResult.getUser();
-
- String name = firebaseUser.getDisplayName();
- Uri photoUri = firebaseUser.getPhotoUrl();
- if (!TextUtils.isEmpty(name) && photoUri != null) {
- return Tasks.forResult(authResult);
- }
-
- User user = mResponse.getUser();
- if (TextUtils.isEmpty(name)) { name = user.getName(); }
- if (photoUri == null) { photoUri = user.getPhotoUri(); }
-
- return firebaseUser.updateProfile(
- new UserProfileChangeRequest.Builder()
- .setDisplayName(name)
- .setPhotoUri(photoUri)
- .build())
- .addOnFailureListener(new TaskFailureLogger(TAG, "Error updating profile"))
- .continueWithTask(task1 -> Tasks.forResult(authResult));
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.kt b/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.kt
deleted file mode 100644
index b665ce1d4..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/SignInKickstarter.kt
+++ /dev/null
@@ -1,247 +0,0 @@
-package com.firebase.ui.auth.data.remote
-
-import android.app.Activity
-import android.app.Application
-import android.content.Intent
-import android.os.Bundle
-import android.text.TextUtils
-import android.util.Log
-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.IntentRequiredException
-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.email.EmailActivity
-import com.firebase.ui.auth.ui.email.EmailLinkCatcherActivity
-import com.firebase.ui.auth.ui.idp.AuthMethodPickerActivity
-import com.firebase.ui.auth.ui.idp.SingleSignInActivity
-import com.firebase.ui.auth.ui.phone.PhoneActivity
-import com.firebase.ui.auth.util.ExtraConstants
-import com.firebase.ui.auth.viewmodel.RequestCodes
-import com.firebase.ui.auth.viewmodel.SignInViewModelBase
-import com.google.android.gms.auth.api.identity.Identity
-import com.google.android.gms.auth.api.identity.SignInCredential
-import com.google.android.gms.common.api.ApiException
-import com.google.firebase.auth.AuthResult
-import com.google.firebase.auth.EmailAuthProvider
-import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException
-import com.google.firebase.auth.FirebaseAuthInvalidUserException
-import com.google.firebase.auth.GoogleAuthProvider
-import com.google.firebase.auth.PhoneAuthProvider
-import kotlinx.coroutines.launch
-import androidx.lifecycle.viewModelScope
-
-import androidx.credentials.Credential
-import androidx.credentials.CustomCredential
-import androidx.credentials.GetCredentialRequest
-import androidx.credentials.GetPasswordOption
-import androidx.credentials.PasswordCredential
-import androidx.credentials.PublicKeyCredential
-import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
-import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
-
-
-private const val TAG = "SignInKickstarter"
-
-class SignInKickstarter(application: Application?) : SignInViewModelBase(application) {
-
- private val app: Application = checkNotNull(application)
-
- /**
- * Entry point. If an email link is detected, immediately launch the email catcher.
- * Otherwise, launch startAuthMethodChoice.
- */
- fun start() {
- if (!TextUtils.isEmpty(arguments.emailLink)) {
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- EmailLinkCatcherActivity.createIntent(app, arguments),
- RequestCodes.EMAIL_FLOW
- )
- )
- )
- return
- }
- startAuthMethodChoice()
- }
-
-
- /**
- * Fallback: if no credential was obtained (or after a failed Credential Manager attempt)
- * choose the proper sign‑in flow.
- */
- private fun startAuthMethodChoice() {
- if (!arguments.shouldShowProviderChoice()) {
- val firstIdpConfig = arguments.defaultOrFirstProvider
- val firstProvider = firstIdpConfig.providerId
- when (firstProvider) {
- AuthUI.EMAIL_LINK_PROVIDER, EmailAuthProvider.PROVIDER_ID ->
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- EmailActivity.createIntent(app, arguments),
- RequestCodes.EMAIL_FLOW
- )
- )
- )
- PhoneAuthProvider.PROVIDER_ID ->
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- PhoneActivity.createIntent(app, arguments, firstIdpConfig.params),
- RequestCodes.PHONE_FLOW
- )
- )
- )
- else -> redirectSignIn(firstProvider, null)
- }
- } else {
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- AuthMethodPickerActivity.createIntent(app, arguments),
- RequestCodes.AUTH_PICKER_FLOW
- )
- )
- )
- }
- }
-
- /**
- * Helper to route to the proper sign‑in activity for a given provider.
- */
- private fun redirectSignIn(provider: String, id: String?) {
- when (provider) {
- EmailAuthProvider.PROVIDER_ID ->
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- EmailActivity.createIntent(app, arguments, id),
- RequestCodes.EMAIL_FLOW
- )
- )
- )
- PhoneAuthProvider.PROVIDER_ID -> {
- val args = Bundle().apply { putString(ExtraConstants.PHONE, id) }
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- PhoneActivity.createIntent(app, arguments, args),
- RequestCodes.PHONE_FLOW
- )
- )
- )
- }
- else ->
- setResult(
- Resource.forFailure(
- IntentRequiredException(
- SingleSignInActivity.createIntent(
- app, arguments, User.Builder(provider, id).build()
- ),
- RequestCodes.PROVIDER_FLOW
- )
- )
- )
- }
- }
-
- /**
- * Legacy onActivityResult handler for other flows.
- */
- fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- when (requestCode) {
- RequestCodes.EMAIL_FLOW,
- RequestCodes.AUTH_PICKER_FLOW,
- RequestCodes.PHONE_FLOW,
- RequestCodes.PROVIDER_FLOW -> {
- if (resultCode == RequestCodes.EMAIL_LINK_WRONG_DEVICE_FLOW ||
- resultCode == RequestCodes.EMAIL_LINK_INVALID_LINK_FLOW
- ) {
- startAuthMethodChoice()
- return
- }
- val response = IdpResponse.fromResultIntent(data)
- if (response == null) {
- setResult(Resource.forFailure(UserCancellationException()))
- } else if (response.isSuccessful) {
- setResult(Resource.forSuccess(response))
- } else if (response.error!!.errorCode == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
- handleMergeFailure(response)
- } else {
- setResult(Resource.forFailure(response.error!!))
- }
- }
- else -> startAuthMethodChoice()
- }
- }
-
- /**
- * Handle a successfully returned Credential from the Credential Manager.
- */
- private fun handleCredentialManagerResult(credential: Credential) {
- when (credential) {
- is PasswordCredential -> {
- val username = credential.id
- val password = credential.password
- val response = IdpResponse.Builder(
- User.Builder(EmailAuthProvider.PROVIDER_ID, username).build()
- ).build()
- setResult(Resource.forLoading())
- auth.signInWithEmailAndPassword(username, password)
- .addOnSuccessListener { authResult: AuthResult ->
- handleSuccess(response, authResult)
- // (Optionally finish the hosting activity here.)
- }
- .addOnFailureListener { e ->
- if (e is FirebaseAuthInvalidUserException ||
- e is FirebaseAuthInvalidCredentialsException
- ) {
- // Sign out using the new API.
- Identity.getSignInClient(app).signOut()
- }
- startAuthMethodChoice()
- }
- }
- is CustomCredential -> {
- if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
- try {
- val googleIdTokenCredential = GoogleIdTokenCredential.createFrom(credential.data)
- auth.signInWithCredential(
- GoogleAuthProvider.getCredential(googleIdTokenCredential.idToken, null)
- )
- .addOnSuccessListener { authResult: AuthResult ->
- val response = IdpResponse.Builder(
- User.Builder(
- GoogleAuthProvider.PROVIDER_ID,
- // Assume the credential data contains the email.
- googleIdTokenCredential.data.getString("email")
- ).build()
- )
- .setToken(googleIdTokenCredential.idToken)
- .build()
- handleSuccess(response, authResult)
- }
- .addOnFailureListener { e ->
- Log.e(TAG, "Failed to sign in with Google ID token", e)
- startAuthMethodChoice()
- }
- } catch (e: GoogleIdTokenParsingException) {
- Log.e(TAG, "Received an invalid google id token response", e)
- startAuthMethodChoice()
- }
- } else {
- Log.e(TAG, "Unexpected type of credential")
- startAuthMethodChoice()
- }
- }
- else -> {
- Log.e(TAG, "Unexpected type of credential")
- startAuthMethodChoice()
- }
- }
- }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/data/remote/SingleProviderSignInHandler.java b/auth/src/main/java/com/firebase/ui/auth/data/remote/SingleProviderSignInHandler.java
deleted file mode 100644
index 502b7bbee..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/data/remote/SingleProviderSignInHandler.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.firebase.ui.auth.data.remote;
-
-import android.app.Application;
-
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class SingleProviderSignInHandler extends ProviderSignInBase {
-
- private final String mProviderId;
-
- protected SingleProviderSignInHandler(Application application, String providerId) {
- super(application);
- this.mProviderId = providerId;
- }
-
- @Override
- public final void startSignIn(@NonNull HelperActivityBase activity) {
- this.startSignIn(activity.getAuth(), activity, mProviderId);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaChallengeContentState.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/MfaChallengeContentState.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaChallengeContentState.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/MfaChallengeContentState.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaEnrollmentContentState.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/MfaEnrollmentContentState.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaEnrollmentContentState.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/MfaEnrollmentContentState.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaEnrollmentStep.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/MfaEnrollmentStep.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaEnrollmentStep.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/MfaEnrollmentStep.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaErrorMapper.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/MfaErrorMapper.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/MfaErrorMapper.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/MfaErrorMapper.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/SmsEnrollmentHandler.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/SmsEnrollmentHandler.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/SmsEnrollmentHandler.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/SmsEnrollmentHandler.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/TotpEnrollmentHandler.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/TotpEnrollmentHandler.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/TotpEnrollmentHandler.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/TotpEnrollmentHandler.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/mfa/TotpSecret.kt b/auth/src/main/java/com/firebase/ui/auth/mfa/TotpSecret.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/mfa/TotpSecret.kt
rename to auth/src/main/java/com/firebase/ui/auth/mfa/TotpSecret.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/package-info.java b/auth/src/main/java/com/firebase/ui/auth/package-info.java
index 5c1ac6f75..20360b5e4 100644
--- a/auth/src/main/java/com/firebase/ui/auth/package-info.java
+++ b/auth/src/main/java/com/firebase/ui/auth/package-info.java
@@ -13,7 +13,7 @@
*/
/**
- * The Firebase AuthUI library. See the {@link com.firebase.ui.auth.AuthUI} entry class for
+ * The Firebase AuthUI library. See the {@link com.firebase.ui.auth.FirebaseAuthUI} entry class for
* information on using the library to manage signed-in user state.
*/
package com.firebase.ui.auth;
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/AppCompatBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/AppCompatBase.java
deleted file mode 100644
index 934f64563..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/AppCompatBase.java
+++ /dev/null
@@ -1,67 +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.ui;
-
-import android.annotation.SuppressLint;
-import android.content.pm.ActivityInfo;
-import android.os.Bundle;
-
-import com.firebase.ui.auth.R;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentTransaction;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class AppCompatBase extends HelperActivityBase {
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setTheme(R.style.FirebaseUI); // Provides default values
- setTheme(getFlowParams().themeId);
-
- if (getFlowParams().lockOrientation) {
- lockOrientation();
- }
- }
-
- protected void switchFragment(@NonNull Fragment fragment,
- int fragmentId,
- @NonNull String tag,
- boolean withTransition,
- boolean addToBackStack) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (withTransition) {
- ft.setCustomAnimations(R.anim.fui_slide_in_right, R.anim.fui_slide_out_left);
- }
- ft.replace(fragmentId, fragment, tag);
- if (addToBackStack) {
- ft.addToBackStack(null).commit();
- } else {
- ft.disallowAddToBackStack().commit();
- }
- }
-
- protected void switchFragment(@NonNull Fragment fragment, int fragmentId, @NonNull String tag) {
- switchFragment(fragment, fragmentId, tag, false, false);
- }
-
- @SuppressLint("SourceLockedOrientationActivity")
- private void lockOrientation() {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/FragmentBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/FragmentBase.java
deleted file mode 100644
index 305b55ac9..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/FragmentBase.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.firebase.ui.auth.ui;
-
-import android.os.Bundle;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.google.firebase.auth.FirebaseUser;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.Fragment;
-import androidx.fragment.app.FragmentActivity;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class FragmentBase extends Fragment implements ProgressView {
- private HelperActivityBase mActivity;
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- FragmentActivity activity = getActivity();
- if (!(activity instanceof HelperActivityBase)) {
- throw new IllegalStateException("Cannot use this fragment without the helper activity");
- }
- mActivity = (HelperActivityBase) activity;
- }
-
- public FlowParameters getFlowParams() {
- return mActivity.getFlowParams();
- }
-
- public void startSaveCredentials(
- FirebaseUser firebaseUser,
- IdpResponse response,
- @Nullable String password) {
- mActivity.startSaveCredentials(firebaseUser, response, password);
- }
-}
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
deleted file mode 100644
index 96714101b..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/HelperActivityBase.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.firebase.ui.auth.ui;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.net.ConnectivityManager;
-
-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;
-import com.firebase.ui.auth.ui.credentials.CredentialSaveActivity;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.FirebaseUser;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.appcompat.app.AppCompatActivity;
-
-import static com.firebase.ui.auth.util.Preconditions.checkNotNull;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class HelperActivityBase extends AppCompatActivity implements ProgressView {
- private FlowParameters mParams;
-
- protected static Intent createBaseIntent(
- @NonNull Context context,
- @NonNull Class extends Activity> target,
- @NonNull FlowParameters flowParams) {
- 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
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- // Forward the results of CredentialManager saving
- if (requestCode == RequestCodes.CRED_SAVE_FLOW
- || resultCode == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) {
- finish(resultCode, data);
- }
- }
-
- public FlowParameters getFlowParams() {
- if (mParams == null) {
- mParams = FlowParameters.fromIntent(getIntent());
- }
- return mParams;
- }
-
- public AuthUI getAuthUI() {
- return AuthUI.getInstance(getFlowParams().appName);
- }
-
- public FirebaseAuth getAuth() {
- return getAuthUI().getAuth();
- }
-
- public void finish(int resultCode, @Nullable Intent intent) {
- setResult(resultCode, intent);
- finish();
- }
-
- /**
- * Starts the CredentialManager save flow.
- *
- *
Instead of building a SmartLock {@link com.google.android.gms.auth.api.credentials.Credential},
- * we now extract the user's email (or phone number as a fallback) and pass it along with the
- * password and response.
- *
- * @param firebaseUser the currently signed-in user.
- * @param response the IdP response.
- * @param password the password used during sign-in (may be {@code null}).
- */
- public void startSaveCredentials(
- FirebaseUser firebaseUser,
- IdpResponse response,
- @Nullable String password) {
- // Extract email; if null, fallback to the phone number.
- String email = firebaseUser.getEmail();
- if (email == null) {
- email = firebaseUser.getPhoneNumber();
- }
- // Start the dedicated CredentialManager Activity.
- Intent intent = CredentialSaveActivity.createIntent(
- this, getFlowParams(), email, password, response);
- startActivityForResult(intent, RequestCodes.CRED_SAVE_FLOW);
- }
-
- /**
- * Check if there is an active or soon-to-be-active network connection.
- *
- * @return true if there is no network connection, false otherwise.
- */
- protected boolean isOffline() {
- ConnectivityManager manager = (ConnectivityManager) getApplicationContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
-
- return !(manager != null
- && manager.getActiveNetworkInfo() != null
- && manager.getActiveNetworkInfo().isConnectedOrConnecting());
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleActivityBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleActivityBase.java
deleted file mode 100644
index ae0769a90..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleActivityBase.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.firebase.ui.auth.ui;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.progressindicator.CircularProgressIndicator;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * Base classes for activities that are just simple overlays.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class InvisibleActivityBase extends HelperActivityBase {
-
- // Minimum time that the spinner will stay on screen, once it is shown.
- private static final long MIN_SPINNER_MS = 750;
-
- private Handler mHandler = new Handler();
- private CircularProgressIndicator mProgressBar;
-
- // Last time that the progress bar was actually shown
- private long mLastShownTime = 0;
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_activity_invisible);
-
- // Create an indeterminate, circular progress bar in the app's theme
- mProgressBar = new CircularProgressIndicator(new ContextThemeWrapper(this, getFlowParams().themeId));
- mProgressBar.setIndeterminate(true);
- mProgressBar.setVisibility(View.GONE);
-
- // Set bar to float in the center
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- params.gravity = Gravity.CENTER;
-
- // Add to the container
- FrameLayout container = findViewById(R.id.invisible_frame);
- container.addView(mProgressBar, params);
- }
-
- @Override
- public void showProgress(int message) {
- if (mProgressBar.getVisibility() == View.VISIBLE) {
- mHandler.removeCallbacksAndMessages(null);
- return;
- }
-
- mLastShownTime = System.currentTimeMillis();
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- doAfterTimeout(() -> {
- mLastShownTime = 0;
- mProgressBar.setVisibility(View.GONE);
- });
- }
-
- @Override
- public void finish(int resultCode, @Nullable Intent intent) {
- setResult(resultCode, intent);
- doAfterTimeout(() -> finish());
- }
-
- /**
- * For certain actions (like finishing or hiding the progress dialog) we want to make sure
- * that we have shown the progress state for at least MIN_SPINNER_MS to prevent flickering.
- *
- * This method performs some action after the window has passed, or immediately if we have
- * already waited longer than that.
- */
- private void doAfterTimeout(Runnable runnable) {
- long currentTime = System.currentTimeMillis();
- long diff = currentTime - mLastShownTime;
-
- // 'diff' is how long it's been since we showed the spinner, so in the
- // case where diff is greater than our minimum spinner duration then our
- // remaining wait time is 0.
- long remaining = Math.max(MIN_SPINNER_MS - diff, 0);
-
- mHandler.postDelayed(runnable, remaining);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleFragmentBase.java b/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleFragmentBase.java
deleted file mode 100644
index d4e3cc2e2..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/InvisibleFragmentBase.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.firebase.ui.auth.ui;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.ContextThemeWrapper;
-import android.view.Gravity;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.FrameLayout;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.progressindicator.CircularProgressIndicator;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class InvisibleFragmentBase extends FragmentBase {
-
- // Minimum time that the spinner will stay on screen, once it is shown.
- private static final long MIN_SPINNER_MS = 750;
- protected FrameLayout mFrameLayout;
- protected View mTopLevelView;
- private Handler mHandler = new Handler();
- private CircularProgressIndicator mProgressBar;
- // Last time that the progress bar was actually shown
- private long mLastShownTime = 0;
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
-
- // Create an indeterminate, circular progress bar in the app's theme
- mProgressBar = new CircularProgressIndicator(new ContextThemeWrapper(getContext(),
- getFlowParams().themeId));
- mProgressBar.setIndeterminate(true);
- mProgressBar.setVisibility(View.GONE);
-
- // Set bar to float in the center
- FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- params.gravity = Gravity.CENTER;
-
- // Add to the container
- mFrameLayout = view.findViewById(R.id.invisible_frame);
- mFrameLayout.addView(mProgressBar, params);
- }
-
-
- @Override
- public void showProgress(int message) {
- if (mProgressBar.getVisibility() == View.VISIBLE) {
- mHandler.removeCallbacksAndMessages(null);
- return;
- }
-
- mLastShownTime = System.currentTimeMillis();
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- doAfterTimeout(() -> {
- mLastShownTime = 0;
- mProgressBar.setVisibility(View.GONE);
- mFrameLayout.setVisibility(View.GONE);
- });
- }
-
- /**
- * For certain actions (like finishing or hiding the progress dialog) we want to make sure
- * that we have shown the progress state for at least MIN_SPINNER_MS to prevent flickering.
- *
- * This method performs some action after the window has passed, or immediately if we have
- * already waited longer than that.
- */
- protected void doAfterTimeout(Runnable runnable) {
- long currentTime = System.currentTimeMillis();
- long diff = currentTime - mLastShownTime;
-
- // 'diff' is how long it's been since we showed the spinner, so in the
- // case where diff is greater than our minimum spinner duration then our
- // remaining wait time is 0.
- long remaining = Math.max(MIN_SPINNER_MS - diff, 0);
-
- mHandler.postDelayed(runnable, remaining);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/ProgressView.java b/auth/src/main/java/com/firebase/ui/auth/ui/ProgressView.java
deleted file mode 100644
index f33daf1de..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/ProgressView.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package com.firebase.ui.auth.ui;
-
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-
-/**
- * View (Activity or Fragment, normally) that can respond to progress events.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public interface ProgressView {
-
- void showProgress(@StringRes int message);
-
- void hideProgress();
-
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/AuthProviderButton.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthProviderButton.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/AuthProviderButton.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthTextField.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/AuthTextField.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthTextField.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/AuthTextField.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/CountrySelector.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/CountrySelector.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/CountrySelector.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/CountrySelector.kt
index 38b4e14ec..5d3e29bdf 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/CountrySelector.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/components/CountrySelector.kt
@@ -57,7 +57,7 @@ import androidx.compose.ui.unit.dp
import com.firebase.ui.auth.compose.configuration.string_provider.LocalAuthUIStringProvider
import com.firebase.ui.auth.compose.data.ALL_COUNTRIES
import com.firebase.ui.auth.compose.data.CountryData
-import com.firebase.ui.auth.compose.data.CountryUtils
+import com.firebase.ui.auth.compose.util.CountryUtils
import kotlinx.coroutines.launch
/**
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/ErrorRecoveryDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/ErrorRecoveryDialog.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/ErrorRecoveryDialog.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/QrCodeImage.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/QrCodeImage.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/QrCodeImage.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/QrCodeImage.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/ReauthenticationDialog.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/ReauthenticationDialog.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/ReauthenticationDialog.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/ReauthenticationDialog.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/TermsAndPrivacyForm.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/TermsAndPrivacyForm.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/TermsAndPrivacyForm.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/TermsAndPrivacyForm.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/TopLevelDialogController.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/TopLevelDialogController.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/TopLevelDialogController.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/components/VerificationCodeInputField.kt b/auth/src/main/java/com/firebase/ui/auth/ui/components/VerificationCodeInputField.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/components/VerificationCodeInputField.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/components/VerificationCodeInputField.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/credentials/CredentialSaveActivity.kt b/auth/src/main/java/com/firebase/ui/auth/ui/credentials/CredentialSaveActivity.kt
deleted file mode 100644
index c539de0c7..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/credentials/CredentialSaveActivity.kt
+++ /dev/null
@@ -1,86 +0,0 @@
-package com.firebase.ui.auth.ui.credentials
-
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import android.util.Log
-import androidx.lifecycle.ViewModelProvider
-import com.firebase.ui.auth.IdpResponse
-import com.firebase.ui.auth.data.model.FlowParameters
-import com.firebase.ui.auth.data.model.Resource
-import com.firebase.ui.auth.ui.InvisibleActivityBase
-import com.firebase.ui.auth.util.ExtraConstants
-import com.firebase.ui.auth.viewmodel.ResourceObserver
-import com.firebase.ui.auth.viewmodel.credentialmanager.CredentialManagerHandler
-import com.google.firebase.auth.FirebaseAuth
-
-class CredentialSaveActivity : InvisibleActivityBase() {
-
- private lateinit var credentialManagerHandler: CredentialManagerHandler
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- val response: IdpResponse? = intent.getParcelableExtra(ExtraConstants.IDP_RESPONSE)
- val emailExtra: String? = intent.getStringExtra(ExtraConstants.EMAIL)
- val password: String? = intent.getStringExtra(ExtraConstants.PASSWORD)
-
- credentialManagerHandler = ViewModelProvider(this)
- .get(CredentialManagerHandler::class.java)
- .apply {
- // Initialize with flow parameters.
- init(flowParams)
- // Pass the IdP response if present.
- response?.let { setResponse(it) }
-
- // Observe the operation's result.
- operation.observe(
- this@CredentialSaveActivity,
- object : ResourceObserver(this@CredentialSaveActivity) {
- override fun onSuccess(response: IdpResponse) {
- finish(RESULT_OK, response.toIntent())
- }
-
- override fun onFailure(e: Exception) {
- // Even if saving fails, do not block the sign-in flow.
- response?.let {
- finish(RESULT_OK, it.toIntent())
- } ?: finish(RESULT_OK, null)
- }
- }
- )
- }
-
- val currentOp: Resource? = credentialManagerHandler.operation.value
-
- if (currentOp == null) {
- Log.d(TAG, "Launching save operation.")
- // With the new CredentialManager, pass the email and password directly.
- val firebaseUser = FirebaseAuth.getInstance().currentUser
- val email = firebaseUser?.email ?: emailExtra
-
- credentialManagerHandler.saveCredentials(this, firebaseUser, email, password)
- } else {
- Log.d(TAG, "Save operation in progress, doing nothing.")
- }
- }
-
- companion object {
- private const val TAG = "CredentialSaveActivity"
-
- @JvmStatic
- fun createIntent(
- context: Context,
- flowParams: FlowParameters,
- email: String,
- password: String?,
- response: IdpResponse
- ): Intent {
- return createBaseIntent(context, CredentialSaveActivity::class.java, flowParams).apply {
- putExtra(ExtraConstants.EMAIL, email)
- putExtra(ExtraConstants.PASSWORD, password)
- putExtra(ExtraConstants.IDP_RESPONSE, response)
- }
- }
- }
-}
\ No newline at end of file
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
deleted file mode 100644
index b34ce6ffa..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailFragment.java
+++ /dev/null
@@ -1,238 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.ImeHelper;
-import com.firebase.ui.auth.util.ui.fieldvalidators.EmailFieldValidator;
-import com.google.android.material.snackbar.Snackbar;
-import com.google.android.material.textfield.TextInputLayout;
-import com.google.firebase.auth.EmailAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.ViewModelProvider;
-
-import static com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER;
-
-/**
- * Fragment that shows a form with an email field and checks for existing accounts with that email.
- *
- * Host Activities should implement {@link CheckEmailFragment.CheckEmailListener}.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CheckEmailFragment extends FragmentBase implements
- View.OnClickListener,
- ImeHelper.DonePressedListener {
-
- public static final String TAG = "CheckEmailFragment";
- private CheckEmailHandler mHandler;
- private Button mSignInButton;
- private Button mSignUpButton;
- private ProgressBar mProgressBar;
- private EditText mEmailEditText;
- private TextInputLayout mEmailLayout;
- private EmailFieldValidator mEmailFieldValidator;
- private CheckEmailListener mListener;
-
- public static CheckEmailFragment newInstance(@Nullable String email) {
- CheckEmailFragment fragment = new CheckEmailFragment();
- Bundle args = new Bundle();
- args.putString(ExtraConstants.EMAIL, email);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_check_email_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mSignInButton = view.findViewById(R.id.button_sign_in);
- mSignUpButton = view.findViewById(R.id.button_sign_up);
- mProgressBar = view.findViewById(R.id.top_progress_bar);
-
- mEmailLayout = view.findViewById(R.id.email_layout);
- mEmailEditText = view.findViewById(R.id.email);
- mEmailFieldValidator = new EmailFieldValidator(mEmailLayout);
- mEmailLayout.setOnClickListener(this);
- mEmailEditText.setOnClickListener(this);
-
- TextView headerText = view.findViewById(R.id.header_text);
- if (headerText != null) {
- headerText.setVisibility(View.GONE);
- }
-
- ImeHelper.setImeOnDoneListener(mEmailEditText, this);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- mEmailEditText.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
- }
-
- // Set listeners for our new sign‑in and sign‑up buttons.
- mSignInButton.setOnClickListener(this);
- mSignUpButton.setOnClickListener(this);
-
- // Hide sign up button for email link authentication
- if (getEmailProvider().equals(EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD)) {
- mSignUpButton.setVisibility(View.GONE);
- }
-
- TextView termsText = view.findViewById(R.id.email_tos_and_pp_text);
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- FlowParameters flowParameters = getFlowParams();
-
- if (!flowParameters.shouldShowProviderChoice()) {
- PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicyText(requireContext(),
- flowParameters,
- termsText);
- } else {
- termsText.setVisibility(View.GONE);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(),
- flowParameters,
- footerText);
- }
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mHandler = new ViewModelProvider(this).get(CheckEmailHandler.class);
- mHandler.init(getFlowParams());
-
- FragmentActivity activity = getActivity();
- if (!(activity instanceof CheckEmailListener)) {
- throw new IllegalStateException("Activity must implement CheckEmailListener");
- }
- mListener = (CheckEmailListener) activity;
-
- // Removed the observer on mHandler.getOperation() since we no longer rely on provider info.
-
- if (savedInstanceState == null) {
- String email = getArguments().getString(ExtraConstants.EMAIL);
- if (!TextUtils.isEmpty(email)) {
- mEmailEditText.setText(email);
- // Previously auto-triggering the check is now removed.
- } else if (getFlowParams().enableCredentials) {
- mHandler.fetchCredential();
- }
- }
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- mHandler.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public void onClick(View view) {
- int id = view.getId();
-
- if (id == R.id.button_sign_in) {
- signIn();
- } else if (id == R.id.button_sign_up) {
- signUp();
- } else if (id == R.id.email_layout || id == R.id.email) {
- mEmailLayout.setError(null);
- }
- }
-
- @Override
- public void onDonePressed() {
- // When the user hits “done” on the keyboard, default to sign‑in.
- signIn();
- }
-
- private String getEmailProvider() {
- // Iterate through all IdpConfig entries
- for (AuthUI.IdpConfig config : getFlowParams().providers) {
- // Assuming there is a getter for the provider ID
- if (EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD.equals(config.getProviderId())) {
- return EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD;
- }
- }
- // Default to standard email/password
- return EmailAuthProvider.PROVIDER_ID;
- }
-
- private void signIn() {
- String email = mEmailEditText.getText().toString();
- if (mEmailFieldValidator.validate(email)) {
- String provider = getEmailProvider();
- User user = new User.Builder(provider, email).build();
- mListener.onExistingEmailUser(user);
- }
- }
-
- private void signUp() {
- String email = mEmailEditText.getText().toString();
- if (mEmailFieldValidator.validate(email)) {
- String provider = getEmailProvider();
- User user = new User.Builder(provider, email).build();
- mListener.onNewUser(user);
- }
- }
-
- @Override
- public void showProgress(int message) {
- // Disable both buttons while progress is showing.
- mSignInButton.setEnabled(false);
- mSignUpButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mSignInButton.setEnabled(true);
- mSignUpButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-
- /**
- * Interface to be implemented by Activities hosting this Fragment.
- */
- interface CheckEmailListener {
-
- /**
- * Email entered belongs to an existing email user (sign‑in flow).
- */
- void onExistingEmailUser(User user);
-
- /**
- * Email entered belongs to an existing IDP user.
- */
- void onExistingIdpUser(User user);
-
- /**
- * Email entered does not belong to an existing user (sign‑up flow).
- */
- void onNewUser(User user);
-
- /**
- * Email entered corresponds to an existing user whose sign in methods we do not support.
- */
- void onDeveloperFailure(Exception e);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailHandler.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailHandler.java
deleted file mode 100644
index 3a911a417..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/CheckEmailHandler.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.app.Activity;
-import android.app.Application;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.util.Log;
-
-import com.firebase.ui.auth.data.model.PendingIntentRequiredException;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.viewmodel.AuthViewModelBase;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.android.gms.common.api.ApiException;
-import com.google.android.gms.tasks.Task;
-
-// New Identity API imports:
-import com.google.android.gms.auth.api.identity.BeginSignInRequest;
-import com.google.android.gms.auth.api.identity.SignInClient;
-import com.google.android.gms.auth.api.identity.SignInCredential;
-import com.google.android.gms.auth.api.identity.Identity;
-
-import androidx.annotation.Nullable;
-
-public class CheckEmailHandler extends AuthViewModelBase {
- private static final String TAG = "CheckEmailHandler";
-
- public CheckEmailHandler(Application application) {
- super(application);
- }
-
- /**
- * Initiates a hint picker flow using the new Identity API.
- * This replaces the deprecated Credentials API call.
- */
- public void fetchCredential() {
- // Build a sign-in request that supports password-based sign in,
- // which will trigger the hint picker UI for email addresses.
- SignInClient signInClient = Identity.getSignInClient(getApplication());
- BeginSignInRequest signInRequest = BeginSignInRequest.builder()
- .setPasswordRequestOptions(
- BeginSignInRequest.PasswordRequestOptions.builder()
- .setSupported(true)
- .build())
- .build();
-
- signInClient.beginSignIn(signInRequest)
- .addOnSuccessListener(result -> {
- // The new API returns a PendingIntent to launch the hint picker.
- PendingIntent pendingIntent = result.getPendingIntent();
- setResult(Resource.forFailure(
- new PendingIntentRequiredException(pendingIntent, RequestCodes.CRED_HINT)));
- })
- .addOnFailureListener(e -> {
- Log.e(TAG, "beginSignIn failed", e);
- setResult(Resource.forFailure(e));
- });
- }
-
- /**
- * Fetches the top provider for the given email.
- */
- public void fetchProvider(final String email) {
- setResult(Resource.forLoading());
- ProviderUtils.fetchTopProvider(getAuth(), getArguments(), email)
- .addOnCompleteListener(task -> {
- if (task.isSuccessful()) {
- setResult(Resource.forSuccess(
- new User.Builder(task.getResult(), email).build()));
- } else {
- setResult(Resource.forFailure(task.getException()));
- }
- });
- }
-
- /**
- * Handles the result from the hint picker launched via the new Identity API.
- */
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode != RequestCodes.CRED_HINT || resultCode != Activity.RESULT_OK) {
- return;
- }
-
- setResult(Resource.forLoading());
- SignInClient signInClient = Identity.getSignInClient(getApplication());
- try {
- // Retrieve the SignInCredential from the returned intent.
- SignInCredential credential = signInClient.getSignInCredentialFromIntent(data);
- final String email = credential.getId();
-
- ProviderUtils.fetchTopProvider(getAuth(), getArguments(), email)
- .addOnCompleteListener(task -> {
- if (task.isSuccessful()) {
- setResult(Resource.forSuccess(new User.Builder(task.getResult(), email)
- .setName(credential.getDisplayName())
- .setPhotoUri(credential.getProfilePictureUri())
- .build()));
- } else {
- setResult(Resource.forFailure(task.getException()));
- }
- });
- } catch (ApiException e) {
- Log.e(TAG, "getSignInCredentialFromIntent failed", e);
- setResult(Resource.forFailure(e));
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailActivity.java
deleted file mode 100644
index feabf506d..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailActivity.java
+++ /dev/null
@@ -1,247 +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.ui.email;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.ErrorCodes;
-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.data.model.User;
-import com.firebase.ui.auth.ui.AppCompatBase;
-import com.firebase.ui.auth.ui.idp.WelcomeBackIdpPrompt;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.EmailLinkPersistenceManager;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.android.material.textfield.TextInputLayout;
-import com.google.firebase.auth.ActionCodeSettings;
-import com.google.firebase.auth.EmailAuthProvider;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.core.view.ViewCompat;
-import androidx.fragment.app.FragmentTransaction;
-
-import static com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER;
-
-/**
- * Activity to control the entire email sign up flow. Plays host to {@link CheckEmailFragment} and
- * {@link RegisterEmailFragment} and triggers {@link WelcomeBackPasswordPrompt} and {@link
- * WelcomeBackIdpPrompt}.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailActivity extends AppCompatBase implements CheckEmailFragment.CheckEmailListener,
- RegisterEmailFragment.AnonymousUpgradeListener, EmailLinkFragment
- .TroubleSigningInListener, TroubleSigningInFragment.ResendEmailListener {
-
- public static Intent createIntent(Context context, FlowParameters flowParams) {
- return createBaseIntent(context, EmailActivity.class, flowParams);
- }
-
- public static Intent createIntent(Context context, FlowParameters flowParams, String email) {
- return createBaseIntent(context, EmailActivity.class, flowParams)
- .putExtra(ExtraConstants.EMAIL, email);
- }
-
- public static Intent createIntentForLinking(Context context, FlowParameters flowParams,
- IdpResponse responseForLinking) {
- return createIntent(context, flowParams, responseForLinking.getEmail())
- .putExtra(ExtraConstants.IDP_RESPONSE, responseForLinking);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_activity_register_email);
-
- if (savedInstanceState != null) {
- return;
- }
-
- // Get email from intent (can be null)
- String email = getIntent().getExtras().getString(ExtraConstants.EMAIL);
-
- IdpResponse responseForLinking = getIntent().getExtras().getParcelable(ExtraConstants
- .IDP_RESPONSE);
- if (email != null && responseForLinking != null) {
- // got here from WelcomeBackEmailLinkPrompt
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
- getFlowParams().providers, EMAIL_LINK_PROVIDER);
- ActionCodeSettings actionCodeSettings = emailConfig.getParams().getParcelable
- (ExtraConstants.ACTION_CODE_SETTINGS);
-
- EmailLinkPersistenceManager.getInstance().saveIdpResponseForLinking(getApplication(),
- responseForLinking);
-
- boolean forceSameDevice =
- emailConfig.getParams().getBoolean(ExtraConstants.FORCE_SAME_DEVICE);
- EmailLinkFragment fragment = EmailLinkFragment.newInstance(email, actionCodeSettings,
- responseForLinking, forceSameDevice);
- switchFragment(fragment, R.id.fragment_register_email, EmailLinkFragment.TAG);
- } else {
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdps(
- getFlowParams().providers, EmailAuthProvider.PROVIDER_ID);
-
- if (emailConfig != null) {
- email = emailConfig.getParams().getString(ExtraConstants.DEFAULT_EMAIL);;
- }
- // Start with check email
- CheckEmailFragment fragment = CheckEmailFragment.newInstance(email);
- switchFragment(fragment, R.id.fragment_register_email, CheckEmailFragment.TAG);
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == RequestCodes.WELCOME_BACK_EMAIL_FLOW
- || requestCode == RequestCodes.WELCOME_BACK_IDP_FLOW) {
- finish(resultCode, data);
- }
- }
-
- @Override
- public void onExistingEmailUser(User user) {
- if (user.getProviderId().equals(EMAIL_LINK_PROVIDER)) {
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
- getFlowParams().providers, EMAIL_LINK_PROVIDER);
- showRegisterEmailLinkFragment(
- emailConfig, user.getEmail());
- } else {
- startActivityForResult(
- WelcomeBackPasswordPrompt.createIntent(
- this, getFlowParams(), new IdpResponse.Builder(user).build()),
- RequestCodes.WELCOME_BACK_EMAIL_FLOW);
- setSlideAnimation();
- }
- }
-
- @Override
- public void onExistingIdpUser(User user) {
- // Existing social user, direct them to sign in using their chosen provider.
- startActivityForResult(
- WelcomeBackIdpPrompt.createIntent(this, getFlowParams(), user),
- RequestCodes.WELCOME_BACK_IDP_FLOW);
- setSlideAnimation();
- }
-
- @Override
- public void onNewUser(User user) {
- // New user, direct them to create an account with email/password
- // if account creation is enabled in SignInIntentBuilder
-
- TextInputLayout emailLayout = findViewById(R.id.email_layout);
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdps(getFlowParams().providers,
- EmailAuthProvider.PROVIDER_ID);
-
- if (emailConfig == null) {
- emailConfig = ProviderUtils.getConfigFromIdps(getFlowParams().providers,
- EMAIL_LINK_PROVIDER);
- }
-
- if (emailConfig.getParams().getBoolean(ExtraConstants.ALLOW_NEW_EMAILS, true)) {
- FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
- if (emailConfig.getProviderId().equals(EMAIL_LINK_PROVIDER)) {
- showRegisterEmailLinkFragment(emailConfig, user.getEmail());
- } else {
- RegisterEmailFragment fragment = RegisterEmailFragment.newInstance(user);
- ft.replace(R.id.fragment_register_email, fragment, RegisterEmailFragment.TAG);
- if (emailLayout != null) {
- String emailFieldName = getString(R.string.fui_email_field_name);
- ViewCompat.setTransitionName(emailLayout, emailFieldName);
- ft.addSharedElement(emailLayout, emailFieldName);
- }
- ft.disallowAddToBackStack().commit();
- }
- } else {
- emailLayout.setError(getString(R.string.fui_error_email_does_not_exist));
- }
- }
-
- @Override
- public void onTroubleSigningIn(String email) {
- TroubleSigningInFragment troubleSigningInFragment = TroubleSigningInFragment.newInstance
- (email);
- switchFragment(troubleSigningInFragment, R.id.fragment_register_email,
- TroubleSigningInFragment.TAG, true, true);
- }
-
- @Override
- public void onClickResendEmail(String email) {
- if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
- // We're assuming that to get to the TroubleSigningInFragment, we went through
- // the EmailLinkFragment, which was added to the fragment back stack.
- // From here, we're going to register the EmailLinkFragment again, meaning we'd have to
- // pop off the back stack twice to return to the nascar screen. To avoid this,
- // we pre-emptively pop off the last EmailLinkFragment here.
- getSupportFragmentManager().popBackStack();
- }
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
- getFlowParams().providers, EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD);
- showRegisterEmailLinkFragment(
- emailConfig, email);
- }
-
- @Override
- public void onSendEmailFailure(Exception e) {
- finishOnDeveloperError(e);
- }
-
- @Override
- public void onDeveloperFailure(Exception e) {
- finishOnDeveloperError(e);
- }
-
- private void finishOnDeveloperError(Exception e) {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
- ErrorCodes.DEVELOPER_ERROR, e.getMessage())));
- }
-
- private void setSlideAnimation() {
- // Make the next activity slide in
- overridePendingTransition(R.anim.fui_slide_in_right, R.anim.fui_slide_out_left);
- }
-
- private void showRegisterEmailLinkFragment(AuthUI.IdpConfig emailConfig,
- String email) {
- ActionCodeSettings actionCodeSettings = emailConfig.getParams().getParcelable
- (ExtraConstants.ACTION_CODE_SETTINGS);
- EmailLinkFragment fragment = EmailLinkFragment.newInstance(email,
- actionCodeSettings);
- switchFragment(fragment, R.id.fragment_register_email, EmailLinkFragment.TAG);
- }
-
- @Override
- public void showProgress(@StringRes int message) {
- throw new UnsupportedOperationException("Email fragments must handle progress updates.");
- }
-
- @Override
- public void hideProgress() {
- throw new UnsupportedOperationException("Email fragments must handle progress updates.");
- }
-
- @Override
- public void onMergeFailure(IdpResponse response) {
- finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent());
- }
-}
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
deleted file mode 100644
index 50a7bfb0f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCatcherActivity.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.app.AlertDialog;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-
-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.data.model.UserCancellationException;
-import com.firebase.ui.auth.ui.InvisibleActivityBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.email.EmailLinkSignInHandler;
-import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkCatcherActivity extends InvisibleActivityBase {
-
- private EmailLinkSignInHandler mHandler;
-
- public static Intent createIntent(Context context, FlowParameters flowParams) {
- return createBaseIntent(context, EmailLinkCatcherActivity.class, flowParams);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- initHandler();
-
- if (getFlowParams().emailLink != null) {
- mHandler.startSignIn();
- }
- }
-
- private void initHandler() {
- mHandler = new ViewModelProvider(this).get(EmailLinkSignInHandler.class);
- mHandler.init(getFlowParams());
- mHandler.getOperation().observe(this, new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- finish(RESULT_OK, response.toIntent());
- }
-
- @Override
- protected void onFailure(@NonNull final Exception e) {
- if (e instanceof UserCancellationException) {
- finish(RESULT_CANCELED, null);
- } else if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse res = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(RESULT_CANCELED, new Intent().putExtra(ExtraConstants.IDP_RESPONSE, res));
- } else if (e instanceof FirebaseUiException) {
- int errorCode = ((FirebaseUiException) e).getErrorCode();
- if (errorCode == ErrorCodes.EMAIL_LINK_WRONG_DEVICE_ERROR
- || errorCode == ErrorCodes.INVALID_EMAIL_LINK_ERROR
- || errorCode == ErrorCodes.EMAIL_LINK_DIFFERENT_ANONYMOUS_USER_ERROR) {
- buildAlertDialog(errorCode).show();
- } else if (errorCode == ErrorCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_ERROR
- || errorCode == ErrorCodes.EMAIL_MISMATCH_ERROR) {
- startErrorRecoveryFlow(RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW);
- } else if (errorCode == ErrorCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_ERROR) {
- startErrorRecoveryFlow(RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW);
- }
- } else if (e instanceof FirebaseAuthInvalidCredentialsException) {
- startErrorRecoveryFlow(RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW);
- } else {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
- });
- }
-
- /**
- * @param flow must be one of RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW or
- * RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW
- */
- private void startErrorRecoveryFlow(int flow) {
- if (flow != RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW
- && flow != RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW) {
- throw new IllegalStateException("Invalid flow param. It must be either " +
- "RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW or " +
- "RequestCodes.EMAIL_LINK_PROMPT_FOR_EMAIL_FLOW");
- }
- Intent intent = EmailLinkErrorRecoveryActivity.createIntent(getApplicationContext(),
- getFlowParams(), flow);
- startActivityForResult(intent, flow);
- }
-
- private AlertDialog buildAlertDialog(final int errorCode) {
- AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);
-
- String titleText;
- String messageText;
- if (errorCode == ErrorCodes.EMAIL_LINK_DIFFERENT_ANONYMOUS_USER_ERROR) {
- titleText = getString(R.string.fui_email_link_different_anonymous_user_header);
- messageText = getString(R.string.fui_email_link_different_anonymous_user_message);
- } else if (errorCode == ErrorCodes.INVALID_EMAIL_LINK_ERROR) {
- titleText = getString(R.string.fui_email_link_invalid_link_header);
- messageText = getString(R.string.fui_email_link_invalid_link_message);
- } else {
- // Default value - ErrorCodes.EMAIL_LINK_WRONG_DEVICE_ERROR
- titleText = getString(R.string.fui_email_link_wrong_device_header);
- messageText = getString(R.string.fui_email_link_wrong_device_message);
- }
-
- return alertDialog.setTitle(titleText)
- .setMessage(messageText)
- .setPositiveButton(R.string.fui_email_link_dismiss_button,
- (dialog, id) -> finish(errorCode, null))
- .create();
- }
-
- @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);
- // CheckActionCode is called before starting this flow, so we only get here
- // if the sign in link is valid - it can only fail by being cancelled.
- if (resultCode == RESULT_OK) {
- finish(RESULT_OK, response.toIntent());
- } else {
- finish(RESULT_CANCELED, null);
- }
- }
- }
-}
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
deleted file mode 100644
index d22da246f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkCrossDeviceLinkingFragment.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.annotation.SuppressLint;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.data.EmailLinkParser;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.util.ui.TextHelper;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-
-/**
- * Fragment that tells the user that a linking flow cannot be completed as they have opened the
- * email link on a different device.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkCrossDeviceLinkingFragment extends FragmentBase
- implements View.OnClickListener {
-
- public static final String TAG = "CrossDeviceFragment";
-
- private FinishEmailLinkSignInListener mListener;
- private ProgressBar mProgressBar;
- private Button mContinueButton;
-
- public static EmailLinkCrossDeviceLinkingFragment newInstance() {
- EmailLinkCrossDeviceLinkingFragment fragment = new EmailLinkCrossDeviceLinkingFragment();
- return fragment;
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_email_link_cross_device_linking, container, false);
- }
-
- @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);
- mContinueButton.setOnClickListener(this);
-
- String link = getFlowParams().emailLink;
-
- EmailLinkParser parser = new EmailLinkParser(link);
-
- String providerId = parser.getProviderId();
- String providerName = ProviderUtils.providerIdToProviderName(providerId);
-
- TextView body = view.findViewById(R.id.cross_device_linking_body);
- String bodyText = getString(R.string.fui_email_link_cross_device_linking_text,
- providerName);
- SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(bodyText);
- TextHelper.boldAllOccurencesOfText(spannableStringBuilder, bodyText, providerName);
- body.setText(spannableStringBuilder);
-
- // Justifies the text
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- body.setJustificationMode(android.text.Layout.JUSTIFICATION_MODE_INTER_WORD);
- }
-
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(), getFlowParams(),
- footerText);
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- FragmentActivity activity = getActivity();
- if (!(activity instanceof FinishEmailLinkSignInListener)) {
- throw new IllegalStateException("Activity must implement EmailLinkPromptEmailListener");
- }
- mListener = (FinishEmailLinkSignInListener) activity;
- }
-
- @Override
- public void onClick(View view) {
- int id = view.getId();
- if (id == R.id.button_continue) {
- mListener.completeCrossDeviceEmailLinkFlow();
- }
- }
-
- @Override
- public void showProgress(int message) {
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-
-
- /**
- * Interface to be implemented by Activities hosting this Fragment.
- */
- interface FinishEmailLinkSignInListener {
- /**
- * Used to let the hosting activity know that we can finish the email link sign in flow
- */
- void completeCrossDeviceEmailLinkFlow();
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkErrorRecoveryActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkErrorRecoveryActivity.java
deleted file mode 100644
index 01e5a199b..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkErrorRecoveryActivity.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-
-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.viewmodel.RequestCodes;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.fragment.app.Fragment;
-
-/**
- * Handles the recovery flow for finishing the cross-device email link sign in flow. We either
- * need the user to input their email, or we need them to determine if they want to continue
- * the linking flow.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkErrorRecoveryActivity extends AppCompatBase
- implements EmailLinkPromptEmailFragment.EmailLinkPromptEmailListener,
- EmailLinkCrossDeviceLinkingFragment.FinishEmailLinkSignInListener {
-
- private static final String RECOVERY_TYPE_KEY = "com.firebase.ui.auth.ui.email.recoveryTypeKey";
-
- public static Intent createIntent(Context context, FlowParameters flowParams, int flow) {
- return createBaseIntent(context, EmailLinkErrorRecoveryActivity.class, flowParams)
- .putExtra(RECOVERY_TYPE_KEY, flow);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_activity_register_email);
-
- if (savedInstanceState != null) {
- return;
- }
-
- boolean linkingFlow = getIntent().getIntExtra(RECOVERY_TYPE_KEY, -1) ==
- RequestCodes.EMAIL_LINK_CROSS_DEVICE_LINKING_FLOW;
-
- Fragment fragment;
- if (linkingFlow) {
- fragment = EmailLinkCrossDeviceLinkingFragment.newInstance();
- } else {
- fragment = EmailLinkPromptEmailFragment.newInstance();
- }
- switchFragment(fragment, R.id.fragment_register_email, EmailLinkPromptEmailFragment.TAG);
- }
-
- @Override
- public void onEmailPromptSuccess(IdpResponse response) {
- finish(RESULT_OK, response.toIntent());
- }
-
- @Override
- public void completeCrossDeviceEmailLinkFlow() {
- EmailLinkPromptEmailFragment fragment
- = EmailLinkPromptEmailFragment.newInstance();
- switchFragment(fragment, R.id.fragment_register_email,
- EmailLinkCrossDeviceLinkingFragment.TAG, /*withTransition=*/true,
- /*addToBackStack=*/true);
- }
-
- @Override
- public void showProgress(@StringRes int message) {
- throw new UnsupportedOperationException("Fragments must handle progress updates.");
- }
-
- @Override
- public void hideProgress() {
- throw new UnsupportedOperationException("Fragments must handle progress updates.");
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkFragment.java
deleted file mode 100644
index 27ce2ddbe..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkFragment.java
+++ /dev/null
@@ -1,174 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ScrollView;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.ui.InvisibleFragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.TextHelper;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.email.EmailLinkSendEmailHandler;
-import com.google.firebase.auth.ActionCodeSettings;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.ViewModelProvider;
-
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkFragment extends InvisibleFragmentBase {
-
- public static final String TAG = "EmailLinkFragment";
- private static final String EMAIL_SENT = "emailSent";
- private EmailLinkSendEmailHandler mEmailLinkSendEmailHandler;
- private TroubleSigningInListener mListener;
- private ScrollView mTopLevelView;
-
- // Used to avoid sending a new email when popping off the fragment backstack
- private boolean mEmailSent;
-
- public static EmailLinkFragment newInstance(@NonNull final String email,
- @NonNull final ActionCodeSettings settings) {
- return newInstance(email, settings, /*idpResponseForLinking=*/null, false);
- }
-
- public static EmailLinkFragment newInstance(@NonNull final String email,
- @NonNull final ActionCodeSettings
- actionCodeSettings,
- @Nullable final IdpResponse idpResponseForLinking,
- final boolean forceSameDevice) {
- EmailLinkFragment fragment = new EmailLinkFragment();
- Bundle args = new Bundle();
- args.putString(ExtraConstants.EMAIL, email);
- args.putParcelable(ExtraConstants.ACTION_CODE_SETTINGS, actionCodeSettings);
- args.putParcelable(ExtraConstants.IDP_RESPONSE, idpResponseForLinking);
- args.putBoolean(ExtraConstants.FORCE_SAME_DEVICE, forceSameDevice);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- FragmentActivity activity = getActivity();
- if (!(activity instanceof TroubleSigningInListener)) {
- throw new IllegalStateException("Activity must implement TroubleSigningInListener");
- }
- mListener = (TroubleSigningInListener) activity;
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_email_link_sign_in_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- if (savedInstanceState != null) {
- mEmailSent = savedInstanceState.getBoolean(EMAIL_SENT);
- }
-
- mTopLevelView = view.findViewById(R.id.top_level_view);
- if (!mEmailSent) {
- // We need to hide the top level view until we know that the email link has been sent
- mTopLevelView.setVisibility(View.GONE);
- }
-
- String email = getArguments().getString(ExtraConstants.EMAIL);
- setBodyText(view, email);
- setOnClickListeners(view, email);
- setPrivacyFooter(view);
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- initHandler();
-
- String email = getArguments().getString(ExtraConstants.EMAIL);
- ActionCodeSettings actionCodeSettings
- = getArguments().getParcelable(ExtraConstants.ACTION_CODE_SETTINGS);
- IdpResponse idpResponseForLinking
- = getArguments().getParcelable(ExtraConstants.IDP_RESPONSE);
- boolean forceSameDevice
- = getArguments().getBoolean(ExtraConstants.FORCE_SAME_DEVICE);
-
- if (!mEmailSent) {
- mEmailLinkSendEmailHandler.sendSignInLinkToEmail(email, actionCodeSettings,
- idpResponseForLinking, forceSameDevice);
- }
- }
-
- private void initHandler() {
- mEmailLinkSendEmailHandler = new ViewModelProvider(this).get(EmailLinkSendEmailHandler
- .class);
- mEmailLinkSendEmailHandler.init(getFlowParams());
-
- mEmailLinkSendEmailHandler.getOperation().observe(getViewLifecycleOwner(), new ResourceObserver(this,
- R.string.fui_progress_dialog_sending) {
- @Override
- protected void onSuccess(@NonNull String email) {
- Log.w(TAG, "Email for email link sign in sent successfully.");
- doAfterTimeout(() -> mTopLevelView.setVisibility(View.VISIBLE));
- mEmailSent = true;
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- mListener.onSendEmailFailure(e);
- }
- });
- }
-
- private void setBodyText(View view, final String email) {
- TextView body = view.findViewById(R.id.sign_in_email_sent_text);
- String bodyText = getString(R.string.fui_email_link_email_sent, email);
-
- SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(bodyText);
- TextHelper.boldAllOccurencesOfText(spannableStringBuilder, bodyText, email);
- body.setText(spannableStringBuilder);
- }
-
- private void setOnClickListeners(View view, final String email) {
- view.findViewById(R.id.trouble_signing_in).setOnClickListener(v -> mListener.onTroubleSigningIn(email));
- }
-
- private void setPrivacyFooter(View view) {
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(), getFlowParams(),
- footerText);
- }
-
- @Override
- public void onSaveInstanceState(Bundle state) {
- super.onSaveInstanceState(state);
- state.putBoolean(EMAIL_SENT, mEmailSent);
- }
-
- interface TroubleSigningInListener {
- /**
- * User clicks on trouble signing in.
- */
- void onTroubleSigningIn(String email);
-
- /**
- * Failure occurs when trying to send the email.
- */
- void onSendEmailFailure(Exception e);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkPromptEmailFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkPromptEmailFragment.java
deleted file mode 100644
index 4e55594db..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/EmailLinkPromptEmailFragment.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.fieldvalidators.EmailFieldValidator;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.email.EmailLinkSignInHandler;
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.ViewModelProvider;
-
-/** Prompts the user to enter their email to finish the cross-device email link sign in flow. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkPromptEmailFragment extends FragmentBase implements
- View.OnClickListener {
-
- public static final String TAG = "EmailLinkPromptEmailFragment";
-
- private Button mNextButton;
- private Button mSignUpButton;
- private ProgressBar mProgressBar;
-
- private EditText mEmailEditText;
- private TextInputLayout mEmailLayout;
- private EmailFieldValidator mEmailFieldValidator;
-
- private EmailLinkSignInHandler mHandler;
- private EmailLinkPromptEmailListener mListener;
-
- public static EmailLinkPromptEmailFragment newInstance() {
- EmailLinkPromptEmailFragment fragment = new EmailLinkPromptEmailFragment();
- return fragment;
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_check_email_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mNextButton = view.findViewById(R.id.button_sign_in);
- mSignUpButton = view.findViewById(R.id.button_sign_up);
- mProgressBar = view.findViewById(R.id.top_progress_bar);
-
- mNextButton.setOnClickListener(this);
-
- // Email field and validator
- mEmailLayout = view.findViewById(R.id.email_layout);
- mEmailEditText = view.findViewById(R.id.email);
- mEmailFieldValidator = new EmailFieldValidator(mEmailLayout);
- mEmailLayout.setOnClickListener(this);
- mEmailEditText.setOnClickListener(this);
-
- // Set activity title
- getActivity().setTitle(R.string.fui_email_link_confirm_email_header);
-
- // Set Tos/Pp footer
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(), getFlowParams(),
- footerText);
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
-
- FragmentActivity activity = getActivity();
- if (!(activity instanceof EmailLinkPromptEmailListener)) {
- throw new IllegalStateException("Activity must implement EmailLinkPromptEmailListener");
- }
- mListener = (EmailLinkPromptEmailListener) activity;
-
- initHandler();
- }
-
- private void initHandler() {
- mHandler = new ViewModelProvider(this).get(EmailLinkSignInHandler.class);
- mHandler.init(getFlowParams());
- mHandler.getOperation().observe(getViewLifecycleOwner(), new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- mListener.onEmailPromptSuccess(response);
- }
-
- @Override
- protected void onFailure(@NonNull final Exception e) {
- // We've checked the oob code before starting this flow via #checkActionCode.
- // I don't see this failing in a non-recoverable way.
- mEmailLayout.setError(e.getMessage());
- }
- });
- }
-
- private void validateEmailAndFinishSignIn() {
- String email = mEmailEditText.getText().toString();
- if (mEmailFieldValidator.validate(email)) {
- mHandler.finishSignIn(email);
- }
- }
-
- @Override
- public void onClick(View view) {
- int id = view.getId();
- if (id == R.id.button_sign_in) {
- validateEmailAndFinishSignIn();
- } else if (id == R.id.email_layout || id == R.id.email) {
- mEmailLayout.setError(null);
- }
- }
-
- @Override
- public void showProgress(int message) {
- mNextButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mNextButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-
- /**
- * Interface to be implemented by Activities hosting this Fragment.
- */
- interface EmailLinkPromptEmailListener {
- /* Pass on the success to the hosting Activity so we can complete the sign in */
- void onEmailPromptSuccess(IdpResponse response);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java
deleted file mode 100644
index 5d1164b5f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RecoverPasswordActivity.java
+++ /dev/null
@@ -1,154 +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.ui.email;
-
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-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.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.ImeHelper;
-import com.firebase.ui.auth.util.ui.fieldvalidators.EmailFieldValidator;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.email.RecoverPasswordHandler;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.google.android.material.textfield.TextInputLayout;
-import com.google.firebase.auth.ActionCodeSettings;
-import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
-import com.google.firebase.auth.FirebaseAuthInvalidUserException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-/**
- * Activity to initiate the "forgot password" flow by asking for the user's email.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class RecoverPasswordActivity extends AppCompatBase implements View.OnClickListener,
- ImeHelper.DonePressedListener {
- private RecoverPasswordHandler mHandler;
-
- private ProgressBar mProgressBar;
- private Button mSubmitButton;
- private TextInputLayout mEmailInputLayout;
- private EditText mEmailEditText;
- private EmailFieldValidator mEmailFieldValidator;
-
- public static Intent createIntent(Context context, FlowParameters params, String email) {
- return createBaseIntent(context, RecoverPasswordActivity.class, params)
- .putExtra(ExtraConstants.EMAIL, email);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_forgot_password_layout);
-
- mHandler = new ViewModelProvider(this).get(RecoverPasswordHandler.class);
- mHandler.init(getFlowParams());
- mHandler.getOperation().observe(this, new ResourceObserver(
- this, R.string.fui_progress_dialog_sending) {
- @Override
- protected void onSuccess(@NonNull String email) {
- mEmailInputLayout.setError(null);
- showEmailSentDialog(email);
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthInvalidUserException
- || e instanceof FirebaseAuthInvalidCredentialsException) {
- // No FirebaseUser exists with this email address, show error.
- mEmailInputLayout.setError(getString(R.string.fui_error_email_does_not_exist));
- } else {
- // Unknown error
- mEmailInputLayout.setError(getString(R.string.fui_error_unknown));
- }
- }
- });
-
- mProgressBar = findViewById(R.id.top_progress_bar);
- mSubmitButton = findViewById(R.id.button_done);
- mEmailInputLayout = findViewById(R.id.email_layout);
- mEmailEditText = findViewById(R.id.email);
- mEmailFieldValidator = new EmailFieldValidator(mEmailInputLayout);
-
- String email = getIntent().getStringExtra(ExtraConstants.EMAIL);
- if (email != null) {
- mEmailEditText.setText(email);
- }
-
- ImeHelper.setImeOnDoneListener(mEmailEditText, this);
- mSubmitButton.setOnClickListener(this);
-
- TextView footerText = findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(this, getFlowParams(), footerText);
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.button_done) {
- onDonePressed();
- }
- }
-
- @Override
- public void onDonePressed() {
- if (mEmailFieldValidator.validate(mEmailEditText.getText())) {
- if (getFlowParams().passwordResetSettings != null) {
- resetPassword(mEmailEditText.getText().toString(), getFlowParams().passwordResetSettings);
- }
- else {
- resetPassword(mEmailEditText.getText().toString(), null);
- }
- }
- }
-
- private void resetPassword(String email, @Nullable ActionCodeSettings passwordResetSettings) {
- mHandler.startReset(email, passwordResetSettings);
- }
- private void showEmailSentDialog(String email) {
- new MaterialAlertDialogBuilder(this)
- .setTitle(R.string.fui_title_confirm_recover_password)
- .setMessage(getString(R.string.fui_confirm_recovery_body, email))
- .setOnDismissListener(dialog -> finish(RESULT_OK, new Intent()))
- .setPositiveButton(android.R.string.ok, null)
- .show();
- }
-
- @Override
- public void showProgress(int message) {
- mSubmitButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mSubmitButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
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
deleted file mode 100644
index d1d2d21f4..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/RegisterEmailFragment.java
+++ /dev/null
@@ -1,288 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.os.Build;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.FirebaseAuthAnonymousUpgradeException;
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.util.ui.ImeHelper;
-import com.firebase.ui.auth.util.ui.fieldvalidators.BaseValidator;
-import com.firebase.ui.auth.util.ui.fieldvalidators.EmailFieldValidator;
-import com.firebase.ui.auth.util.ui.fieldvalidators.NoOpValidator;
-import com.firebase.ui.auth.util.ui.fieldvalidators.PasswordFieldValidator;
-import com.firebase.ui.auth.util.ui.fieldvalidators.RequiredFieldValidator;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.email.EmailProviderResponseHandler;
-import com.google.android.material.textfield.TextInputLayout;
-import com.google.firebase.auth.EmailAuthProvider;
-import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
-import com.google.firebase.auth.FirebaseAuthWeakPasswordException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-import androidx.lifecycle.ViewModelProvider;
-
-/**
- * Fragment to display an email/name/password sign up form for new users.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class RegisterEmailFragment extends FragmentBase implements
- View.OnClickListener, View.OnFocusChangeListener, ImeHelper.DonePressedListener {
- public static final String TAG = "RegisterEmailFragment";
-
- private EmailProviderResponseHandler mHandler;
-
- private Button mNextButton;
- private ProgressBar mProgressBar;
-
- private EditText mEmailEditText;
- private EditText mNameEditText;
- private EditText mPasswordEditText;
- private TextInputLayout mEmailInput;
- private TextInputLayout mPasswordInput;
-
- private EmailFieldValidator mEmailFieldValidator;
- private PasswordFieldValidator mPasswordFieldValidator;
- private BaseValidator mNameValidator;
-
- private AnonymousUpgradeListener mListener;
- private User mUser;
-
- /**
- * Interface to be implemented by Activities hosting this Fragment.
- */
- interface AnonymousUpgradeListener {
-
- /**
- * Email belongs to an existing user - failed to merge anonymous user.
- */
- void onMergeFailure(IdpResponse response);
-
- }
-
- public static RegisterEmailFragment newInstance(User user) {
- RegisterEmailFragment fragment = new RegisterEmailFragment();
- Bundle args = new Bundle();
- args.putParcelable(ExtraConstants.USER, user);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (savedInstanceState == null) {
- mUser = User.getUser(getArguments());
- } else {
- mUser = User.getUser(savedInstanceState);
- }
-
- mHandler = new ViewModelProvider(this).get(EmailProviderResponseHandler.class);
- mHandler.init(getFlowParams());
- mHandler.getOperation().observe(this, new ResourceObserver(
- this, R.string.fui_progress_dialog_signing_up) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- startSaveCredentials(
- mHandler.getCurrentUser(),
- response,
- mPasswordEditText.getText().toString());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthWeakPasswordException) {
- mPasswordInput.setError(getResources().getQuantityString(
- R.plurals.fui_error_weak_password,
- R.integer.fui_min_password_length));
- } else if (e instanceof FirebaseAuthInvalidCredentialsException) {
- mEmailInput.setError(getString(R.string.fui_invalid_email_address));
- } else if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- mListener.onMergeFailure(response);
- } else {
- // General error message, this branch should not be invoked but
- // covers future API changes
- mEmailInput.setError(getString(R.string.fui_email_account_creation_error));
- }
- }
- });
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_register_email_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mNextButton = view.findViewById(R.id.button_create);
- mProgressBar = view.findViewById(R.id.top_progress_bar);
-
- mEmailEditText = view.findViewById(R.id.email);
- mNameEditText = view.findViewById(R.id.name);
- mPasswordEditText = view.findViewById(R.id.password);
- mEmailInput = view.findViewById(R.id.email_layout);
- mPasswordInput = view.findViewById(R.id.password_layout);
- TextInputLayout nameInput = view.findViewById(R.id.name_layout);
-
- // Get configuration
- AuthUI.IdpConfig emailConfig = ProviderUtils.getConfigFromIdpsOrThrow(
- getFlowParams().providers, EmailAuthProvider.PROVIDER_ID);
- boolean requireName = emailConfig.getParams()
- .getBoolean(ExtraConstants.REQUIRE_NAME, true);
- mPasswordFieldValidator = new PasswordFieldValidator(
- mPasswordInput,
- getResources().getInteger(R.integer.fui_min_password_length));
- mNameValidator = requireName
- ? new RequiredFieldValidator(nameInput,
- getResources().getString(R.string.fui_missing_first_and_last_name))
- : new NoOpValidator(nameInput);
- mEmailFieldValidator = new EmailFieldValidator(mEmailInput);
-
- ImeHelper.setImeOnDoneListener(mPasswordEditText, this);
-
- mEmailEditText.setOnFocusChangeListener(this);
- mNameEditText.setOnFocusChangeListener(this);
- mPasswordEditText.setOnFocusChangeListener(this);
- mNextButton.setOnClickListener(this);
-
- // Only show the name field if required
- nameInput.setVisibility(requireName ? View.VISIBLE : View.GONE);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && getFlowParams().enableCredentials) {
- mEmailEditText.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
- }
-
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(
- requireContext(), getFlowParams(), footerText);
-
- // WARNING: Nothing below this line will be executed on rotation
- if (savedInstanceState != null) {
- return;
- }
-
- // If email is passed in, fill in the field and move down to the name field.
- String email = mUser.getEmail();
- if (!TextUtils.isEmpty(email)) {
- mEmailEditText.setText(email);
- }
-
- // If name is passed in, fill in the field and move down to the password field.
- String name = mUser.getName();
- if (!TextUtils.isEmpty(name)) {
- mNameEditText.setText(name);
- }
-
- // See http://stackoverflow.com/questions/11082341/android-requestfocus-ineffective#comment51774752_11082523
- if (!requireName || !TextUtils.isEmpty(mNameEditText.getText())) {
- safeRequestFocus(mPasswordEditText);
- } else if (!TextUtils.isEmpty(mEmailEditText.getText())) {
- safeRequestFocus(mNameEditText);
- } else {
- safeRequestFocus(mEmailEditText);
- }
- }
-
- private void safeRequestFocus(final View v) {
- v.post(() -> v.requestFocus());
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- FragmentActivity activity = requireActivity();
- activity.setTitle(R.string.fui_title_register_email);
- if (!(activity instanceof AnonymousUpgradeListener)) {
- throw new IllegalStateException("Activity must implement CheckEmailListener");
- }
- mListener = (AnonymousUpgradeListener) activity;
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putParcelable(ExtraConstants.USER,
- new User.Builder(EmailAuthProvider.PROVIDER_ID, mEmailEditText.getText().toString())
- .setName(mNameEditText.getText().toString())
- .setPhotoUri(mUser.getPhotoUri())
- .build());
- }
-
- @Override
- public void onFocusChange(View view, boolean hasFocus) {
- if (hasFocus) return; // Only consider fields losing focus
-
- int id = view.getId();
- if (id == R.id.email) {
- mEmailFieldValidator.validate(mEmailEditText.getText());
- } else if (id == R.id.name) {
- mNameValidator.validate(mNameEditText.getText());
- } else if (id == R.id.password) {
- mPasswordFieldValidator.validate(mPasswordEditText.getText());
- }
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.button_create) {
- validateAndRegisterUser();
- }
- }
-
- @Override
- public void onDonePressed() {
- validateAndRegisterUser();
- }
-
- @Override
- public void showProgress(int message) {
- mNextButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mNextButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-
- private void validateAndRegisterUser() {
- String email = mEmailEditText.getText().toString();
- String password = mPasswordEditText.getText().toString();
- String name = mNameEditText.getText().toString();
-
- boolean emailValid = mEmailFieldValidator.validate(email);
- boolean passwordValid = mPasswordFieldValidator.validate(password);
- boolean nameValid = mNameValidator.validate(name);
- if (emailValid && passwordValid && nameValid) {
- mHandler.startSignIn(new IdpResponse.Builder(
- new User.Builder(EmailAuthProvider.PROVIDER_ID, email)
- .setName(name)
- .setPhotoUri(mUser.getPhotoUri())
- .build())
- .build(),
- password);
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/TroubleSigningInFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/TroubleSigningInFragment.java
deleted file mode 100644
index 4b76e5926..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/TroubleSigningInFragment.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.content.Context;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentActivity;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class TroubleSigningInFragment extends FragmentBase implements View.OnClickListener {
-
- public static final String TAG = "TroubleSigningInFragment";
-
- private ResendEmailListener mListener;
- private ProgressBar mProgressBar;
-
- private String mEmail;
-
- public static TroubleSigningInFragment newInstance(@NonNull final String email) {
- TroubleSigningInFragment fragment = new TroubleSigningInFragment();
- Bundle args = new Bundle();
- args.putString(ExtraConstants.EMAIL, email);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_email_link_trouble_signing_in_layout, container,
- false);
- }
-
- @Override
- public void onAttach(Context context) {
- super.onAttach(context);
- FragmentActivity activity = getActivity();
- if (!(activity instanceof ResendEmailListener)) {
- throw new IllegalStateException("Activity must implement ResendEmailListener");
- }
- mListener = (ResendEmailListener) activity;
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mProgressBar = view.findViewById(R.id.top_progress_bar);
- mEmail = getArguments().getString(ExtraConstants.EMAIL);
-
- setOnClickListeners(view);
- setPrivacyFooter(view);
- }
-
- private void setOnClickListeners(View view) {
- view.findViewById(R.id.button_resend_email).setOnClickListener(this);
- }
-
- private void setPrivacyFooter(View view) {
- TextView footerText = view.findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(), getFlowParams(),
- footerText);
- }
-
- @Override
- public void onClick(View view) {
- if (view.getId() == R.id.button_resend_email) {
- mListener.onClickResendEmail(mEmail);
- }
- }
-
- @Override
- public void showProgress(int message) {
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-
- interface ResendEmailListener {
- /**
- * User clicks on the resend email button.
- */
- void onClickResendEmail(String email);
- }
-}
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
deleted file mode 100644
index 2b62ff63c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackEmailLinkPrompt.java
+++ /dev/null
@@ -1,118 +0,0 @@
-package com.firebase.ui.auth.ui.email;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-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.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.TextHelper;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class WelcomeBackEmailLinkPrompt extends AppCompatBase implements View.OnClickListener {
-
-
- private IdpResponse mIdpResponseForLinking;
- private Button mSignInButton;
- private ProgressBar mProgressBar;
-
-
- public static Intent createIntent(
- Context context, FlowParameters flowParams, IdpResponse response) {
- return createBaseIntent(context, WelcomeBackEmailLinkPrompt.class, flowParams)
- .putExtra(ExtraConstants.IDP_RESPONSE, response);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_welcome_back_email_link_prompt_layout);
- mIdpResponseForLinking = IdpResponse.fromResultIntent(getIntent());
- initializeViewObjects();
- setBodyText();
- setOnClickListeners();
- setPrivacyFooter();
- }
-
- private void startEmailLinkFlow() {
- Intent intent = EmailActivity.createIntentForLinking(this, getFlowParams(),
- mIdpResponseForLinking);
- startActivityForResult(intent, RequestCodes.WELCOME_BACK_EMAIL_LINK_FLOW);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- finish(resultCode, data);
- }
-
- private void initializeViewObjects() {
- mSignInButton = findViewById(R.id.button_sign_in);
- mProgressBar = findViewById(R.id.top_progress_bar);
- }
-
- @SuppressWarnings("WrongConstant")
- private void setBodyText() {
- TextView body = findViewById(R.id.welcome_back_email_link_body);
- String bodyText = getString(R.string.fui_welcome_back_email_link_prompt_body,
- mIdpResponseForLinking.getEmail(),
- mIdpResponseForLinking
- .getProviderType());
-
- SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(bodyText);
- // bold email & provider text
- TextHelper.boldAllOccurencesOfText(spannableStringBuilder, bodyText,
- mIdpResponseForLinking.getEmail());
- TextHelper.boldAllOccurencesOfText(spannableStringBuilder, bodyText,
- mIdpResponseForLinking.getProviderType());
-
- body.setText(spannableStringBuilder);
- // Justifies the text
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- body.setJustificationMode(android.text.Layout.JUSTIFICATION_MODE_INTER_WORD);
- }
- }
-
- private void setOnClickListeners() {
- mSignInButton.setOnClickListener(this);
- }
-
- private void setPrivacyFooter() {
- TextView footerText = findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(this, getFlowParams(), footerText);
- }
-
- @Override
- public void onClick(View view) {
- final int id = view.getId();
- if (id == R.id.button_sign_in) {
- startEmailLinkFlow();
- }
- }
-
- @Override
- public void showProgress(int message) {
- mSignInButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mProgressBar.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
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
deleted file mode 100644
index 7528152bd..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/WelcomeBackPasswordPrompt.java
+++ /dev/null
@@ -1,207 +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.ui.email;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-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;
-import com.firebase.ui.auth.util.ui.TextHelper;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-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.FirebaseAuthException;
-import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.lifecycle.ViewModelProvider;
-
-/**
- * Activity to link a pre-existing email/password account to a new IDP sign-in by confirming the
- * password before initiating a link.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class WelcomeBackPasswordPrompt extends AppCompatBase
- implements View.OnClickListener, ImeHelper.DonePressedListener {
- private IdpResponse mIdpResponse;
- private WelcomeBackPasswordHandler mHandler;
-
- private Button mDoneButton;
- private ProgressBar mProgressBar;
- private TextInputLayout mPasswordLayout;
- private EditText mPasswordField;
-
- public static Intent createIntent(
- Context context, FlowParameters flowParams, IdpResponse response) {
- return createBaseIntent(context, WelcomeBackPasswordPrompt.class, flowParams)
- .putExtra(ExtraConstants.IDP_RESPONSE, response);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_welcome_back_password_prompt_layout);
-
- // Show keyboard
- getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
-
- mIdpResponse = IdpResponse.fromResultIntent(getIntent());
- String email = mIdpResponse.getEmail();
-
- mDoneButton = findViewById(R.id.button_done);
- mProgressBar = findViewById(R.id.top_progress_bar);
- mPasswordLayout = findViewById(R.id.password_layout);
- mPasswordField = findViewById(R.id.password);
-
- ImeHelper.setImeOnDoneListener(mPasswordField, this);
-
- // Create welcome back text with email bolded.
- String bodyText =
- getString(R.string.fui_welcome_back_password_prompt_body, email);
-
- SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(bodyText);
- TextHelper.boldAllOccurencesOfText(spannableStringBuilder, bodyText, email);
-
- TextView bodyTextView = findViewById(R.id.welcome_back_password_body);
- bodyTextView.setText(spannableStringBuilder);
-
- // Click listeners
- mDoneButton.setOnClickListener(this);
- findViewById(R.id.trouble_signing_in).setOnClickListener(this);
-
- // Initialize ViewModel with arguments
- mHandler = new ViewModelProvider(this).get(WelcomeBackPasswordHandler.class);
- mHandler.init(getFlowParams());
-
- // Observe the state of the main auth operation
- mHandler.getOperation().observe(this, new ResourceObserver(
- this, R.string.fui_progress_dialog_signing_in) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- startSaveCredentials(
- mHandler.getCurrentUser(), response, mHandler.getPendingPassword());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent());
- 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)));
- }
- });
-
- TextView footerText = findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(this, getFlowParams(), footerText);
- }
-
- @StringRes
- private int getErrorMessage(Exception exception) {
- if (exception instanceof FirebaseAuthInvalidCredentialsException) {
- return R.string.fui_error_invalid_password;
- }
-
- return R.string.fui_error_unknown;
- }
-
- private void onForgotPasswordClicked() {
- startActivity(RecoverPasswordActivity.createIntent(
- this,
- getFlowParams(),
- mIdpResponse.getEmail()));
- }
-
- @Override
- public void onDonePressed() {
- validateAndSignIn();
- }
-
- private void validateAndSignIn() {
- validateAndSignIn(mPasswordField.getText().toString());
- }
-
- private void validateAndSignIn(String password) {
- // Check for null or empty password
- if (TextUtils.isEmpty(password)) {
- mPasswordLayout.setError(getString(R.string.fui_error_invalid_password));
- return;
- } else {
- mPasswordLayout.setError(null);
- }
-
- AuthCredential authCredential = ProviderUtils.getAuthCredential(mIdpResponse);
- mHandler.startSignIn(mIdpResponse.getEmail(), password, mIdpResponse, authCredential);
- }
-
- @Override
- public void onClick(View view) {
- final int id = view.getId();
- if (id == R.id.button_done) {
- validateAndSignIn();
- } else if (id == R.id.trouble_signing_in) {
- onForgotPasswordClicked();
- }
- }
-
- @Override
- public void showProgress(int message) {
- mDoneButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mDoneButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/email/package-info.java b/auth/src/main/java/com/firebase/ui/auth/ui/email/package-info.java
deleted file mode 100644
index 0cdcb0450..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/email/package-info.java
+++ /dev/null
@@ -1,18 +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.
- */
-
-/**
- * Activities related to the email and password based authentication.
- */
-package com.firebase.ui.auth.ui.email;
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.kt b/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.kt
deleted file mode 100644
index 5cec78510..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/AuthMethodPickerActivity.kt
+++ /dev/null
@@ -1,492 +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.ui.idp
-
-import android.app.PendingIntent
-import android.content.Context
-import android.content.Intent
-import android.os.Bundle
-import android.text.TextUtils
-import android.util.Log
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.ProgressBar
-import android.widget.TextView
-import android.widget.Toast
-import androidx.activity.result.ActivityResult
-import androidx.activity.result.IntentSenderRequest
-import androidx.activity.result.contract.ActivityResultContracts
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.constraintlayout.widget.ConstraintSet
-import androidx.core.view.isVisible
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import com.firebase.ui.auth.AuthMethodPickerLayout
-import com.firebase.ui.auth.AuthUI
-import com.firebase.ui.auth.AuthUI.IdpConfig
-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.KickoffActivity
-import com.firebase.ui.auth.R
-import com.firebase.ui.auth.data.model.FlowParameters
-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.data.remote.AnonymousSignInHandler
-import com.firebase.ui.auth.data.remote.EmailSignInHandler
-import com.firebase.ui.auth.data.remote.FacebookSignInHandler
-import com.firebase.ui.auth.data.remote.GenericIdpSignInHandler
-import com.firebase.ui.auth.data.remote.GoogleSignInHandler
-import com.firebase.ui.auth.data.remote.PhoneSignInHandler
-import com.firebase.ui.auth.ui.AppCompatBase
-import com.firebase.ui.auth.util.ExtraConstants
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils
-import com.firebase.ui.auth.util.data.ProviderUtils
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase
-import com.firebase.ui.auth.viewmodel.ResourceObserver
-import com.firebase.ui.auth.viewmodel.idp.SocialProviderResponseHandler
-import com.google.android.gms.auth.api.identity.BeginSignInRequest
-import com.google.android.gms.auth.api.identity.Identity
-import com.google.android.gms.auth.api.identity.SignInCredential
-import com.google.android.gms.common.api.ApiException
-import com.google.android.material.snackbar.Snackbar
-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
-import kotlinx.coroutines.launch
-
-// Imports for the new Credential Manager types (adjust these to match your library)
-import androidx.credentials.Credential
-import androidx.credentials.CredentialManager
-import androidx.credentials.CustomCredential
-import androidx.credentials.GetCredentialRequest
-import androidx.credentials.GetPasswordOption
-import androidx.credentials.PasswordCredential
-import androidx.credentials.PublicKeyCredential
-import androidx.credentials.exceptions.GetCredentialException
-
-import com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER
-import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_BUTTON_ID
-import com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_PROVIDER_ID
-import com.firebase.ui.auth.util.GoogleApiUtils
-import com.google.android.libraries.identity.googleid.GetGoogleIdOption
-import com.google.android.libraries.identity.googleid.GoogleIdTokenCredential
-import com.google.android.libraries.identity.googleid.GoogleIdTokenParsingException
-import com.google.firebase.auth.GoogleAuthCredential
-
-@androidx.annotation.RestrictTo(androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP)
-class AuthMethodPickerActivity : AppCompatBase() {
-
- private lateinit var mHandler: SocialProviderResponseHandler
- private val mProviders: MutableList> = mutableListOf()
-
- private var mProgressBar: ProgressBar? = null
- private var mProviderHolder: ViewGroup? = null
-
- private var customLayout: AuthMethodPickerLayout? = null
-
- // For demonstration, assume that CredentialManager provides a create() method.
- private val credentialManager by lazy {
- // Replace with your actual CredentialManager instance creation.
- GoogleApiUtils.getCredentialManager(this)
- }
-
- companion object {
- private const val TAG = "AuthMethodPickerActivity"
-
- @JvmStatic
- fun createIntent(context: Context, flowParams: FlowParameters): Intent {
- return createBaseIntent(context, AuthMethodPickerActivity::class.java, flowParams)
- }
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
-
- val params = flowParams
- customLayout = params.authMethodPickerLayout
-
- mHandler = ViewModelProvider(this).get(SocialProviderResponseHandler::class.java)
- mHandler.init(params)
-
- if (customLayout != null) {
- setContentView(customLayout!!.mainLayout)
- populateIdpListCustomLayout(params.providers)
- } else {
- setContentView(R.layout.fui_auth_method_picker_layout)
- mProgressBar = findViewById(R.id.top_progress_bar)
- mProviderHolder = findViewById(R.id.btn_holder)
- populateIdpList(params.providers)
-
- val logoId = params.logoId
- if (logoId == AuthUI.NO_LOGO) {
- findViewById(R.id.logo).visibility = View.GONE
-
- val layout = findViewById(R.id.root)
- val constraints = ConstraintSet()
- constraints.clone(layout)
- constraints.setHorizontalBias(R.id.container, 0.5f)
- constraints.setVerticalBias(R.id.container, 0.5f)
- constraints.applyTo(layout)
- } else {
- val logo = findViewById(R.id.logo)
- logo.setImageResource(logoId)
- }
- }
-
- val tosAndPpConfigured = flowParams.isPrivacyPolicyUrlProvided() &&
- flowParams.isTermsOfServiceUrlProvided()
-
- val termsTextId = if (customLayout == null) {
- R.id.main_tos_and_pp
- } else {
- customLayout!!.tosPpView
- }
-
- if (termsTextId >= 0) {
- val termsText = findViewById(termsTextId)
- if (!tosAndPpConfigured) {
- termsText.visibility = View.GONE
- } else {
- PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicyText(this, flowParams, termsText)
- }
- }
-
- // Observe the social provider response handler.
- mHandler.operation.observe(this, object : ResourceObserver(this, R.string.fui_progress_dialog_signing_in) {
- override fun onSuccess(response: IdpResponse) {
- startSaveCredentials(mHandler.currentUser, response, null)
- }
-
- override fun onFailure(e: Exception) {
- when (e) {
- is UserCancellationException -> {
- // User pressed back – no error.
- }
- is FirebaseAuthAnonymousUpgradeException -> {
- finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, e.response.toIntent())
- }
- is FirebaseUiException -> {
- finish(RESULT_CANCELED, IdpResponse.from(e).toIntent())
- }
- else -> {
- val text = getString(R.string.fui_error_unknown)
- Toast.makeText(this@AuthMethodPickerActivity, text, Toast.LENGTH_SHORT).show()
- }
- }
- }
- })
-
- // Attempt sign in using the new Credential Manager API.
- attemptCredentialSignIn()
- }
-
- /**
- * Attempts to sign in automatically using the Credential Manager API.
- */
- private fun attemptCredentialSignIn() {
- val args = flowParams
- val supportPasswords = ProviderUtils.getConfigFromIdps(args.providers, EmailAuthProvider.PROVIDER_ID) != null
- val accountTypes = getCredentialAccountTypes()
- val willRequestCredentials = supportPasswords || accountTypes.isNotEmpty()
-
- if (args.enableCredentials && willRequestCredentials) {
- // Build the new Credential Manager request.
- val getPasswordOption = GetPasswordOption()
- val googleIdOption = GetGoogleIdOption.Builder()
- .setFilterByAuthorizedAccounts(true)
- .setServerClientId(getString(R.string.default_web_client_id))
- .build()
- val request = GetCredentialRequest(listOf(getPasswordOption, googleIdOption))
-
- lifecycleScope.launch {
- try {
- val result = credentialManager.getCredential(
- context = this@AuthMethodPickerActivity,
- request = request
- )
- // Handle the returned credential.
- handleCredentialManagerResult(result.credential)
- } catch (e: GetCredentialException) {
- handleCredentialManagerFailure(e)
- // Fallback: show the auth method picker.
- showAuthMethodPicker()
- }
- }
- } else {
- showAuthMethodPicker()
- }
- }
-
- /**
- * Handles the credential returned from the Credential Manager.
- */
- private fun handleCredentialManagerResult(credential: Credential) {
- when (credential) {
- is PasswordCredential -> {
- val username = credential.id
- val password = credential.password
- val response = IdpResponse.Builder(
- User.Builder(EmailAuthProvider.PROVIDER_ID, username).build()
- ).build()
- KickoffActivity.mKickstarter.setResult(Resource.forLoading())
- auth.signInWithEmailAndPassword(username, password)
- .addOnSuccessListener { authResult ->
- KickoffActivity.mKickstarter.handleSuccess(response, authResult)
- finish()
- }
- .addOnFailureListener { e ->
- if (e is FirebaseAuthInvalidUserException ||
- e is FirebaseAuthInvalidCredentialsException) {
- // Sign out via the new API.
- Identity.getSignInClient(application).signOut()
- }
- }
- }
- is CustomCredential -> {
- if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
- try {
- val googleIdTokenCredential = GoogleIdTokenCredential
- .createFrom(credential.data)
- auth.signInWithCredential(GoogleAuthProvider.getCredential(googleIdTokenCredential.idToken, null))
- .addOnSuccessListener { authResult ->
- val response = IdpResponse.Builder(
- User.Builder(GoogleAuthProvider.PROVIDER_ID, googleIdTokenCredential.data.getString("email")).build(),
- ).setToken(googleIdTokenCredential.idToken).build()
- KickoffActivity.mKickstarter.handleSuccess(response, authResult)
- finish()
- }
- .addOnFailureListener { e ->
- Log.e(TAG, "Failed to sign in with Google ID token", e)
- }
- } catch (e: GoogleIdTokenParsingException) {
- Log.e(TAG, "Received an invalid google id token response", e)
- }
- } else {
- // Catch any unrecognized custom credential type here.
- Log.e(TAG, "Unexpected type of credential")
- }
- }
- else -> {
- Log.e(TAG, "Unexpected type of credential")
- }
- }
- }
-
- /**
- * Example helper to extract a Google ID token from a PublicKeyCredential.
- * In your implementation you may need to parse the JSON response accordingly.
- */
- private fun extractGoogleIdToken(credential: PublicKeyCredential): String? {
- // TODO: Extract and return the Google ID token from credential.authenticationResponseJson.
- // For demonstration, we assume that authenticationResponseJson is the token.
- return credential.authenticationResponseJson
- }
-
- private fun handleCredentialManagerFailure(e: GetCredentialException) {
- Log.e(TAG, "Credential Manager sign in failed", e)
- }
-
- /**
- * Returns the account types to pass to the credential manager.
- */
- private fun getCredentialAccountTypes(): List {
- val accounts = mutableListOf()
- for (idpConfig in flowParams.providers) {
- if (idpConfig.providerId == GoogleAuthProvider.PROVIDER_ID) {
- accounts.add(ProviderUtils.providerIdToAccountType(idpConfig.providerId))
- }
- }
- return accounts
- }
-
- /**
- * Fallback – show the auth method picker UI.
- */
- private fun showAuthMethodPicker() {
- hideProgress()
- }
-
- private fun populateIdpList(providerConfigs: List) {
- // Clear any previous providers.
- mProviders.clear()
- for (idpConfig in providerConfigs) {
- val buttonLayout = when (idpConfig.providerId) {
- GoogleAuthProvider.PROVIDER_ID -> R.layout.fui_idp_button_google
- FacebookAuthProvider.PROVIDER_ID -> R.layout.fui_idp_button_facebook
- EMAIL_LINK_PROVIDER, EmailAuthProvider.PROVIDER_ID -> R.layout.fui_provider_button_email
- PhoneAuthProvider.PROVIDER_ID -> R.layout.fui_provider_button_phone
- AuthUI.ANONYMOUS_PROVIDER -> R.layout.fui_provider_button_anonymous
- else -> {
- if (!TextUtils.isEmpty(idpConfig.params.getString(GENERIC_OAUTH_PROVIDER_ID))) {
- idpConfig.params.getInt(GENERIC_OAUTH_BUTTON_ID)
- } else {
- throw IllegalStateException("Unknown provider: ${idpConfig.providerId}")
- }
- }
- }
- val loginButton = layoutInflater.inflate(buttonLayout, mProviderHolder, false)
- handleSignInOperation(idpConfig, loginButton)
- mProviderHolder?.addView(loginButton)
- }
- }
-
- private fun populateIdpListCustomLayout(providerConfigs: List) {
- val providerButtonIds = customLayout?.providersButton ?: return
- for (idpConfig in providerConfigs) {
- val providerId = providerOrEmailLinkProvider(idpConfig.providerId)
- val buttonResId = providerButtonIds[providerId]
- ?: throw IllegalStateException("No button found for auth provider: ${idpConfig.providerId}")
- val loginButton = findViewById(buttonResId)
- handleSignInOperation(idpConfig, loginButton)
- }
- // Hide custom layout buttons that don't have an associated provider.
- for ((providerBtnId, resId) in providerButtonIds) {
- if (providerBtnId == null) continue
- var hasProvider = false
- for (idpConfig in providerConfigs) {
- if (providerOrEmailLinkProvider(idpConfig.providerId) == providerBtnId) {
- hasProvider = true
- break
- }
- }
- if (!hasProvider) {
- findViewById(resId)?.visibility = View.GONE
- }
- }
- }
-
- private fun providerOrEmailLinkProvider(providerId: String): String {
- return if (providerId == EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD) {
- EmailAuthProvider.PROVIDER_ID
- } else providerId
- }
-
- private fun handleSignInOperation(idpConfig: IdpConfig, view: View) {
- val providerId = idpConfig.providerId
- val authUI = getAuthUI()
- val viewModelProvider = ViewModelProvider(this)
- val provider: ProviderSignInBase<*> = when (providerId) {
- EMAIL_LINK_PROVIDER, EmailAuthProvider.PROVIDER_ID ->
- viewModelProvider.get(EmailSignInHandler::class.java).initWith(null)
- PhoneAuthProvider.PROVIDER_ID ->
- viewModelProvider.get(PhoneSignInHandler::class.java).initWith(idpConfig)
- AuthUI.ANONYMOUS_PROVIDER ->
- viewModelProvider.get(AnonymousSignInHandler::class.java).initWith(flowParams)
- GoogleAuthProvider.PROVIDER_ID ->
- if (authUI.isUseEmulator) {
- viewModelProvider.get(GenericIdpSignInHandler::class.java)
- .initWith(GenericIdpSignInHandler.getGenericGoogleConfig())
- } else {
- viewModelProvider.get(GoogleSignInHandler::class.java)
- .initWith(GoogleSignInHandler.Params(idpConfig))
- }
- FacebookAuthProvider.PROVIDER_ID ->
- if (authUI.isUseEmulator) {
- viewModelProvider.get(GenericIdpSignInHandler::class.java)
- .initWith(GenericIdpSignInHandler.getGenericFacebookConfig())
- } else {
- viewModelProvider.get(FacebookSignInHandler::class.java).initWith(idpConfig)
- }
- else -> {
- if (!TextUtils.isEmpty(idpConfig.params.getString(GENERIC_OAUTH_PROVIDER_ID))) {
- viewModelProvider.get(GenericIdpSignInHandler::class.java).initWith(idpConfig)
- } else {
- throw IllegalStateException("Unknown provider: $providerId")
- }
- }
- }
-
- mProviders.add(provider)
-
- provider.operation.observe(this, object : ResourceObserver(this) {
- override fun onSuccess(response: IdpResponse) {
- handleResponse(response)
- }
-
- override fun onFailure(e: Exception) {
- if (e is FirebaseAuthAnonymousUpgradeException) {
- finish(
- RESULT_CANCELED,
- Intent().putExtra(ExtraConstants.IDP_RESPONSE, IdpResponse.from(e))
- )
- return
- }
- handleResponse(IdpResponse.from(e))
- }
-
- private fun handleResponse(response: IdpResponse) {
- // For social providers (unless using an emulator) use the social response handler.
- val isSocialResponse = AuthUI.SOCIAL_PROVIDERS.contains(providerId) && !authUI.isUseEmulator
- if (!response.isSuccessful) {
- mHandler.startSignIn(response)
- } else if (isSocialResponse) {
- mHandler.startSignIn(response)
- } else {
- finish(if (response.isSuccessful) RESULT_OK else RESULT_CANCELED, response.toIntent())
- }
- }
- })
-
- view.setOnClickListener {
- if (isOffline()) {
- Snackbar.make(findViewById(android.R.id.content), getString(R.string.fui_no_internet), Snackbar.LENGTH_SHORT)
- .show()
- return@setOnClickListener
- }
- provider.startSignIn(getAuth(), this@AuthMethodPickerActivity, idpConfig.providerId)
- }
- }
-
- override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
- super.onActivityResult(requestCode, resultCode, data)
- mHandler.onActivityResult(requestCode, resultCode, data)
- for (provider in mProviders) {
- provider.onActivityResult(requestCode, resultCode, data)
- }
- }
-
- override fun showProgress(message: Int) {
- if (customLayout == null) {
- mProgressBar?.visibility = View.VISIBLE
- mProviderHolder?.let { holder ->
- for (i in 0 until holder.childCount) {
- val child = holder.getChildAt(i)
- child.isEnabled = false
- child.alpha = 0.75f
- }
- }
- }
- }
-
- override fun hideProgress() {
- if (customLayout == null) {
- mProgressBar?.visibility = View.INVISIBLE
- mProviderHolder?.let { holder ->
- for (i in 0 until holder.childCount) {
- val child = holder.getChildAt(i)
- child.isEnabled = true
- child.alpha = 1.0f
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java
deleted file mode 100644
index 17221f7ed..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/SingleSignInActivity.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.firebase.ui.auth.ui.idp;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.TextUtils;
-
-import com.firebase.ui.auth.AuthUI;
-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.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.data.remote.FacebookSignInHandler;
-import com.firebase.ui.auth.data.remote.GenericIdpSignInHandler;
-import com.firebase.ui.auth.data.remote.GoogleSignInHandler;
-import com.firebase.ui.auth.ui.InvisibleActivityBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.idp.SocialProviderResponseHandler;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-import static com.firebase.ui.auth.util.ExtraConstants.GENERIC_OAUTH_PROVIDER_ID;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class SingleSignInActivity extends InvisibleActivityBase {
- private SocialProviderResponseHandler mHandler;
- private ProviderSignInBase> mProvider;
-
- public static Intent createIntent(Context context, FlowParameters flowParams, User user) {
- return createBaseIntent(context, SingleSignInActivity.class, flowParams)
- .putExtra(ExtraConstants.USER, user);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- User user = User.getUser(getIntent());
- final String provider = user.getProviderId();
-
- AuthUI.IdpConfig providerConfig =
- ProviderUtils.getConfigFromIdps(getFlowParams().providers, provider);
- if (providerConfig == null) {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
- ErrorCodes.DEVELOPER_ERROR,
- "Provider not enabled: " + provider)));
- return;
- }
-
- ViewModelProvider supplier = new ViewModelProvider(this);
-
- mHandler = supplier.get(SocialProviderResponseHandler.class);
- mHandler.init(getFlowParams());
-
- boolean useEmulator = getAuthUI().isUseEmulator();
-
- switch (provider) {
- case GoogleAuthProvider.PROVIDER_ID:
- if (useEmulator) {
- mProvider = supplier.get(GenericIdpSignInHandler.class)
- .initWith(GenericIdpSignInHandler.getGenericGoogleConfig());
- } else {
- mProvider = supplier.get(GoogleSignInHandler.class).initWith(
- new GoogleSignInHandler.Params(providerConfig, user.getEmail()));
- }
- break;
- case FacebookAuthProvider.PROVIDER_ID:
- if (useEmulator) {
- mProvider = supplier.get(GenericIdpSignInHandler.class)
- .initWith(GenericIdpSignInHandler.getGenericFacebookConfig());
- } else {
- mProvider = supplier.get(FacebookSignInHandler.class).initWith(providerConfig);
- }
- break;
- default:
- if (!TextUtils.isEmpty(
- providerConfig.getParams().getString(GENERIC_OAUTH_PROVIDER_ID))) {
- mProvider = supplier.get(GenericIdpSignInHandler.class).initWith(providerConfig);
- break;
- }
- throw new IllegalStateException("Invalid provider id: " + provider);
- }
-
- mProvider.getOperation().observe(this, new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- boolean useSocialHandler = AuthUI.SOCIAL_PROVIDERS.contains(provider)
- && !getAuthUI().isUseEmulator();
-
- if (useSocialHandler || !response.isSuccessful()) {
- mHandler.startSignIn(response);
- return;
- }
- finish(response.isSuccessful() ? RESULT_OK : RESULT_CANCELED,
- response.toIntent());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- finish(RESULT_CANCELED, new Intent().putExtra(ExtraConstants.IDP_RESPONSE,
- IdpResponse.from(e)));
- return;
- }
- mHandler.startSignIn(IdpResponse.from(e));
- }
- });
-
- mHandler.getOperation().observe(this, new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- startSaveCredentials(mHandler.getCurrentUser(), response, null);
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse res = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(RESULT_CANCELED, new Intent().putExtra(ExtraConstants.IDP_RESPONSE, res));
- } else {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
- });
-
- if (mHandler.getOperation().getValue() == null) {
- mProvider.startSignIn(getAuth(), this, provider);
- }
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- mHandler.onActivityResult(requestCode, resultCode, data);
- mProvider.onActivityResult(requestCode, resultCode, data);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java
deleted file mode 100644
index 0f3e2319c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/WelcomeBackIdpPrompt.java
+++ /dev/null
@@ -1,220 +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.ui.idp;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.AuthUI;
-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.data.model.User;
-import com.firebase.ui.auth.data.remote.FacebookSignInHandler;
-import com.firebase.ui.auth.data.remote.GenericIdpAnonymousUpgradeLinkingHandler;
-import com.firebase.ui.auth.data.remote.GenericIdpSignInHandler;
-import com.firebase.ui.auth.data.remote.GoogleSignInHandler;
-import com.firebase.ui.auth.ui.AppCompatBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.data.ProviderUtils;
-import com.firebase.ui.auth.viewmodel.ProviderSignInBase;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.firebase.ui.auth.viewmodel.idp.LinkingSocialProviderResponseHandler;
-import com.google.android.gms.auth.api.Auth;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.GoogleAuthProvider;
-
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class WelcomeBackIdpPrompt extends AppCompatBase {
- private ProviderSignInBase> mProvider;
-
- private Button mDoneButton;
- private ProgressBar mProgressBar;
- private TextView mPromptText;
-
- public static Intent createIntent(
- Context context, FlowParameters flowParams, User existingUser) {
- return createIntent(context, flowParams, existingUser, null);
- }
-
- public static Intent createIntent(
- Context context,
- FlowParameters flowParams,
- User existingUser,
- @Nullable IdpResponse requestedUserResponse) {
- return createBaseIntent(context, WelcomeBackIdpPrompt.class, flowParams)
- .putExtra(ExtraConstants.IDP_RESPONSE, requestedUserResponse)
- .putExtra(ExtraConstants.USER, existingUser);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_welcome_back_idp_prompt_layout);
-
- mDoneButton = findViewById(R.id.welcome_back_idp_button);
- mProgressBar = findViewById(R.id.top_progress_bar);
- mPromptText = findViewById(R.id.welcome_back_idp_prompt);
-
- User existingUser = User.getUser(getIntent());
- IdpResponse requestedUserResponse = IdpResponse.fromResultIntent(getIntent());
- ViewModelProvider supplier = new ViewModelProvider(this);
-
- final LinkingSocialProviderResponseHandler handler =
- supplier.get(LinkingSocialProviderResponseHandler.class);
- handler.init(getFlowParams());
- if (requestedUserResponse != null) {
- handler.setRequestedSignInCredentialForEmail(
- ProviderUtils.getAuthCredential(requestedUserResponse),
- existingUser.getEmail());
- }
-
- final String providerId = existingUser.getProviderId();
- AuthUI.IdpConfig config =
- ProviderUtils.getConfigFromIdps(getFlowParams().providers, providerId);
- if (config == null) {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(new FirebaseUiException(
- ErrorCodes.DEVELOPER_ERROR,
- "Firebase login unsuccessful."
- + " Account linking failed due to provider not enabled by application: "
- + providerId)));
- return;
- }
-
-
- String providerName;
-
- String genericOAuthProviderId = config.getParams()
- .getString(ExtraConstants.GENERIC_OAUTH_PROVIDER_ID);
-
- boolean useEmulator = getAuthUI().isUseEmulator();
-
- switch (providerId) {
- case GoogleAuthProvider.PROVIDER_ID:
- if (useEmulator) {
- mProvider = supplier.get(GenericIdpAnonymousUpgradeLinkingHandler.class)
- .initWith(GenericIdpSignInHandler.getGenericGoogleConfig());
- } else {
- mProvider = supplier.get(GoogleSignInHandler.class).initWith(
- new GoogleSignInHandler.Params(config, existingUser.getEmail()));
- }
- providerName = getString(R.string.fui_idp_name_google);
- break;
- case FacebookAuthProvider.PROVIDER_ID:
- if (useEmulator) {
- mProvider = supplier.get(GenericIdpAnonymousUpgradeLinkingHandler.class)
- .initWith(GenericIdpSignInHandler.getGenericFacebookConfig());
- } else {
- mProvider = supplier.get(FacebookSignInHandler.class).initWith(config);
- }
- providerName = getString(R.string.fui_idp_name_facebook);
- break;
- default:
- if (TextUtils.equals(providerId, genericOAuthProviderId)) {
- mProvider = supplier.get(GenericIdpAnonymousUpgradeLinkingHandler.class)
- .initWith(config);
- providerName = config.getParams()
- .getString(ExtraConstants.GENERIC_OAUTH_PROVIDER_NAME);
- } else {
- throw new IllegalStateException("Invalid provider id: " + providerId);
- }
- }
-
- mProvider.getOperation().observe(this, new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- boolean isGenericIdp = getAuthUI().isUseEmulator()
- || !AuthUI.SOCIAL_PROVIDERS.contains(response.getProviderType());
-
- if (isGenericIdp
- && !response.hasCredentialForLinking()
- && !handler.hasCredentialForLinking()) {
- // Generic Idp does not return a credential - if this is not a linking flow,
- // the user is already signed in and we are done.
- finish(RESULT_OK, response.toIntent());
- return;
- }
- handler.startSignIn(response);
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- handler.startSignIn(IdpResponse.from(e));
- }
- });
-
- mPromptText.setText(getString(
- R.string.fui_welcome_back_idp_prompt,
- existingUser.getEmail(),
- providerName));
-
- mDoneButton.setOnClickListener(view -> mProvider.startSignIn(getAuth(), WelcomeBackIdpPrompt.this, providerId));
-
- handler.getOperation().observe(this, new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- finish(RESULT_OK, response.toIntent());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent());
- } else {
- finish(RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
- });
-
- TextView footerText = findViewById(R.id.email_footer_tos_and_pp_text);
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(this, getFlowParams(), footerText);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- mProvider.onActivityResult(requestCode, resultCode, data);
- }
-
- @Override
- public void showProgress(int message) {
- mDoneButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mDoneButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/idp/package-info.java b/auth/src/main/java/com/firebase/ui/auth/ui/idp/package-info.java
deleted file mode 100644
index 31d0137e0..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/idp/package-info.java
+++ /dev/null
@@ -1,18 +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.
- */
-
-/**
- * Activites related to identity provider authentication.
- */
-package com.firebase.ui.auth.ui.idp;
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/method_picker/AnnotatedStringResource.kt b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AnnotatedStringResource.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/method_picker/AnnotatedStringResource.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AnnotatedStringResource.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/method_picker/AuthMethodPicker.kt b/auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/method_picker/AuthMethodPicker.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/method_picker/AuthMethodPicker.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/package-info.java b/auth/src/main/java/com/firebase/ui/auth/ui/package-info.java
deleted file mode 100644
index 4b7f7584f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/package-info.java
+++ /dev/null
@@ -1,18 +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.
- */
-
-/**
- * Activities which implement the AuthUI authentication flow.
- */
-package com.firebase.ui.auth.ui;
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneHandler.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneHandler.java
deleted file mode 100644
index c10e5067a..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneHandler.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.firebase.ui.auth.ui.phone;
-
-import android.app.Activity;
-import android.app.Application;
-import android.content.Intent;
-import android.content.IntentSender;
-import android.util.Log;
-
-import com.firebase.ui.auth.data.model.PendingIntentRequiredException;
-import com.firebase.ui.auth.data.model.PhoneNumber;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.util.data.PhoneNumberUtils;
-import com.firebase.ui.auth.viewmodel.AuthViewModelBase;
-import com.firebase.ui.auth.viewmodel.RequestCodes;
-import com.google.android.gms.auth.api.identity.GetPhoneNumberHintIntentRequest;
-import com.google.android.gms.auth.api.identity.Identity;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CheckPhoneHandler extends AuthViewModelBase {
-
- private static final String TAG = "CheckPhoneHandler";
-
- public CheckPhoneHandler(Application application) {
- super(application);
- }
-
- /**
- * Initiates the Phone Number Hint flow using the new API.
- *
- *
This method creates a GetPhoneNumberHintIntentRequest and calls
- * Identity.getSignInClient(activity).getPhoneNumberHintIntent(request) to retrieve an
- * IntentSender. The IntentSender is then wrapped in a PendingIntentRequiredException so that
- * the caller can launch the hint flow.
- *
- *
Note: Update your PendingIntentRequiredException to accept an IntentSender
- * rather than a PendingIntent.
- *
- * @param activity The activity used to retrieve the Phone Number Hint IntentSender.
- */
- public void fetchCredential(final Activity activity) {
- GetPhoneNumberHintIntentRequest request = GetPhoneNumberHintIntentRequest.builder().build();
- Identity.getSignInClient(activity)
- .getPhoneNumberHintIntent(request)
- .addOnSuccessListener(result -> {
- try {
- // The new API returns an IntentSender.
- IntentSender intentSender = result.getIntentSender();
- // Update your exception to accept an IntentSender.
- setResult(Resource.forFailure(new PendingIntentRequiredException(intentSender, RequestCodes.CRED_HINT)));
- } catch (Exception e) {
- Log.e(TAG, "Launching the IntentSender failed", e);
- setResult(Resource.forFailure(e));
- }
- })
- .addOnFailureListener(e -> {
- Log.e(TAG, "Phone Number Hint failed", e);
- setResult(Resource.forFailure(e));
- });
- }
-
- /**
- * Handles the result from the Phone Number Hint flow.
- *
- *
Call this method from your Activity's onActivityResult. It extracts the phone number from the
- * returned Intent and formats it.
- *
- * @param activity The activity used to process the returned Intent.
- * @param requestCode The request code (should match RequestCodes.CRED_HINT).
- * @param resultCode The result code from the hint flow.
- * @param data The Intent data returned from the hint flow.
- */
- public void onActivityResult(Activity activity, int requestCode, int resultCode, @Nullable Intent data) {
- if (requestCode != RequestCodes.CRED_HINT || resultCode != Activity.RESULT_OK) {
- return;
- }
- try {
- String phoneNumber = Identity.getSignInClient(activity).getPhoneNumberFromIntent(data);
- String formattedPhone = PhoneNumberUtils.formatUsingCurrentCountry(phoneNumber, getApplication());
- if (formattedPhone != null) {
- setResult(Resource.forSuccess(PhoneNumberUtils.getPhoneNumber(formattedPhone)));
- } else {
- setResult(Resource.forFailure(new Exception("Failed to format phone number")));
- }
- } catch (Exception e) {
- Log.e(TAG, "Phone Number Hint failed", e);
- setResult(Resource.forFailure(e));
- }
- }
-}
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
deleted file mode 100644
index c011f1a3e..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CheckPhoneNumberFragment.java
+++ /dev/null
@@ -1,240 +0,0 @@
-package com.firebase.ui.auth.ui.phone;
-
-import android.content.Intent;
-import android.os.Build;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.PhoneNumber;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PhoneNumberUtils;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.ImeHelper;
-import com.firebase.ui.auth.viewmodel.ResourceObserver;
-import com.google.android.material.textfield.TextInputLayout;
-
-import java.util.Locale;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.ViewModelProvider;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class CheckPhoneNumberFragment extends FragmentBase implements View.OnClickListener {
- public static final String TAG = "VerifyPhoneFragment";
-
- private PhoneNumberVerificationHandler mVerificationHandler;
- private CheckPhoneHandler mCheckPhoneHandler;
- private boolean mCalled;
-
- private ProgressBar mProgressBar;
- private Button mSubmitButton;
- private CountryListSpinner mCountryListSpinner;
- private View mCountryListAnchor;
- private TextInputLayout mPhoneInputLayout;
- private EditText mPhoneEditText;
- private TextView mSmsTermsText;
- private TextView mFooterText;
-
- public static CheckPhoneNumberFragment newInstance(Bundle params) {
- CheckPhoneNumberFragment fragment = new CheckPhoneNumberFragment();
- Bundle args = new Bundle();
- args.putBundle(ExtraConstants.PARAMS, params);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mVerificationHandler = new ViewModelProvider(requireActivity())
- .get(PhoneNumberVerificationHandler.class);
- mCheckPhoneHandler = new ViewModelProvider(this)
- .get(CheckPhoneHandler.class);
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_phone_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mProgressBar = view.findViewById(R.id.top_progress_bar);
- mSubmitButton = view.findViewById(R.id.send_code);
- mCountryListSpinner = view.findViewById(R.id.country_list);
- mCountryListAnchor = view.findViewById(R.id.country_list_popup_anchor);
- mPhoneInputLayout = view.findViewById(R.id.phone_layout);
- mPhoneEditText = view.findViewById(R.id.phone_number);
- mSmsTermsText = view.findViewById(R.id.send_sms_tos);
- mFooterText = view.findViewById(R.id.email_footer_tos_and_pp_text);
-
- mSmsTermsText.setText(getString(R.string.fui_sms_terms_of_service,
- getString(R.string.fui_verify_phone_number)));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- mPhoneEditText.setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_NO);
- }
- requireActivity().setTitle(getString(R.string.fui_verify_phone_number_title));
-
- ImeHelper.setImeOnDoneListener(mPhoneEditText, this::onNext);
- mSubmitButton.setOnClickListener(this);
-
- setupPrivacyDisclosures();
- setupCountrySpinner();
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- mCheckPhoneHandler.getOperation().observe(getViewLifecycleOwner(), new ResourceObserver(this) {
- @Override
- protected void onSuccess(@NonNull PhoneNumber number) {
- start(number);
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- // Let the user enter their data if hint retrieval fails
- }
- });
-
- if (savedInstanceState != null || mCalled) {
- return;
- }
- // Fragment back stacks can cause state retention so we rely on an instance field.
- mCalled = true;
-
- // Set default country or prompt for phone number using the Phone Number Hint flow.
- setDefaultCountryForSpinner();
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- // Pass the activity instance to the handler
- mCheckPhoneHandler.onActivityResult(requireActivity(), requestCode, resultCode, data);
- }
-
- @Override
- public void onClick(View v) {
- onNext();
- }
-
- private void start(PhoneNumber number) {
- if (!PhoneNumber.isValid(number)) {
- mPhoneInputLayout.setError(getString(R.string.fui_invalid_phone_number));
- return;
- }
- mPhoneEditText.setText(number.getPhoneNumber());
- mPhoneEditText.setSelection(number.getPhoneNumber().length());
-
- String iso = number.getCountryIso();
-
- if (PhoneNumber.isCountryValid(number) && mCountryListSpinner.isValidIso(iso)) {
- setCountryCode(number);
- onNext();
- }
- }
-
- private void onNext() {
- String phoneNumber = getPseudoValidPhoneNumber();
- if (phoneNumber == null) {
- mPhoneInputLayout.setError(getString(R.string.fui_invalid_phone_number));
- } else {
- mVerificationHandler.verifyPhoneNumber(requireActivity(), phoneNumber, false);
- }
- }
-
- @Nullable
- private String getPseudoValidPhoneNumber() {
- String everythingElse = mPhoneEditText.getText().toString();
- if (TextUtils.isEmpty(everythingElse)) {
- return null;
- }
- return PhoneNumberUtils.format(
- everythingElse, mCountryListSpinner.getSelectedCountryInfo());
- }
-
- private void setupPrivacyDisclosures() {
- FlowParameters params = getFlowParams();
- boolean termsAndPrivacyUrlsProvided = params.isTermsOfServiceUrlProvided()
- && params.isPrivacyPolicyUrlProvided();
-
- if (!params.shouldShowProviderChoice() && termsAndPrivacyUrlsProvided) {
- PrivacyDisclosureUtils.setupTermsOfServiceAndPrivacyPolicySmsText(requireContext(),
- params,
- mSmsTermsText);
- } else {
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(requireContext(),
- params,
- mFooterText);
- String verifyText = getString(R.string.fui_verify_phone_number);
- mSmsTermsText.setText(getString(R.string.fui_sms_terms_of_service, verifyText));
- }
- }
-
- private void setCountryCode(PhoneNumber number) {
- mCountryListSpinner.setSelectedForCountry(
- new Locale("", number.getCountryIso()), number.getCountryCode());
- }
-
- private void setupCountrySpinner() {
- Bundle params = getArguments().getBundle(ExtraConstants.PARAMS);
- mCountryListSpinner.init(params, mCountryListAnchor);
- // Clear error when spinner is clicked
- mCountryListSpinner.setOnClickListener(v -> mPhoneInputLayout.setError(null));
- }
-
- private void setDefaultCountryForSpinner() {
- // Check for phone number defaults
- Bundle params = getArguments().getBundle(ExtraConstants.PARAMS);
- String phone = null;
- String countryIso = null;
- String nationalNumber = null;
- if (params != null) {
- phone = params.getString(ExtraConstants.PHONE);
- countryIso = params.getString(ExtraConstants.COUNTRY_ISO);
- nationalNumber = params.getString(ExtraConstants.NATIONAL_NUMBER);
- }
-
- // If phone is provided in full, use it. Otherwise, parse ISO and national number or prompt for a phone hint.
- if (!TextUtils.isEmpty(phone)) {
- start(PhoneNumberUtils.getPhoneNumber(phone));
- } else if (!TextUtils.isEmpty(countryIso) && !TextUtils.isEmpty(nationalNumber)) {
- start(PhoneNumberUtils.getPhoneNumber(countryIso, nationalNumber));
- } else if (!TextUtils.isEmpty(countryIso)) {
- setCountryCode(new PhoneNumber(
- "",
- countryIso,
- String.valueOf(PhoneNumberUtils.getCountryCode(countryIso))));
- } else if (getFlowParams().enableCredentials) {
- // Launch phone number hint flow using the new API
- mCheckPhoneHandler.fetchCredential(requireActivity());
- }
- }
-
- @Override
- public void showProgress(int message) {
- mSubmitButton.setEnabled(false);
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mSubmitButton.setEnabled(true);
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
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
deleted file mode 100644
index d61504564..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/CountryListSpinner.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- */
-package com.firebase.ui.auth.ui.phone;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.AdapterView;
-import android.widget.ArrayAdapter;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.CountryInfo;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PhoneNumberUtils;
-import com.google.android.material.textfield.TextInputEditText;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.widget.ListPopupWindow;
-
-public final class CountryListSpinner extends TextInputEditText implements View.OnClickListener {
-
- private static final String KEY_SUPER_STATE = "KEY_SUPER_STATE";
- private static final String KEY_COUNTRY_INFO = "KEY_COUNTRY_INFO";
-
- private final ArrayAdapter mCountryListAdapter;
- private View.OnClickListener mListener;
- private CountryInfo mSelectedCountryInfo;
-
- private ListPopupWindow mListPopupWindow;
-
- private Set mAllowedCountryIsos = new HashSet<>();
- private Set mBlockedCountryIsos = new HashSet<>();
-
- public CountryListSpinner(Context context) {
- this(context, null);
- }
-
- public CountryListSpinner(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.editTextStyle);
- }
-
- public CountryListSpinner(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- super.setOnClickListener(this);
-
- mCountryListAdapter = new ArrayAdapter<>(getContext(),
- R.layout.fui_dgts_country_row,
- android.R.id.text1);
- mListPopupWindow = new ListPopupWindow(context, null, R.attr.listPopupWindowStyle);
- mListPopupWindow.setModal(true);
-
- // Prevent the keyboard from showing
- setInputType(EditorInfo.TYPE_NULL);
-
- mListPopupWindow.setOnItemClickListener(new AdapterView.OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id) {
- CountryInfo info = mCountryListAdapter.getItem(position);
- if (info != null) {
- setSelectedForCountry(info.getCountryCode(), info.getLocale());
- }
-
- onUnfocus();
- }
- });
- }
-
- @Override
- protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
- super.onFocusChanged(focused, direction, previouslyFocusedRect);
- if (focused) {
- onFocus();
- } else {
- onUnfocus();
- }
- }
-
- private void onFocus() {
- hideKeyboard(getContext(), this);
- mListPopupWindow.show();
- }
-
- private void onUnfocus() {
- mListPopupWindow.dismiss();
- }
-
- public void init(Bundle params, View anchorView) {
- if (params != null) {
- List countries = getCountriesToDisplayInSpinner(params);
- setCountriesToDisplay(countries);
- setDefaultCountryForSpinner(countries);
-
- mListPopupWindow.setAnchorView(anchorView);
- mListPopupWindow.setAdapter(mCountryListAdapter);
- }
- }
-
- private List getCountriesToDisplayInSpinner(Bundle params) {
- initCountrySpinnerIsosFromParams(params);
- Map countryInfoMap = PhoneNumberUtils.getImmutableCountryIsoMap();
-
- // We consider all countries to be allowed if there are no allowed
- // or blocked countries given as input.
- if (mAllowedCountryIsos.isEmpty() && mBlockedCountryIsos.isEmpty()) {
- this.mAllowedCountryIsos = new HashSet<>(countryInfoMap.keySet());
- }
-
- List countryInfoList = new ArrayList<>();
-
- // At this point either mAllowedCountryIsos or mBlockedCountryIsos is null.
- // 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 (!mBlockedCountryIsos.isEmpty()) {
- // Exclude all countries in the mBlockedCountryIsos list.
- excludedCountries.addAll(mBlockedCountryIsos);
- } else {
- // Exclude all countries that are not present in the mAllowedCountryIsos list.
- excludedCountries.addAll(countryInfoMap.keySet());
- excludedCountries.removeAll(mAllowedCountryIsos);
- }
-
- // Once we know which countries need to be excluded, we loop through the country isos,
- // skipping those that have been excluded.
- for (String countryIso : countryInfoMap.keySet()) {
- if (!excludedCountries.contains(countryIso)) {
- countryInfoList.add(new CountryInfo(new Locale("", countryIso),
- countryInfoMap.get(countryIso)));
- }
- }
- Collections.sort(countryInfoList);
- return countryInfoList;
- }
-
- private void initCountrySpinnerIsosFromParams(@NonNull Bundle params) {
- List allowedCountries =
- params.getStringArrayList(ExtraConstants.ALLOWLISTED_COUNTRIES);
- List blockedCountries =
- params.getStringArrayList(ExtraConstants.BLOCKLISTED_COUNTRIES);
-
- if (allowedCountries != null) {
- mAllowedCountryIsos = convertCodesToIsos(allowedCountries);
- }
-
- if (blockedCountries != null) {
- mBlockedCountryIsos = convertCodesToIsos(blockedCountries);
- }
- }
-
- private Set convertCodesToIsos(@NonNull List codes) {
- Set isos = new HashSet<>();
- for (String code : codes) {
- if (PhoneNumberUtils.isValid(code)) {
- isos.addAll(PhoneNumberUtils.getCountryIsosFromCountryCode(code));
- } else {
- isos.add(code);
- }
- }
- return isos;
- }
-
- public void setCountriesToDisplay(List countries) {
- mCountryListAdapter.addAll(countries);
- mCountryListAdapter.notifyDataSetChanged();
- }
-
- private void setDefaultCountryForSpinner(List countries) {
- CountryInfo countryInfo = PhoneNumberUtils.getCurrentCountryInfo(getContext());
- if (isValidIso(countryInfo.getLocale().getCountry())) {
- setSelectedForCountry(countryInfo.getCountryCode(),
- countryInfo.getLocale());
- } else if (countries.iterator().hasNext()) {
- countryInfo = countries.iterator().next();
- setSelectedForCountry(countryInfo.getCountryCode(),
- countryInfo.getLocale());
- }
- }
-
- public boolean isValidIso(String iso) {
- iso = iso.toUpperCase(Locale.getDefault());
- boolean valid = true;
- if (!mAllowedCountryIsos.isEmpty()) {
- valid = valid && mAllowedCountryIsos.contains(iso);
- }
-
- if (!mBlockedCountryIsos.isEmpty()) {
- valid = valid && !mBlockedCountryIsos.contains(iso);
- }
-
- return valid;
- }
-
- @Override
- public Parcelable onSaveInstanceState() {
- Parcelable superState = super.onSaveInstanceState();
-
- Bundle bundle = new Bundle();
- bundle.putParcelable(KEY_SUPER_STATE, superState);
- bundle.putParcelable(KEY_COUNTRY_INFO, mSelectedCountryInfo);
-
- return bundle;
- }
-
- @Override
- public void onRestoreInstanceState(Parcelable state) {
- if (!(state instanceof Bundle)) {
- super.onRestoreInstanceState(state);
- return;
- }
-
- Bundle bundle = (Bundle) state;
- Parcelable superState = bundle.getParcelable(KEY_SUPER_STATE);
- mSelectedCountryInfo = bundle.getParcelable(KEY_COUNTRY_INFO);
-
- super.onRestoreInstanceState(superState);
- }
-
- private static void hideKeyboard(Context context, View view) {
- final InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
- if (imm != null) {
- imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
- }
- }
-
- public void setSelectedForCountry(int countryCode, Locale locale) {
- mSelectedCountryInfo = new CountryInfo(locale, countryCode);
- setText(mSelectedCountryInfo.toShortString());
- }
-
- public void setSelectedForCountry(final Locale locale, String countryCode) {
- if (isValidIso(locale.getCountry())) {
- final String countryName = locale.getDisplayName();
- if (!TextUtils.isEmpty(countryName) && !TextUtils.isEmpty(countryCode)) {
- setSelectedForCountry(Integer.parseInt(countryCode), locale);
- }
- }
- }
-
- public CountryInfo getSelectedCountryInfo() {
- return mSelectedCountryInfo;
- }
-
- @Override
- public void setOnClickListener(OnClickListener l) {
- mListener = l;
- }
-
- @Override
- public void onClick(View view) {
- hideKeyboard(getContext(), this);
- executeUserClickListener(view);
- }
-
- private void executeUserClickListener(View view) {
- if (mListener != null) {
- mListener.onClick(view);
- }
-
- onFocus();
- }
-}
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
deleted file mode 100644
index dacac70de..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneActivity.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2017 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.ui.phone;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.widget.Toast;
-
-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.data.model.PhoneNumberVerificationRequiredException;
-import com.firebase.ui.auth.data.model.User;
-import com.firebase.ui.auth.ui.AppCompatBase;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.FirebaseAuthError;
-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.FirebaseAuthException;
-import com.google.firebase.auth.PhoneAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.fragment.app.FragmentManager;
-import androidx.lifecycle.ViewModelProvider;
-
-/**
- * Activity to control the entire phone verification flow. Plays host to {@link
- * CheckPhoneNumberFragment} and {@link SubmitConfirmationCodeFragment}
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PhoneActivity extends AppCompatBase {
- private PhoneNumberVerificationHandler mPhoneVerifier;
-
- public static Intent createIntent(Context context, FlowParameters params, Bundle args) {
- return createBaseIntent(context, PhoneActivity.class, params)
- .putExtra(ExtraConstants.PARAMS, args);
- }
-
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.fui_activity_register_phone);
-
- final PhoneProviderResponseHandler handler =
- new ViewModelProvider(this).get(PhoneProviderResponseHandler.class);
- handler.init(getFlowParams());
- handler.getOperation().observe(this, new ResourceObserver(
- this, R.string.fui_progress_dialog_signing_in) {
- @Override
- protected void onSuccess(@NonNull IdpResponse response) {
- startSaveCredentials(handler.getCurrentUser(), response, null);
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- handleError(e);
- }
- });
-
- mPhoneVerifier = new ViewModelProvider(this).get(PhoneNumberVerificationHandler.class);
- mPhoneVerifier.init(getFlowParams());
- mPhoneVerifier.onRestoreInstanceState(savedInstanceState);
- mPhoneVerifier.getOperation().observe(this, new ResourceObserver(
- this, R.string.fui_verifying) {
- @Override
- protected void onSuccess(@NonNull PhoneVerification verification) {
- if (verification.isAutoVerified()) {
- Toast.makeText(
- PhoneActivity.this,
- R.string.fui_auto_verified,
- Toast.LENGTH_LONG
- ).show();
-
- FragmentManager manager = getSupportFragmentManager();
- if (manager.findFragmentByTag(SubmitConfirmationCodeFragment.TAG) != null) {
- // Ensure the submit code screen isn't visible if there's no code to submit.
- // It's possible to get into this state when an SMS is sent, but then
- // automatically retrieved.
- manager.popBackStack();
- }
- }
-
- handler.startSignIn(verification.getCredential(), new IdpResponse.Builder(
- new User.Builder(PhoneAuthProvider.PROVIDER_ID, null)
- .setPhoneNumber(verification.getNumber())
- .build())
- .build());
- }
-
- @Override
- protected void onFailure(@NonNull Exception e) {
- if (e instanceof PhoneNumberVerificationRequiredException) {
- // Only boot up the submit code fragment if it isn't already being shown to the
- // user. If the user requests another verification code, the fragment will
- // already be visible so we have nothing to do.
- if (getSupportFragmentManager()
- .findFragmentByTag(SubmitConfirmationCodeFragment.TAG) == null) {
- showSubmitCodeFragment(
- ((PhoneNumberVerificationRequiredException) e).getPhoneNumber());
- }
-
- // Clear existing errors
- handleError(null);
- } else {
- handleError(e);
- }
- }
- });
-
- if (savedInstanceState != null) { return; }
-
- Bundle params = getIntent().getExtras().getBundle(ExtraConstants.PARAMS);
- CheckPhoneNumberFragment fragment = CheckPhoneNumberFragment.newInstance(params);
- getSupportFragmentManager().beginTransaction()
- .replace(R.id.fragment_phone, fragment, CheckPhoneNumberFragment.TAG)
- .disallowAddToBackStack()
- .commit();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
- mPhoneVerifier.onSaveInstanceState(outState);
- }
-
- @Override
- public void onBackPressed() {
- if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
- getSupportFragmentManager().popBackStack();
- } else {
- super.onBackPressed();
- }
- }
-
- private void handleError(@Nullable Exception e) {
- TextInputLayout errorView = getErrorView();
- if (errorView == null) { return; }
-
- if (e instanceof FirebaseAuthAnonymousUpgradeException) {
- IdpResponse response = ((FirebaseAuthAnonymousUpgradeException) e).getResponse();
- finish(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response.toIntent());
- } else if (e instanceof FirebaseAuthException) {
- 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(getErrorMessage(FirebaseAuthError.ERROR_UNKNOWN));
- } else {
- errorView.setError(null);
- }
- }
-
- @Nullable
- private TextInputLayout getErrorView() {
- CheckPhoneNumberFragment checkFragment = (CheckPhoneNumberFragment)
- getSupportFragmentManager().findFragmentByTag(CheckPhoneNumberFragment.TAG);
- SubmitConfirmationCodeFragment submitFragment = (SubmitConfirmationCodeFragment)
- getSupportFragmentManager().findFragmentByTag(SubmitConfirmationCodeFragment.TAG);
-
- if (checkFragment != null && checkFragment.getView() != null) {
- return checkFragment.getView().findViewById(R.id.phone_layout);
- } else if (submitFragment != null && submitFragment.getView() != null) {
- return submitFragment.getView().findViewById(R.id.confirmation_code_layout);
- } else {
- return null;
- }
- }
-
- private String getErrorMessage(FirebaseAuthError error) {
- switch (error) {
- case ERROR_INVALID_PHONE_NUMBER:
- return getString(R.string.fui_invalid_phone_number);
- case ERROR_TOO_MANY_REQUESTS:
- return getString(R.string.fui_error_too_many_attempts);
- case ERROR_QUOTA_EXCEEDED:
- return getString(R.string.fui_error_quota_exceeded);
- case ERROR_INVALID_VERIFICATION_CODE:
- return getString(R.string.fui_incorrect_code_dialog_body);
- case ERROR_SESSION_EXPIRED:
- return getString(R.string.fui_error_session_expired);
- default:
- return error.getDescription();
- }
- }
-
- private void showSubmitCodeFragment(String number) {
- getSupportFragmentManager().beginTransaction()
- .replace(
- R.id.fragment_phone,
- SubmitConfirmationCodeFragment.newInstance(number),
- SubmitConfirmationCodeFragment.TAG)
- .addToBackStack(null)
- .commit();
- }
-
- @Override
- public void showProgress(int message) {
- getActiveFragment().showProgress(message);
- }
-
- @Override
- public void hideProgress() {
- getActiveFragment().hideProgress();
- }
-
- @NonNull
- private FragmentBase getActiveFragment() {
- FragmentBase fragment = (CheckPhoneNumberFragment)
- getSupportFragmentManager().findFragmentByTag(CheckPhoneNumberFragment.TAG);
- if (fragment == null || fragment.getView() == null) {
- fragment = (SubmitConfirmationCodeFragment)
- getSupportFragmentManager().findFragmentByTag(SubmitConfirmationCodeFragment.TAG);
- }
-
- if (fragment == null || fragment.getView() == null) {
- throw new IllegalStateException("No fragments added");
- } else {
- return fragment;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberVerificationHandler.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberVerificationHandler.java
deleted file mode 100644
index 251581560..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneNumberVerificationHandler.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package com.firebase.ui.auth.ui.phone;
-
-import android.app.Activity;
-import android.app.Application;
-import android.content.ActivityNotFoundException;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import com.firebase.ui.auth.data.model.PhoneNumberVerificationRequiredException;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.viewmodel.AuthViewModelBase;
-import com.google.firebase.FirebaseException;
-import com.google.firebase.auth.PhoneAuthCredential;
-import com.google.firebase.auth.PhoneAuthOptions;
-import com.google.firebase.auth.PhoneAuthProvider;
-
-import java.util.concurrent.TimeUnit;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public class PhoneNumberVerificationHandler extends AuthViewModelBase {
- private static final long AUTO_RETRIEVAL_TIMEOUT_SECONDS = 120;
- private static final String VERIFICATION_ID_KEY = "verification_id";
-
- private String mVerificationId;
- private PhoneAuthProvider.ForceResendingToken mForceResendingToken;
-
- public PhoneNumberVerificationHandler(Application application) {
- super(application);
- }
-
- public void verifyPhoneNumber(@NonNull Activity activity, final String number, boolean force) {
- setResult(Resource.forLoading());
- PhoneAuthOptions.Builder optionsBuilder = PhoneAuthOptions.newBuilder(getAuth())
- .setPhoneNumber(number)
- .setTimeout(AUTO_RETRIEVAL_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- .setActivity(activity)
- .setCallbacks(new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
- @Override
- public void onVerificationCompleted(@NonNull PhoneAuthCredential credential) {
- setResult(Resource.forSuccess(new PhoneVerification(
- number, credential, true)));
- }
-
- @Override
- public void onVerificationFailed(@NonNull FirebaseException e) {
- setResult(Resource.forFailure(e));
- }
-
- @Override
- public void onCodeSent(@NonNull String verificationId,
- @NonNull PhoneAuthProvider.ForceResendingToken token) {
- mVerificationId = verificationId;
- mForceResendingToken = token;
- setResult(Resource.forFailure(
- new PhoneNumberVerificationRequiredException(number)));
- }
- });
- if (force) {
- optionsBuilder.setForceResendingToken(mForceResendingToken);
- }
- if (isBrowserAvailable(activity)) {
- PhoneAuthProvider.verifyPhoneNumber(optionsBuilder.build());
- } else {
- setResult(Resource.forFailure(new ActivityNotFoundException("No browser was found in this device")));
- }
- }
-
- public void submitVerificationCode(String number, String code) {
- setResult(Resource.forSuccess(new PhoneVerification(
- number,
- PhoneAuthProvider.getCredential(mVerificationId, code),
- false)));
- }
-
- public void onSaveInstanceState(@NonNull Bundle outState) {
- outState.putString(VERIFICATION_ID_KEY, mVerificationId);
- }
-
- public void onRestoreInstanceState(@Nullable Bundle savedInstanceState) {
- if (mVerificationId == null && savedInstanceState != null) {
- mVerificationId = savedInstanceState.getString(VERIFICATION_ID_KEY);
- }
- }
-
- private boolean isBrowserAvailable(Activity activity) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://"));
- return browserIntent.resolveActivity(activity.getPackageManager()) != null;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerification.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerification.java
deleted file mode 100644
index 2de2bdd32..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/PhoneVerification.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package com.firebase.ui.auth.ui.phone;
-
-import com.google.firebase.auth.PhoneAuthCredential;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class PhoneVerification {
- private final String mNumber;
- private final PhoneAuthCredential mCredential;
- private final boolean mIsAutoVerified;
-
- public PhoneVerification(@NonNull String number,
- @NonNull PhoneAuthCredential credential,
- boolean verified) {
- mNumber = number;
- mCredential = credential;
- mIsAutoVerified = verified;
- }
-
- @NonNull
- public String getNumber() {
- return mNumber;
- }
-
- @NonNull
- public PhoneAuthCredential getCredential() {
- return mCredential;
- }
-
- public boolean isAutoVerified() {
- return mIsAutoVerified;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- PhoneVerification that = (PhoneVerification) o;
-
- return mIsAutoVerified == that.mIsAutoVerified
- && mNumber.equals(that.mNumber)
- && mCredential.equals(that.mCredential);
- }
-
- @Override
- public int hashCode() {
- int result = mNumber.hashCode();
- result = 31 * result + mCredential.hashCode();
- result = 31 * result + (mIsAutoVerified ? 1 : 0);
- return result;
- }
-
- public String toString() {
- return "PhoneVerification{" +
- "mNumber='" + mNumber + '\'' +
- ", mCredential=" + mCredential +
- ", mIsAutoVerified=" + mIsAutoVerified +
- '}';
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/SpacedEditText.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/SpacedEditText.java
deleted file mode 100644
index e7b67e7ae..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/SpacedEditText.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- */
-
-package com.firebase.ui.auth.ui.phone;
-
-import android.content.Context;
-import android.content.res.TypedArray;
-import android.text.Editable;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.style.ScaleXSpan;
-import android.util.AttributeSet;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.textfield.TextInputEditText;
-
-/**
- * This element inserts spaces between characters in the edit text and expands the width of the
- * spaces using spannables. This is required since Android's letter spacing is not available until
- * API 21.
- */
-public final class SpacedEditText extends TextInputEditText {
- private float mProportion;
- private SpannableStringBuilder mOriginalText = new SpannableStringBuilder("");
-
- public SpacedEditText(Context context) {
- super(context);
- }
-
- public SpacedEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- initAttrs(context, attrs);
- }
-
- void initAttrs(Context context, AttributeSet attrs) {
- TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.SpacedEditText);
- // Controls the ScaleXSpan applied on the injected spaces
- mProportion = array.getFloat(R.styleable.SpacedEditText_spacingProportion, 1f);
- array.recycle();
- }
-
- @Override
- public void setText(CharSequence text, BufferType type) {
- mOriginalText = new SpannableStringBuilder(text);
- super.setText(getSpacedOutString(text), BufferType.SPANNABLE);
- }
-
- /**
- * Set the selection after recalculating the index intended by the caller.
- */
- @Override
- public void setSelection(int index) {
- // Desired mapping:
- // 0 --> 0
- // 1 --> 1
- // 2 --> 3
- // 3 --> 5
- // 4 --> 7
- // 5 --> 9
- // 6 --> 11
-
- // Naive transformation
- int newIndex = (index * 2) - 1;
-
- // Lower bound is 0
- newIndex = Math.max(newIndex, 0);
-
- // Upper bound is original length * 2 - 1
- newIndex = Math.min(newIndex, (mOriginalText.length() * 2) - 1);
-
- try {
- super.setSelection(newIndex);
- } catch (IndexOutOfBoundsException e) {
- // For debug purposes only
- throw new IndexOutOfBoundsException(e.getMessage() +
- ", requestedIndex=" + index +
- ", newIndex= " + newIndex +
- ", originalText=" + mOriginalText);
- }
- }
-
- private SpannableStringBuilder getSpacedOutString(CharSequence text) {
- SpannableStringBuilder builder = new SpannableStringBuilder();
- int textLength = text.length();
- int lastSpaceIndex = -1;
-
- //Insert a space in front of all characters upto the last character
- //Scale the space without scaling the character to preserve font appearance
- for (int i = 0; i < textLength - 1; i++) {
- builder.append(text.charAt(i));
- builder.append(" ");
- lastSpaceIndex += 2;
- builder.setSpan(new ScaleXSpan(mProportion), lastSpaceIndex, lastSpaceIndex + 1,
- Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
-
- //Append the last character
- if (textLength != 0) builder.append(text.charAt(textLength - 1));
-
- return builder;
- }
-
- public Editable getUnspacedText() {
- return mOriginalText;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/phone/SubmitConfirmationCodeFragment.java b/auth/src/main/java/com/firebase/ui/auth/ui/phone/SubmitConfirmationCodeFragment.java
deleted file mode 100644
index 26badc4b0..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/ui/phone/SubmitConfirmationCodeFragment.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright 2017 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.ui.phone;
-
-import android.content.ClipData;
-import android.content.ClipboardManager;
-import android.content.Context;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.data.model.State;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.util.ExtraConstants;
-import com.firebase.ui.auth.util.data.PrivacyDisclosureUtils;
-import com.firebase.ui.auth.util.ui.BucketedTextChangeListener;
-import com.firebase.ui.auth.viewmodel.phone.PhoneProviderResponseHandler;
-
-import java.util.concurrent.TimeUnit;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.core.content.ContextCompat;
-import androidx.lifecycle.Observer;
-import androidx.lifecycle.ViewModelProvider;
-
-/**
- * Display confirmation code to verify phone numbers input in {@link CheckPhoneNumberFragment}
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class SubmitConfirmationCodeFragment extends FragmentBase {
-
- public static final String TAG = "SubmitConfirmationCodeFragment";
-
- private static final int VERIFICATION_CODE_LENGTH = 6;
- private static final long RESEND_WAIT_MILLIS = 60000;
- private static final long TICK_INTERVAL_MILLIS = 500;
- private static final String EXTRA_MILLIS_UNTIL_FINISHED = "millis_until_finished";
-
- private final Handler mLooper = new Handler();
- private final Runnable mCountdown = () -> processCountdownTick();
-
- private PhoneNumberVerificationHandler mHandler;
- private String mPhoneNumber;
-
- private ProgressBar mProgressBar;
- private TextView mPhoneTextView;
- private TextView mResendCodeTextView;
- private TextView mCountDownTextView;
- private SpacedEditText mConfirmationCodeEditText;
- private long mMillisUntilFinished = RESEND_WAIT_MILLIS;
-
- private boolean mHasResumed;
-
- public static SubmitConfirmationCodeFragment newInstance(String phoneNumber) {
- SubmitConfirmationCodeFragment fragment = new SubmitConfirmationCodeFragment();
- Bundle args = new Bundle();
- args.putString(ExtraConstants.PHONE, phoneNumber);
- fragment.setArguments(args);
- return fragment;
- }
-
- @Override
- public void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- mHandler = new ViewModelProvider(requireActivity())
- .get(PhoneNumberVerificationHandler.class);
- mPhoneNumber = getArguments().getString(ExtraConstants.PHONE);
- if (savedInstanceState != null) {
- mMillisUntilFinished = savedInstanceState.getLong(EXTRA_MILLIS_UNTIL_FINISHED);
- }
- }
-
- @Nullable
- @Override
- public View onCreateView(@NonNull LayoutInflater inflater,
- @Nullable ViewGroup container,
- @Nullable Bundle savedInstanceState) {
- return inflater.inflate(R.layout.fui_confirmation_code_layout, container, false);
- }
-
- @Override
- public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
- mProgressBar = view.findViewById(R.id.top_progress_bar);
- mPhoneTextView = view.findViewById(R.id.edit_phone_number);
- mCountDownTextView = view.findViewById(R.id.ticker);
- mResendCodeTextView = view.findViewById(R.id.resend_code);
- mConfirmationCodeEditText = view.findViewById(R.id.confirmation_code);
-
- requireActivity().setTitle(getString(R.string.fui_verify_your_phone_title));
- processCountdownTick();
- setupConfirmationCodeEditText();
- setupEditPhoneNumberTextView();
- setupResendConfirmationCodeTextView();
- PrivacyDisclosureUtils.setupTermsOfServiceFooter(
- requireContext(),
- getFlowParams(),
- view.findViewById(R.id.email_footer_tos_and_pp_text));
- }
-
- @Override
- public void onActivityCreated(@Nullable Bundle savedInstanceState) {
- super.onActivityCreated(savedInstanceState);
- new ViewModelProvider(requireActivity())
- .get(PhoneProviderResponseHandler.class)
- .getOperation()
- .observe(getViewLifecycleOwner(), resource -> {
- if (resource.getState() == State.FAILURE) {
- mConfirmationCodeEditText.setText("");
- }
- });
- }
-
- @Override
- public void onStart() {
- super.onStart();
- mConfirmationCodeEditText.requestFocus();
- ((InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE))
- .showSoftInput(mConfirmationCodeEditText, 0);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- if (!mHasResumed) {
- // Don't check for codes before we've even had the chance to send one.
- mHasResumed = true;
- return;
- }
-
- ClipData clip = ContextCompat.getSystemService(requireContext(), ClipboardManager.class)
- .getPrimaryClip();
- if (clip != null && clip.getItemCount() == 1) {
- CharSequence candidate = clip.getItemAt(0).getText();
- if (candidate != null && candidate.length() == VERIFICATION_CODE_LENGTH) {
- try {
- Integer.parseInt(candidate.toString());
-
- // We have a number! Try to submit it.
- mConfirmationCodeEditText.setText(candidate);
- } catch (NumberFormatException ignored) {
- // Turns out it wasn't a number
- }
- }
- }
-
- mLooper.removeCallbacks(mCountdown);
- mLooper.postDelayed(mCountdown, TICK_INTERVAL_MILLIS);
- }
-
- @Override
- public void onSaveInstanceState(@NonNull Bundle outState) {
- mLooper.removeCallbacks(mCountdown);
- outState.putLong(EXTRA_MILLIS_UNTIL_FINISHED, mMillisUntilFinished);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- // Remove here in addition to onSaveInstanceState since it might not be called if finishing
- // for good.
- mLooper.removeCallbacks(mCountdown);
- }
-
- private void setupConfirmationCodeEditText() {
- mConfirmationCodeEditText.setText("------");
- mConfirmationCodeEditText.addTextChangedListener(new BucketedTextChangeListener(
- mConfirmationCodeEditText, VERIFICATION_CODE_LENGTH, "-",
- new BucketedTextChangeListener.ContentChangeCallback() {
- @Override
- public void whenComplete() {
- submitCode();
- }
-
- @Override
- public void whileIncomplete() {}
- }));
- }
-
- private void setupEditPhoneNumberTextView() {
- mPhoneTextView.setText(mPhoneNumber);
- mPhoneTextView.setOnClickListener(v -> requireActivity().getSupportFragmentManager().popBackStack());
- }
-
- private void setupResendConfirmationCodeTextView() {
- mResendCodeTextView.setOnClickListener(v -> {
- mHandler.verifyPhoneNumber(requireActivity(), mPhoneNumber, true);
-
- mResendCodeTextView.setVisibility(View.GONE);
- mCountDownTextView.setVisibility(View.VISIBLE);
- mCountDownTextView.setText(String.format(getString(R.string.fui_resend_code_in),
- RESEND_WAIT_MILLIS / 1000));
- mMillisUntilFinished = RESEND_WAIT_MILLIS;
- mLooper.postDelayed(mCountdown, TICK_INTERVAL_MILLIS);
- });
- }
-
- private void processCountdownTick() {
- mMillisUntilFinished -= TICK_INTERVAL_MILLIS;
- if (mMillisUntilFinished <= 0) {
- mCountDownTextView.setText("");
- mCountDownTextView.setVisibility(View.GONE);
- mResendCodeTextView.setVisibility(View.VISIBLE);
- } else {
- mCountDownTextView.setText(String.format(getString(R.string.fui_resend_code_in),
- TimeUnit.MILLISECONDS.toSeconds(mMillisUntilFinished) + 1));
- mLooper.postDelayed(mCountdown, TICK_INTERVAL_MILLIS);
- }
- }
-
- private void submitCode() {
- mHandler.submitVerificationCode(
- mPhoneNumber, mConfirmationCodeEditText.getUnspacedText().toString());
- }
-
- @Override
- public void showProgress(int message) {
- mProgressBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void hideProgress() {
- mProgressBar.setVisibility(View.INVISIBLE);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
similarity index 92%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
index 6cb2dc6f6..9039ead5d 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/FirebaseAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
@@ -26,6 +26,7 @@ import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -62,6 +63,7 @@ import com.firebase.ui.auth.compose.configuration.string_provider.LocalAuthUIStr
import com.firebase.ui.auth.compose.ui.components.LocalTopLevelDialogController
import com.firebase.ui.auth.compose.ui.components.rememberTopLevelDialogController
import com.firebase.ui.auth.compose.ui.method_picker.AuthMethodPicker
+import com.firebase.ui.auth.compose.ui.screens.email.EmailAuthScreen
import com.firebase.ui.auth.compose.ui.screens.phone.PhoneAuthScreen
import com.firebase.ui.auth.compose.util.EmailLinkPersistenceManager
import com.google.firebase.auth.AuthCredential
@@ -206,52 +208,56 @@ fun FirebaseAuthScreen(
startDestination = AuthRoute.MethodPicker.route
) {
composable(AuthRoute.MethodPicker.route) {
- AuthMethodPicker(
- providers = configuration.providers,
- logo = logoAsset,
- termsOfServiceUrl = configuration.tosUrl,
- privacyPolicyUrl = configuration.privacyPolicyUrl,
- onProviderSelected = { provider ->
- when (provider) {
- is AuthProvider.Anonymous -> onSignInAnonymously?.invoke()
-
- is AuthProvider.Email -> {
- navController.navigate(AuthRoute.Email.route)
- }
+ Scaffold { innerPadding ->
+ AuthMethodPicker(
+ modifier = modifier
+ .padding(innerPadding),
+ providers = configuration.providers,
+ logo = logoAsset,
+ termsOfServiceUrl = configuration.tosUrl,
+ privacyPolicyUrl = configuration.privacyPolicyUrl,
+ onProviderSelected = { provider ->
+ when (provider) {
+ is AuthProvider.Anonymous -> onSignInAnonymously?.invoke()
+
+ is AuthProvider.Email -> {
+ navController.navigate(AuthRoute.Email.route)
+ }
- is AuthProvider.Phone -> {
- navController.navigate(AuthRoute.Phone.route)
- }
+ is AuthProvider.Phone -> {
+ navController.navigate(AuthRoute.Phone.route)
+ }
- is AuthProvider.Google -> onSignInWithGoogle?.invoke()
+ is AuthProvider.Google -> onSignInWithGoogle?.invoke()
- is AuthProvider.Facebook -> onSignInWithFacebook?.invoke()
+ is AuthProvider.Facebook -> onSignInWithFacebook?.invoke()
- is AuthProvider.Apple -> onSignInWithApple?.invoke()
+ is AuthProvider.Apple -> onSignInWithApple?.invoke()
- is AuthProvider.Github -> onSignInWithGithub?.invoke()
+ is AuthProvider.Github -> onSignInWithGithub?.invoke()
- is AuthProvider.Microsoft -> onSignInWithMicrosoft?.invoke()
+ is AuthProvider.Microsoft -> onSignInWithMicrosoft?.invoke()
- is AuthProvider.Yahoo -> onSignInWithYahoo?.invoke()
+ is AuthProvider.Yahoo -> onSignInWithYahoo?.invoke()
- is AuthProvider.Twitter -> onSignInWithTwitter?.invoke()
+ is AuthProvider.Twitter -> onSignInWithTwitter?.invoke()
- is AuthProvider.GenericOAuth -> genericOAuthHandlers[provider]?.invoke()
+ is AuthProvider.GenericOAuth -> genericOAuthHandlers[provider]?.invoke()
- else -> {
- onSignInFailure(
- AuthException.UnknownException(
- message = "Provider ${provider.providerId} is not supported in FirebaseAuthScreen",
- cause = IllegalArgumentException(
- "Provider ${provider.providerId} is not supported in FirebaseAuthScreen"
+ else -> {
+ onSignInFailure(
+ AuthException.UnknownException(
+ message = "Provider ${provider.providerId} is not supported in FirebaseAuthScreen",
+ cause = IllegalArgumentException(
+ "Provider ${provider.providerId} is not supported in FirebaseAuthScreen"
+ )
)
)
- )
+ }
}
}
- }
- )
+ )
+ }
}
composable(AuthRoute.Email.route) {
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaChallengeDefaults.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaChallengeDefaults.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaChallengeDefaults.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaChallengeDefaults.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaChallengeScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaChallengeScreen.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaChallengeScreen.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaChallengeScreen.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaEnrollmentDefaults.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaEnrollmentDefaults.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaEnrollmentDefaults.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaEnrollmentDefaults.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaEnrollmentScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaEnrollmentScreen.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaEnrollmentScreen.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaEnrollmentScreen.kt
index 082a3c980..7be763141 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/MfaEnrollmentScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/MfaEnrollmentScreen.kt
@@ -29,7 +29,7 @@ import com.firebase.ui.auth.compose.configuration.MfaFactor
import com.firebase.ui.auth.compose.configuration.authUIConfiguration
import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider
import com.firebase.ui.auth.compose.data.CountryData
-import com.firebase.ui.auth.compose.data.CountryUtils
+import com.firebase.ui.auth.compose.util.CountryUtils
import com.firebase.ui.auth.compose.mfa.MfaEnrollmentContentState
import com.firebase.ui.auth.compose.mfa.MfaEnrollmentStep
import com.firebase.ui.auth.compose.mfa.SmsEnrollmentHandler
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
index 11db03107..74b9fa045 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/EmailAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package com.firebase.ui.auth.compose.ui.screens
+package com.firebase.ui.auth.compose.ui.screens.email
import android.content.Context
import android.util.Log
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/ResetPasswordUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/ResetPasswordUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/ResetPasswordUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/email/ResetPasswordUI.kt
index 1f8ba5e66..820dcf0aa 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/ResetPasswordUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/ResetPasswordUI.kt
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package com.firebase.ui.auth.compose.ui.screens
+package com.firebase.ui.auth.compose.ui.screens.email
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInEmailLinkUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInEmailLinkUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInEmailLinkUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInEmailLinkUI.kt
index f06667cd2..f8dbb1efa 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInEmailLinkUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInEmailLinkUI.kt
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package com.firebase.ui.auth.compose.ui.screens
+package com.firebase.ui.auth.compose.ui.screens.email
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInUI.kt
index 698b99def..2a9df4307 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignInUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignInUI.kt
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package com.firebase.ui.auth.compose.ui.screens
+package com.firebase.ui.auth.compose.ui.screens.email
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignUpUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignUpUI.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignUpUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignUpUI.kt
index 655b2c5f6..db266af79 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/email/SignUpUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/SignUpUI.kt
@@ -12,7 +12,7 @@
* limitations under the License.
*/
-package com.firebase.ui.auth.compose.ui.screens
+package com.firebase.ui.auth.compose.ui.screens.email
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterPhoneNumberUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/EnterPhoneNumberUI.kt
similarity index 95%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterPhoneNumberUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/EnterPhoneNumberUI.kt
index 2a56ffde4..6a6fc4010 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterPhoneNumberUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/EnterPhoneNumberUI.kt
@@ -14,7 +14,6 @@
package com.firebase.ui.auth.compose.ui.screens.phone
-import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -22,20 +21,16 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
-import androidx.compose.material.icons.filled.CheckBox
-import androidx.compose.material.icons.filled.CheckBoxOutlineBlank
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
@@ -55,10 +50,10 @@ import com.firebase.ui.auth.compose.configuration.string_provider.LocalAuthUIStr
import com.firebase.ui.auth.compose.configuration.theme.AuthUITheme
import com.firebase.ui.auth.compose.configuration.validators.PhoneNumberValidator
import com.firebase.ui.auth.compose.data.CountryData
-import com.firebase.ui.auth.compose.data.CountryUtils
import com.firebase.ui.auth.compose.ui.components.AuthTextField
import com.firebase.ui.auth.compose.ui.components.CountrySelector
import com.firebase.ui.auth.compose.ui.components.TermsAndPrivacyForm
+import com.firebase.ui.auth.compose.util.CountryUtils
@OptIn(ExperimentalMaterial3Api::class)
@Composable
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterVerificationCodeUI.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/EnterVerificationCodeUI.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterVerificationCodeUI.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/EnterVerificationCodeUI.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/PhoneAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
similarity index 99%
rename from auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/PhoneAuthScreen.kt
rename to auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
index 47998014c..d87afa6bc 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/PhoneAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
@@ -37,8 +37,8 @@ import com.firebase.ui.auth.compose.configuration.auth_provider.submitVerificati
import com.firebase.ui.auth.compose.configuration.auth_provider.verifyPhoneNumber
import com.firebase.ui.auth.compose.configuration.string_provider.LocalAuthUIStringProvider
import com.firebase.ui.auth.compose.data.CountryData
-import com.firebase.ui.auth.compose.data.CountryUtils
import com.firebase.ui.auth.compose.ui.components.LocalTopLevelDialogController
+import com.firebase.ui.auth.compose.util.CountryUtils
import com.google.firebase.auth.AuthResult
import com.google.firebase.auth.PhoneAuthProvider
import kotlinx.coroutines.delay
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt b/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt
new file mode 100644
index 000000000..80efbd8bd
--- /dev/null
+++ b/auth/src/main/java/com/firebase/ui/auth/util/ContinueUrlBuilder.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2025 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.util
+
+import androidx.annotation.RestrictTo
+import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.ANONYMOUS_USER_ID_IDENTIFIER
+import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.FORCE_SAME_DEVICE_IDENTIFIER
+import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.PROVIDER_ID_IDENTIFIER
+import com.firebase.ui.auth.util.EmailLinkParser.LinkParameters.SESSION_IDENTIFIER
+
+/**
+ * Builder for constructing continue URLs with embedded session and authentication parameters.
+ * Used in email link sign-in flows to pass state between devices.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class ContinueUrlBuilder(url: String) {
+
+ private val continueUrl: StringBuilder
+
+ init {
+ require(url.isNotBlank()) { "URL cannot be empty" }
+ continueUrl = StringBuilder(url).append("?")
+ }
+
+ fun appendSessionId(sessionId: String): ContinueUrlBuilder {
+ addQueryParam(SESSION_IDENTIFIER, sessionId)
+ return this
+ }
+
+ fun appendAnonymousUserId(anonymousUserId: String): ContinueUrlBuilder {
+ addQueryParam(ANONYMOUS_USER_ID_IDENTIFIER, anonymousUserId)
+ return this
+ }
+
+ fun appendProviderId(providerId: String): ContinueUrlBuilder {
+ addQueryParam(PROVIDER_ID_IDENTIFIER, providerId)
+ return this
+ }
+
+ fun appendForceSameDeviceBit(forceSameDevice: Boolean): ContinueUrlBuilder {
+ val bit = if (forceSameDevice) "1" else "0"
+ addQueryParam(FORCE_SAME_DEVICE_IDENTIFIER, bit)
+ return this
+ }
+
+ private fun addQueryParam(key: String, value: String) {
+ if (value.isBlank()) return
+
+ val isFirstParam = continueUrl.last() == '?'
+ val mark = if (isFirstParam) "" else "&"
+ continueUrl.append("$mark$key=$value")
+ }
+
+ fun build(): String {
+ if (continueUrl.last() == '?') {
+ // No params added so we remove the '?'
+ continueUrl.setLength(continueUrl.length - 1)
+ }
+ return continueUrl.toString()
+ }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/data/CountryUtils.kt b/auth/src/main/java/com/firebase/ui/auth/util/CountryUtils.kt
similarity index 88%
rename from auth/src/main/java/com/firebase/ui/auth/compose/data/CountryUtils.kt
rename to auth/src/main/java/com/firebase/ui/auth/util/CountryUtils.kt
index e71270a33..8516b454f 100644
--- a/auth/src/main/java/com/firebase/ui/auth/compose/data/CountryUtils.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/util/CountryUtils.kt
@@ -1,19 +1,7 @@
-/*
- * Copyright 2025 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.compose.data
+package com.firebase.ui.auth.compose.util
+import com.firebase.ui.auth.compose.data.ALL_COUNTRIES
+import com.firebase.ui.auth.compose.data.CountryData
import java.text.Normalizer
import java.util.Locale
@@ -152,4 +140,4 @@ object CountryUtils {
.replace(Regex("\\p{M}"), "")
.lowercase()
}
-}
+}
\ No newline at end of file
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
deleted file mode 100644
index ca21a9553..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/CredentialUtils.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package com.firebase.ui.auth.util;
-
-import android.net.Uri;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.google.firebase.auth.FirebaseUser;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-/**
- * Utility class for extracting credential data from a {@link FirebaseUser} for the new CredentialManager.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class CredentialUtils {
-
- private static final String TAG = "CredentialUtils";
-
- private CredentialUtils() {
- throw new AssertionError("No instance for you!");
- }
-
- /**
- * Extracts the necessary data from the specified {@link FirebaseUser} along with the user's password.
- *
- * If both the email and phone number are missing or the password is empty, this method returns {@code null}.
- *
- * @param user the FirebaseUser from which to extract data.
- * @param password the password the user signed in with.
- * @return a {@link CredentialData} instance containing the user’s sign-in information, or {@code null} if insufficient data.
- */
- @Nullable
- public static CredentialData buildCredentialData(@NonNull FirebaseUser user,
- @Nullable String password) {
- String email = user.getEmail();
- String phone = user.getPhoneNumber();
- Uri profilePictureUri = (user.getPhotoUrl() != null)
- ? Uri.parse(user.getPhotoUrl().toString())
- : null;
-
- if (TextUtils.isEmpty(email) && TextUtils.isEmpty(phone)) {
- Log.w(TAG, "User has no email or phone number; cannot build credential data.");
- return null;
- }
- if (TextUtils.isEmpty(password)) {
- Log.w(TAG, "Password is required to build credential data.");
- return null;
- }
-
- // Prefer email if available; otherwise fall back to phone.
- String identifier = !TextUtils.isEmpty(email) ? email : phone;
- return new CredentialData(identifier, user.getDisplayName(), password, profilePictureUri);
- }
-
- /**
- * Same as {@link #buildCredentialData(FirebaseUser, String)} but throws an exception if data cannot be built.
- *
- * @param user the FirebaseUser.
- * @param password the password the user signed in with.
- * @return a non-null {@link CredentialData} instance.
- * @throws IllegalStateException if credential data cannot be constructed.
- */
- @NonNull
- public static CredentialData buildCredentialDataOrThrow(@NonNull FirebaseUser user,
- @Nullable String password) {
- CredentialData credentialData = buildCredentialData(user, password);
- if (credentialData == null) {
- throw new IllegalStateException("Unable to build credential data");
- }
- return credentialData;
- }
-
- /**
- * A simple data class representing the information required by the new CredentialManager.
- */
- public static final class CredentialData {
- private final String identifier;
- private final String displayName;
- private final String password;
- private final Uri profilePictureUri;
-
- public CredentialData(String identifier, String displayName, String password, Uri profilePictureUri) {
- this.identifier = identifier;
- this.displayName = displayName;
- this.password = password;
- this.profilePictureUri = profilePictureUri;
- }
-
- public String getIdentifier() {
- return identifier;
- }
-
- public String getDisplayName() {
- return displayName;
- }
-
- public String getPassword() {
- return password;
- }
-
- public Uri getProfilePictureUri() {
- return profilePictureUri;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/util/EmailLinkConstants.kt b/auth/src/main/java/com/firebase/ui/auth/util/EmailLinkConstants.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/util/EmailLinkConstants.kt
rename to auth/src/main/java/com/firebase/ui/auth/util/EmailLinkConstants.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/EmailLinkParser.kt b/auth/src/main/java/com/firebase/ui/auth/util/EmailLinkParser.kt
new file mode 100644
index 000000000..2841344d4
--- /dev/null
+++ b/auth/src/main/java/com/firebase/ui/auth/util/EmailLinkParser.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2025 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.util
+
+import android.net.Uri
+import androidx.annotation.RestrictTo
+import androidx.core.net.toUri
+
+/**
+ * Parser for email link sign-in URLs.
+ * Extracts session information and parameters from Firebase email authentication links.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+class EmailLinkParser(link: String) {
+
+ private val params: Map
+
+ init {
+ require(link.isNotBlank()) { "Link cannot be empty" }
+ params = parseUri(link.toUri())
+ require(params.isNotEmpty()) { "Invalid link: no parameters found" }
+ }
+
+ /**
+ * The out-of-band code (OOB code) from the email link.
+ * This is a required field for email link authentication.
+ * @throws IllegalArgumentException if the OOB code is missing from the link
+ */
+ val oobCode: String
+ get() = requireNotNull(params[OOB_CODE]) {
+ "Invalid email link: missing required OOB code"
+ }
+
+ val sessionId: String?
+ get() = params[LinkParameters.SESSION_IDENTIFIER]
+
+ val anonymousUserId: String?
+ get() = params[LinkParameters.ANONYMOUS_USER_ID_IDENTIFIER]
+
+ val forceSameDeviceBit: Boolean
+ get() {
+ val bit = params[LinkParameters.FORCE_SAME_DEVICE_IDENTIFIER]
+ // Default value is false when no bit is set
+ return bit == "1"
+ }
+
+ val providerId: String?
+ get() = params[LinkParameters.PROVIDER_ID_IDENTIFIER]
+
+ private fun parseUri(uri: Uri): Map {
+ val map = mutableMapOf()
+ try {
+ val queryParameters = uri.queryParameterNames
+ for (param in queryParameters) {
+ if (param.equals(LINK, ignoreCase = true) ||
+ param.equals(CONTINUE_URL, ignoreCase = true)) {
+ val innerUriString = uri.getQueryParameter(param)
+ if (innerUriString != null) {
+ val innerUri = innerUriString.toUri()
+ val innerValues = parseUri(innerUri)
+ map.putAll(innerValues)
+ }
+ } else {
+ val value = uri.getQueryParameter(param)
+ if (value != null) {
+ map[param] = value
+ }
+ }
+ }
+ } catch (e: Exception) {
+ // Do nothing - return what we have
+ }
+ return map
+ }
+
+ object LinkParameters {
+ const val SESSION_IDENTIFIER = "ui_sid"
+ const val ANONYMOUS_USER_ID_IDENTIFIER = "ui_auid"
+ const val FORCE_SAME_DEVICE_IDENTIFIER = "ui_sd"
+ const val PROVIDER_ID_IDENTIFIER = "ui_pid"
+ }
+
+ companion object {
+ private const val LINK = "link"
+ private const val OOB_CODE = "oobCode"
+ private const val CONTINUE_URL = "continueUrl"
+ }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/util/EmailLinkPersistenceManager.kt b/auth/src/main/java/com/firebase/ui/auth/util/EmailLinkPersistenceManager.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/util/EmailLinkPersistenceManager.kt
rename to auth/src/main/java/com/firebase/ui/auth/util/EmailLinkPersistenceManager.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java b/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java
deleted file mode 100644
index 07211a4ea..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ExtraConstants.java
+++ /dev/null
@@ -1,61 +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.util;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * Constants used for passing Intent extra params between authentication flow activities.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class ExtraConstants {
- public static final String FLOW_PARAMS = "extra_flow_params";
- public static final String IDP_RESPONSE = "extra_idp_response";
- public static final String USER = "extra_user";
- public static final String CREDENTIAL = "extra_credential";
-
- public static final String EMAIL = "extra_email";
- public static final String PASSWORD = "extra_password";
- public static final String DEFAULT_EMAIL = "extra_default_email";
- public static final String ALLOW_NEW_EMAILS = "extra_allow_new_emails";
- public static final String REQUIRE_NAME = "extra_require_name";
- public static final String GOOGLE_SIGN_IN_OPTIONS = "extra_google_sign_in_options";
- public static final String FACEBOOK_PERMISSIONS = "extra_facebook_permissions";
- public static final String GITHUB_PERMISSIONS = "extra_github_permissions";
- public static final String GITHUB_URL = "github_url";
-
- public static final String PARAMS = "extra_params";
- public static final String PHONE = "extra_phone_number";
- public static final String COUNTRY_ISO = "extra_country_iso";
- public static final String NATIONAL_NUMBER = "extra_national_number";
-
- public static final String ALLOWLISTED_COUNTRIES = "allowlisted_countries";
- public static final String BLOCKLISTED_COUNTRIES = "blocklisted_countries";
-
- public static final String EMAIL_LINK_SIGN_IN = "email_link_sign_in";
- public static final String ACTION_CODE_SETTINGS = "action_code_settings";
- public static final String FORCE_SAME_DEVICE = "force_same_device";
- public static final String PROVIDER_ID = "provider_id";
-
- public static final String GENERIC_OAUTH_PROVIDER_ID = "generic_oauth_provider_id";
- public static final String GENERIC_OAUTH_PROVIDER_NAME = "generic_oauth_provider_name";
- public static final String GENERIC_OAUTH_BUTTON_ID = "generic_oauth_button_id";
- public static final String GENERIC_OAUTH_SCOPES = "generic_oauth_scopes";
- public static final String GENERIC_OAUTH_CUSTOM_PARAMETERS = "generic_oauth_custom_parameters";
-
- private ExtraConstants() {
- throw new AssertionError("No instance for you!");
- }
-}
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
deleted file mode 100644
index 9c903de5f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/FirebaseAuthError.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.firebase.ui.auth.util;
-
-import com.google.firebase.auth.FirebaseAuthException;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * List of all possible results of {@link FirebaseAuthException#getErrorCode()} and their meanings.
- *
- * This is a temporary band-aid until we have better documentation and exposure for these
- * error codes in the real Firebase Auth SDK.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public enum FirebaseAuthError {
-
- ERROR_INVALID_CUSTOM_TOKEN("The custom token format is incorrect. Please check the documentation."),
-
- ERROR_CUSTOM_TOKEN_MISMATCH("Invalid configuration. Ensure your app's SHA1 is correct in the Firebase console."),
-
- ERROR_INVALID_CREDENTIAL("The supplied auth credential is malformed or has expired."),
-
- ERROR_INVALID_EMAIL("The email address is badly formatted."),
-
- ERROR_WRONG_PASSWORD("The password is invalid or the user does not have a password."),
-
- ERROR_USER_MISMATCH("The supplied credentials do not correspond to the previously signed in user."),
-
- ERROR_REQUIRES_RECENT_LOGIN("This operation is sensitive and requires recent authentication. Log in again before retrying this request."),
-
- ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL("An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address."),
-
- ERROR_EMAIL_ALREADY_IN_USE("The email address is already in use by another account."),
-
- ERROR_CREDENTIAL_ALREADY_IN_USE("This credential is already associated with a different user account."),
-
- ERROR_USER_DISABLED("The user account has been disabled by an administrator."),
-
- ERROR_USER_TOKEN_EXPIRED("The user's credential has expired. The user must sign in again."),
-
- ERROR_USER_NOT_FOUND("There is no user record corresponding to this identifier. The user may have been deleted."),
-
- ERROR_INVALID_USER_TOKEN("The user's credential is no longer valid. The user must sign in again."),
-
- ERROR_OPERATION_NOT_ALLOWED("This operation is not allowed. Enable the sign-in method in the Authentication tab of the Firebase console"),
-
- ERROR_TOO_MANY_REQUESTS("We have blocked all requests from this device due to unusual activity. Try again later."),
-
- ERROR_WEAK_PASSWORD("The given password is too weak, please choose a stronger password."),
-
- ERROR_EXPIRED_ACTION_CODE("The out of band code has expired."),
-
- ERROR_INVALID_ACTION_CODE("The out of band code is invalid. This can happen if the code is malformed, expired, or has already been used."),
-
- ERROR_INVALID_MESSAGE_PAYLOAD("The email template corresponding to this action contains invalid characters in its message. Please fix by going to the Auth email templates section in the Firebase Console."),
-
- ERROR_INVALID_RECIPIENT_EMAIL("The email corresponding to this action failed to send as the provided recipient email address is invalid."),
-
- ERROR_INVALID_SENDER("The email template corresponding to this action contains an invalid sender email or name. Please fix by going to the Auth email templates section in the Firebase Console."),
-
- ERROR_MISSING_EMAIL("An email address must be provided."),
-
- ERROR_MISSING_PASSWORD("A password must be provided."),
-
- ERROR_MISSING_PHONE_NUMBER("To send verification codes, provide a phone number for the recipient."),
-
- ERROR_INVALID_PHONE_NUMBER("The format of the phone number provided is incorrect. Please enter the phone number in a format that can be parsed into E.164 format. E.164 phone numbers are written in the format [+][country code][subscriber number including area code]."),
-
- ERROR_MISSING_VERIFICATION_CODE("The phone auth credential was created with an empty sms verification code"),
-
- ERROR_INVALID_VERIFICATION_CODE("The sms verification code used to create the phone auth credential is invalid. Please resend the verification code sms and be sure use the verification code provided by the user."),
-
- ERROR_MISSING_VERIFICATION_ID("The phone auth credential was created with an empty verification ID"),
-
- ERROR_INVALID_VERIFICATION_ID("The verification ID used to create the phone auth credential is invalid."),
-
- ERROR_RETRY_PHONE_AUTH("An error occurred during authentication using the PhoneAuthCredential. Please retry authentication."),
-
- ERROR_SESSION_EXPIRED("The sms code has expired. Please re-send the verification code to try again."),
-
- ERROR_QUOTA_EXCEEDED("The sms quota for this project has been exceeded."),
-
- ERROR_APP_NOT_AUTHORIZED("This app is not authorized to use Firebase Authentication. Please verify that the correct package name and SHA-1 are configured in the Firebase Console."),
-
- 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.");
-
- /**
- * Get an {@link FirebaseAuthError} from an exception, returning {@link #ERROR_UNKNOWN} as
- * a default.
- */
- public static FirebaseAuthError fromException(FirebaseAuthException ex) {
- try {
- return FirebaseAuthError.valueOf(ex.getErrorCode());
- } catch (IllegalArgumentException e) {
- return FirebaseAuthError.ERROR_UNKNOWN;
- }
- }
-
- private final String description;
-
- FirebaseAuthError(String description) {
- this.description = description;
- }
-
- public String getDescription() {
- return description;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiUtils.java
deleted file mode 100644
index 15d1e0ebb..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/GoogleApiUtils.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package com.firebase.ui.auth.util;
-
-import android.content.Context;
-
-import com.google.android.gms.auth.api.identity.Identity;
-import com.google.android.gms.auth.api.identity.SignInClient;
-import com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GoogleApiAvailability;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.credentials.CredentialManager;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class GoogleApiUtils {
- private GoogleApiUtils() {
- throw new AssertionError("No instance for you!");
- }
-
- public static boolean isPlayServicesAvailable(@NonNull Context context) {
- return GoogleApiAvailability.getInstance()
- .isGooglePlayServicesAvailable(context) == ConnectionResult.SUCCESS;
- }
-
- @NonNull
- public static SignInClient getSignInClient(@NonNull Context context) {
- return Identity.getSignInClient(context);
- }
-
- @NonNull
- public static CredentialManager getCredentialManager(@NonNull Context context) {
- return CredentialManager.create(context);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/compose/util/PersistenceManager.kt b/auth/src/main/java/com/firebase/ui/auth/util/PersistenceManager.kt
similarity index 100%
rename from auth/src/main/java/com/firebase/ui/auth/compose/util/PersistenceManager.kt
rename to auth/src/main/java/com/firebase/ui/auth/util/PersistenceManager.kt
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/PhoneNumberUtils.kt b/auth/src/main/java/com/firebase/ui/auth/util/PhoneNumberUtils.kt
new file mode 100644
index 000000000..5875d23cc
--- /dev/null
+++ b/auth/src/main/java/com/firebase/ui/auth/util/PhoneNumberUtils.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2025 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.util
+
+import androidx.annotation.RestrictTo
+import com.firebase.ui.auth.compose.util.CountryUtils
+
+/**
+ * Phone number validation utilities.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object PhoneNumberUtils {
+
+ /**
+ * Validates if a string starts with a valid dial code.
+ * Accepts dial codes like "+1" or phone numbers like "+14155552671".
+ * Does NOT validate the full phone number, only checks if it starts with a valid country code.
+ *
+ * @param number The dial code or phone number to validate (should start with "+")
+ * @return true if the string starts with a valid country dial code, false otherwise
+ */
+ fun isValid(number: String): Boolean {
+ if (!number.startsWith("+")) return false
+
+ // Try to extract country code from the beginning (1-3 digits)
+ val digitsOnly = number.drop(1).takeWhile { it.isDigit() }
+ if (digitsOnly.isEmpty()) return false
+
+ // Check if any prefix (1-3 digits) is a valid dial code
+ for (length in 1..minOf(3, digitsOnly.length)) {
+ val dialCode = "+${digitsOnly.take(length)}"
+ if (CountryUtils.findByDialCode(dialCode).isNotEmpty()) {
+ return true
+ }
+ }
+ return false
+ }
+
+ /**
+ * Validates if a country ISO code or dial code is valid.
+ * Accepts both ISO codes (e.g., "US", "us") and dial codes (e.g., "+1").
+ *
+ * @param code The ISO 3166-1 alpha-2 country code or E.164 dial code
+ * @return true if the code is a valid ISO code or dial code, false otherwise
+ */
+ fun isValidIso(code: String?): Boolean {
+ if (code == null) return false
+
+ // Check if it's a valid ISO country code (e.g., "US", "GB")
+ if (CountryUtils.findByCountryCode(code) != null) {
+ return true
+ }
+
+ // Check if it's a valid dial code (e.g., "+1", "+44")
+ if (code.startsWith("+")) {
+ return CountryUtils.findByDialCode(code).isNotEmpty()
+ }
+
+ return false
+ }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/Preconditions.java b/auth/src/main/java/com/firebase/ui/auth/util/Preconditions.java
index 0d3d66993..ffd084c30 100644
--- a/auth/src/main/java/com/firebase/ui/auth/util/Preconditions.java
+++ b/auth/src/main/java/com/firebase/ui/auth/util/Preconditions.java
@@ -18,7 +18,7 @@
import android.content.res.Resources;
import android.os.Bundle;
-import com.firebase.ui.auth.AuthUI;
+import com.firebase.ui.auth.compose.FirebaseAuthUI;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -91,7 +91,7 @@ public static void checkConfigured(@NonNull Context context,
@Nullable String message,
@StringRes int... ids) {
for (int id : ids) {
- if (context.getString(id).equals(AuthUI.UNCONFIGURED_CONFIG_VALUE)) {
+ if (context.getString(id).equals(FirebaseAuthUI.UNCONFIGURED_CONFIG_VALUE)) {
throw new IllegalStateException(message);
}
}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ProviderAvailability.kt b/auth/src/main/java/com/firebase/ui/auth/util/ProviderAvailability.kt
new file mode 100644
index 000000000..2c75de102
--- /dev/null
+++ b/auth/src/main/java/com/firebase/ui/auth/util/ProviderAvailability.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2025 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.util
+
+import androidx.annotation.RestrictTo
+
+/**
+ * Utility for checking the availability of authentication providers at runtime.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object ProviderAvailability {
+
+ /**
+ * Checks if Facebook authentication is available.
+ * Returns true if the Facebook SDK is present in the classpath.
+ */
+ val IS_FACEBOOK_AVAILABLE: Boolean = classExists("com.facebook.login.LoginManager")
+
+ private fun classExists(className: String): Boolean {
+ return try {
+ Class.forName(className)
+ true
+ } catch (e: ClassNotFoundException) {
+ false
+ }
+ }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/SessionUtils.kt b/auth/src/main/java/com/firebase/ui/auth/util/SessionUtils.kt
new file mode 100644
index 000000000..c2640a174
--- /dev/null
+++ b/auth/src/main/java/com/firebase/ui/auth/util/SessionUtils.kt
@@ -0,0 +1,25 @@
+package com.firebase.ui.auth.util
+
+import androidx.annotation.RestrictTo
+import kotlin.random.Random
+
+/**
+ * Utility for generating random session identifiers.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+object SessionUtils {
+
+ private const val VALID_CHARS = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+
+ /**
+ * Generates a random alphanumeric string.
+ *
+ * @param length The desired length of the generated string.
+ * @return A randomly generated string with the desired number of characters.
+ */
+ fun generateRandomAlphaNumericString(length: Int): String {
+ return (1..length)
+ .map { VALID_CHARS[Random.nextInt(VALID_CHARS.length)] }
+ .joinToString("")
+ }
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/AuthOperationManager.java b/auth/src/main/java/com/firebase/ui/auth/util/data/AuthOperationManager.java
deleted file mode 100644
index db016ccef..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/AuthOperationManager.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-import com.google.android.gms.tasks.Continuation;
-import com.google.android.gms.tasks.Task;
-import com.google.firebase.FirebaseApp;
-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.OAuthProvider;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-
-/**
- * Utilities to help with Anonymous user upgrade.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class AuthOperationManager {
-
- private static String firebaseAppName = "FUIScratchApp";
-
- private static AuthOperationManager mAuthManager;
-
- @VisibleForTesting
- public FirebaseAuth mScratchAuth;
-
- private AuthOperationManager() {}
-
- public static synchronized AuthOperationManager getInstance() {
- if (mAuthManager == null) {
- mAuthManager = new AuthOperationManager();
- }
- return mAuthManager;
- }
-
- private FirebaseApp getScratchApp(FirebaseApp defaultApp) {
- try {
- return FirebaseApp.getInstance(firebaseAppName);
- } catch (IllegalStateException e) {
- return FirebaseApp.initializeApp(defaultApp.getApplicationContext(),
- defaultApp.getOptions(), firebaseAppName);
- }
- }
-
- private FirebaseAuth getScratchAuth(FlowParameters flowParameters) {
- // Use a different FirebaseApp so that the anonymous user state is not lost in our
- // original FirebaseAuth instance.
- if (mScratchAuth == null) {
- AuthUI authUI = AuthUI.getInstance(flowParameters.appName);
- mScratchAuth = FirebaseAuth.getInstance(getScratchApp(authUI.getApp()));
-
- if (authUI.isUseEmulator()) {
- mScratchAuth.useEmulator(authUI.getEmulatorHost(), authUI.getEmulatorPort());
- }
- }
- return mScratchAuth;
- }
-
- public Task createOrLinkUserWithEmailAndPassword(@NonNull FirebaseAuth auth,
- @NonNull FlowParameters flowParameters,
- @NonNull String email,
- @NonNull String password) {
- if (canUpgradeAnonymous(auth, flowParameters)) {
- AuthCredential credential = EmailAuthProvider.getCredential(email, password);
- return auth.getCurrentUser().linkWithCredential(credential);
- } else {
- return auth.createUserWithEmailAndPassword(email, password);
- }
- }
-
- public Task signInAndLinkWithCredential(@NonNull FirebaseAuth auth,
- @NonNull FlowParameters flowParameters,
- @NonNull AuthCredential credential) {
- if (canUpgradeAnonymous(auth, flowParameters)) {
- return auth.getCurrentUser().linkWithCredential(credential);
- } else {
- return auth.signInWithCredential(credential);
- }
- }
-
- public boolean canUpgradeAnonymous(FirebaseAuth auth, FlowParameters flowParameters) {
- return flowParameters.isAnonymousUpgradeEnabled() && auth.getCurrentUser() != null &&
- auth.getCurrentUser().isAnonymous();
- }
-
- @NonNull
- public Task validateCredential(AuthCredential credential,
- FlowParameters flowParameters) {
- return getScratchAuth(flowParameters).signInWithCredential(credential);
- }
-
- public Task safeLink(final AuthCredential credential,
- final AuthCredential credentialToLink,
- final FlowParameters flowParameters) {
- return getScratchAuth(flowParameters)
- .signInWithCredential(credential)
- .continueWithTask(task -> {
- if (task.isSuccessful()) {
- return task.getResult().getUser().linkWithCredential(credentialToLink);
- }
- return task;
- });
- }
-
- @NonNull
- public Task safeGenericIdpSignIn(@NonNull final HelperActivityBase activity,
- @NonNull final OAuthProvider provider,
- @NonNull final FlowParameters flowParameters) {
- return getScratchAuth(flowParameters)
- .startActivityForSignInWithProvider(activity, provider);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/ContinueUrlBuilder.java b/auth/src/main/java/com/firebase/ui/auth/util/data/ContinueUrlBuilder.java
deleted file mode 100644
index ab9bda09b..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/ContinueUrlBuilder.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import android.text.TextUtils;
-
-import com.google.android.gms.common.internal.Preconditions;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import static com.firebase.ui.auth.util.data.EmailLinkParser.LinkParameters.ANONYMOUS_USER_ID_IDENTIFIER;
-import static com.firebase.ui.auth.util.data.EmailLinkParser.LinkParameters.FORCE_SAME_DEVICE_IDENTIFIER;
-import static com.firebase.ui.auth.util.data.EmailLinkParser.LinkParameters.PROVIDER_ID_IDENTIFIER;
-import static com.firebase.ui.auth.util.data.EmailLinkParser.LinkParameters.SESSION_IDENTIFIER;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ContinueUrlBuilder {
-
- private StringBuilder mContinueUrl;
-
- public ContinueUrlBuilder(@NonNull String url) {
- Preconditions.checkNotEmpty(url);
- mContinueUrl = new StringBuilder(url + "?");
- }
-
- public ContinueUrlBuilder appendSessionId(@NonNull String sessionId) {
- addQueryParam(SESSION_IDENTIFIER, sessionId);
- return this;
- }
-
- public ContinueUrlBuilder appendAnonymousUserId(@NonNull String anonymousUserId) {
- addQueryParam(ANONYMOUS_USER_ID_IDENTIFIER, anonymousUserId);
- return this;
- }
-
- public ContinueUrlBuilder appendProviderId(@NonNull String providerId) {
- addQueryParam(PROVIDER_ID_IDENTIFIER, providerId);
- return this;
- }
-
- public ContinueUrlBuilder appendForceSameDeviceBit(@NonNull boolean forceSameDevice) {
- String bit = forceSameDevice ? "1" : "0";
- addQueryParam(FORCE_SAME_DEVICE_IDENTIFIER, bit);
- return this;
- }
-
- private void addQueryParam(String key, String value) {
- if (TextUtils.isEmpty(value)) {
- return;
- }
- boolean isFirstParam = mContinueUrl.charAt(mContinueUrl.length() - 1) == '?';
- String mark = isFirstParam ? "" : "&";
- mContinueUrl.append(String.format("%s%s=%s", mark, key, value));
- }
-
- public String build() {
- if (mContinueUrl.charAt(mContinueUrl.length() - 1) == '?') {
- // No params added so we remove the '?'
- mContinueUrl.setLength(mContinueUrl.length() - 1);
- }
- return mContinueUrl.toString();
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkParser.java b/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkParser.java
deleted file mode 100644
index 67c12d24c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkParser.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.google.android.gms.common.internal.Preconditions;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Set;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkParser {
-
- private static String LINK = "link";
-
- private static final String OOB_CODE = "oobCode";
- private static final String CONTINUE_URL = "continueUrl";
-
- private Map mParams;
-
- public EmailLinkParser(@NonNull String link) {
- Preconditions.checkNotEmpty(link);
- mParams = parseUri(Uri.parse(link));
- if (mParams.isEmpty()) {
- throw new IllegalArgumentException("Invalid link: no parameters found");
- }
- }
-
- public String getOobCode() {
- return mParams.get(OOB_CODE);
- }
-
- public String getSessionId() {
- return mParams.get(LinkParameters.SESSION_IDENTIFIER);
- }
-
- public String getAnonymousUserId() {
- return mParams.get(LinkParameters.ANONYMOUS_USER_ID_IDENTIFIER);
- }
-
- public boolean getForceSameDeviceBit() {
- String forceSameDeviceBit = mParams.get(LinkParameters.FORCE_SAME_DEVICE_IDENTIFIER);
- if (TextUtils.isEmpty(forceSameDeviceBit)) {
- // Default value is false when no bit is set
- return false;
- }
- return forceSameDeviceBit.equals("1");
- }
-
- public String getProviderId() {
- return mParams.get(LinkParameters.PROVIDER_ID_IDENTIFIER);
- }
-
- private Map parseUri(Uri uri) {
- Map map = new HashMap<>();
- try {
- Set queryParameters = uri.getQueryParameterNames();
- for (String param : queryParameters) {
- if (param.equalsIgnoreCase(LINK) || param.equalsIgnoreCase(CONTINUE_URL)) {
- Uri innerUri = Uri.parse(uri.getQueryParameter(param));
- Map innerValues = parseUri(innerUri);
- if (innerValues != null) {
- map.putAll(innerValues);
- }
- } else {
- String value = uri.getQueryParameter(param);
- if (value != null) {
- map.put(param, value);
- }
- }
- }
- } catch (Exception e) {
- // Do nothing.
- }
- return map;
- }
-
- public static class LinkParameters {
- public static final String SESSION_IDENTIFIER = "ui_sid";
- public static final String ANONYMOUS_USER_ID_IDENTIFIER = "ui_auid";
- public static final String FORCE_SAME_DEVICE_IDENTIFIER = "ui_sd";
- public static final String PROVIDER_ID_IDENTIFIER = "ui_pid";
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkPersistenceManager.java b/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkPersistenceManager.java
deleted file mode 100644
index f52a32de5..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/EmailLinkPersistenceManager.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.User;
-import com.google.android.gms.common.internal.Preconditions;
-import com.google.firebase.auth.AuthCredential;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-
-/** Manages saving/retrieving from SharedPreferences for email link sign in. */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailLinkPersistenceManager {
-
- private static final String SHARED_PREF_NAME =
- "com.firebase.ui.auth.util.data.EmailLinkPersistenceManager";
-
- private static final String KEY_EMAIL = "com.firebase.ui.auth.data.client.email";
- private static final String KEY_PROVIDER = "com.firebase.ui.auth.data.client.provider";
- private static final String KEY_IDP_TOKEN = "com.firebase.ui.auth.data.client.idpToken";
- private static final String KEY_IDP_SECRET = "com.firebase.ui.auth.data.client.idpSecret";
- private static final String KEY_ANONYMOUS_USER_ID = "com.firebase.ui.auth.data.client.auid";
- private static final String KEY_SESSION_ID = "com.firebase.ui.auth.data.client.sid";
-
- private static final Set KEYS =
- Collections.unmodifiableSet(new HashSet<>(Arrays.asList(KEY_EMAIL, KEY_PROVIDER,
- KEY_IDP_TOKEN, KEY_IDP_SECRET)));
-
- private static final EmailLinkPersistenceManager instance = new EmailLinkPersistenceManager();
-
- public static EmailLinkPersistenceManager getInstance() {
- return instance;
- }
-
- private AuthCredential mCredentialForLinking;
-
- public void saveEmail(@NonNull Context context,
- @NonNull String email,
- @NonNull String sessionId,
- @Nullable String anonymousUserId) {
- Preconditions.checkNotNull(context);
- Preconditions.checkNotNull(email);
- SharedPreferences.Editor editor =
- context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit();
- editor.putString(KEY_EMAIL, email);
- editor.putString(KEY_ANONYMOUS_USER_ID, anonymousUserId);
- editor.putString(KEY_SESSION_ID, sessionId);
- editor.apply();
- }
-
- public void saveIdpResponseForLinking(@NonNull Context context,
- @NonNull IdpResponse idpResponseForLinking) {
- if (idpResponseForLinking.hasCredentialForLinking()) {
- // Signing in via Generic IDP returns a credential that must be used for sign-in.
- // We can't sign-in by rebuilding using the id token/access token, so we need to use the
- // credential directly.
- // We also can't store this credential in SharedPrefs, so this is just
- // best effort.
- mCredentialForLinking = idpResponseForLinking.getCredentialForLinking();
- }
- Preconditions.checkNotNull(context);
- Preconditions.checkNotNull(idpResponseForLinking);
- SharedPreferences.Editor editor =
- context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE).edit();
- editor.putString(KEY_EMAIL, idpResponseForLinking.getEmail());
- editor.putString(KEY_PROVIDER, idpResponseForLinking.getProviderType());
- editor.putString(KEY_IDP_TOKEN, idpResponseForLinking.getIdpToken());
- editor.putString(KEY_IDP_SECRET, idpResponseForLinking.getIdpSecret());
- editor.apply();
- }
-
- @Nullable
- public SessionRecord retrieveSessionRecord(@NonNull Context context) {
- Preconditions.checkNotNull(context);
- SharedPreferences sharedPreferences =
- context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
- String email = sharedPreferences.getString(KEY_EMAIL, null);
- String sessionId = sharedPreferences.getString(KEY_SESSION_ID, null);
- if (email == null || sessionId == null) {
- return null;
- }
- String anonymousUserId = sharedPreferences.getString(KEY_ANONYMOUS_USER_ID, null);
- String provider = sharedPreferences.getString(KEY_PROVIDER, null);
- String idpToken = sharedPreferences.getString(KEY_IDP_TOKEN, null);
- String idpSecret = sharedPreferences.getString(KEY_IDP_SECRET, null);
-
- SessionRecord sessionRecord = new SessionRecord(sessionId, anonymousUserId).setEmail(email);
- if (provider != null && (idpToken != null || mCredentialForLinking != null)) {
- IdpResponse response = new IdpResponse.Builder(
- new User.Builder(provider, email).build())
- .setPendingCredential(mCredentialForLinking)
- .setToken(idpToken)
- .setSecret(idpSecret)
- .setNewUser(false)
- .build();
- sessionRecord.setIdpResponseForLinking(response);
- }
- mCredentialForLinking = null;
- return sessionRecord;
- }
-
- public void clearAllData(@NonNull Context context) {
- Preconditions.checkNotNull(context);
- SharedPreferences sharedPreferences =
- context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sharedPreferences.edit();
- for (String key : KEYS) {
- editor.remove(key);
- }
- editor.apply();
- }
-
- /** Holds the necessary information to complete the email link sign in flow */
- public static class SessionRecord {
- private String mSessionId;
- private String mEmail;
- @Nullable private String mAnonymousUserId;
- @Nullable private IdpResponse mIdpResponseForLinking;
-
- public SessionRecord(@NonNull String sessionId,
- @Nullable String anonymousUserId) {
- Preconditions.checkNotNull(sessionId);
- this.mSessionId = sessionId;
- this.mAnonymousUserId = anonymousUserId;
- }
-
- public String getSessionId() {
- return mSessionId;
- }
-
- public String getEmail() {
- return mEmail;
- }
-
- public SessionRecord setEmail(@NonNull String email) {
- this.mEmail = email;
- return this;
- }
-
- @Nullable
- public IdpResponse getIdpResponseForLinking() {
- return mIdpResponseForLinking;
- }
-
- public SessionRecord setIdpResponseForLinking(@NonNull IdpResponse idpResponseForLinking) {
- this.mIdpResponseForLinking = idpResponseForLinking;
- return this;
- }
-
- @Nullable
- public String getAnonymousUserId() {
- return mAnonymousUserId;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java
deleted file mode 100644
index ce2945b77..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/PhoneNumberUtils.java
+++ /dev/null
@@ -1,478 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- */
-package com.firebase.ui.auth.util.data;
-
-import android.content.Context;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.SparseArray;
-
-import com.firebase.ui.auth.data.model.CountryInfo;
-import com.firebase.ui.auth.data.model.PhoneNumber;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class PhoneNumberUtils {
- private static final int DEFAULT_COUNTRY_CODE_INT = 1;
- private static final String DEFAULT_COUNTRY_CODE = String.valueOf(DEFAULT_COUNTRY_CODE_INT);
- private static final Locale DEFAULT_LOCALE = Locale.US;
- private static final CountryInfo DEFAULT_COUNTRY =
- new CountryInfo(DEFAULT_LOCALE, DEFAULT_COUNTRY_CODE_INT);
-
- private static final int MAX_COUNTRY_CODES = 215;
- private static final int MAX_COUNTRIES = 248;
- private static final int MAX_LENGTH_COUNTRY_CODE = 3;
-
- private static final SparseArray> COUNTRY_TO_REGION_CODES =
- createCountryCodeToRegionCodeMap();
-
- private static Map COUNTRY_TO_ISO_CODES;
-
- /**
- * This method works as follow:
When the android version is LOLLIPOP or greater, the
- * reliable {{@link android.telephony.PhoneNumberUtils#formatNumberToE164}} is used to
- * format.
For lower versions, we construct a value with the input phone number
- * stripped of non numeric characters and prefix it with a "+" and country code
- *
- * @param phoneNumber that may or may not itself have country code
- * @param countryInfo must have locale with ISO 3166 2-letter code for country
- */
- public static String format(@NonNull String phoneNumber, @NonNull CountryInfo countryInfo) {
- if (phoneNumber.startsWith("+")) {
- return phoneNumber;
- } else {
- return "+"
- + String.valueOf(countryInfo.getCountryCode())
- + phoneNumber.replaceAll("[^\\d.]", "");
- }
- }
-
- /**
- * This method uses the country returned by {@link #getCurrentCountryInfo(Context)} to format
- * the phone number. Internally invokes {@link #format(String, CountryInfo)}
- *
- * @param phoneNumber that may or may not itself have country code
- */
- @Nullable
- public static String formatUsingCurrentCountry(@NonNull String phoneNumber, Context context) {
- return format(phoneNumber, getCurrentCountryInfo(context));
- }
-
- @NonNull
- public static CountryInfo getCurrentCountryInfo(@NonNull Context context) {
- Locale locale = getSimBasedLocale(context);
-
- if (locale == null) {
- locale = getOSLocale();
- }
-
- if (locale == null) {
- return DEFAULT_COUNTRY;
- }
-
- Integer countryCode = getCountryCode(locale.getCountry());
-
- return countryCode == null ? DEFAULT_COUNTRY : new CountryInfo(locale, countryCode);
- }
-
- /**
- * This method should not be called on UI thread. Potentially creates a country code by iso map
- * which can take long in some devices
- *
- * @param providedPhoneNumber works best when formatted as e164
- * @return an instance of the PhoneNumber using the SIM information
- */
- public static PhoneNumber getPhoneNumber(@NonNull String providedPhoneNumber) {
- String countryCode = DEFAULT_COUNTRY_CODE;
- String countryIso = DEFAULT_LOCALE.getCountry();
-
- String phoneNumber = providedPhoneNumber;
- if (providedPhoneNumber.startsWith("+")) {
- countryCode = getCountryCodeForPhoneNumberOrDefault(providedPhoneNumber);
- countryIso = getCountryIsoForCountryCode(countryCode);
- phoneNumber = stripCountryCode(providedPhoneNumber, countryCode);
- }
-
- return new PhoneNumber(phoneNumber, countryIso, countryCode);
- }
-
- public static boolean isValid(@NonNull String number) {
- return number.startsWith("+") && getCountryCodeForPhoneNumber(number) != null;
- }
-
- public static boolean isValidIso(@Nullable String iso) {
- return getCountryCode(iso) != null;
- }
-
- /**
- * @see #getPhoneNumber(String)
- */
- public static PhoneNumber getPhoneNumber(
- @NonNull String providedCountryIso, @NonNull String providedNationalNumber) {
- Integer countryCode = getCountryCode(providedCountryIso);
- if (countryCode == null) {
- // Invalid ISO supplied:
- countryCode = DEFAULT_COUNTRY_CODE_INT;
- providedCountryIso = DEFAULT_COUNTRY_CODE;
- }
-
- // National number shouldn't include '+', but just in case:
- providedNationalNumber = stripPlusSign(providedNationalNumber);
-
- return new PhoneNumber(
- providedNationalNumber, providedCountryIso, String.valueOf(countryCode));
- }
-
- @Nullable
- public static Integer getCountryCode(String countryIso) {
- if (COUNTRY_TO_ISO_CODES == null) {
- initCountryCodeByIsoMap();
- }
- return countryIso == null
- ? null : COUNTRY_TO_ISO_CODES.get(countryIso.toUpperCase(Locale.getDefault()));
- }
-
- public static Map getImmutableCountryIsoMap() {
- if (COUNTRY_TO_ISO_CODES == null) {
- initCountryCodeByIsoMap();
- }
- return COUNTRY_TO_ISO_CODES;
- }
-
- private static String getCountryIsoForCountryCode(String countryCode) {
- List countries = COUNTRY_TO_REGION_CODES.get(Integer.parseInt(countryCode));
- if (countries != null) {
- return countries.get(0);
- }
- return DEFAULT_LOCALE.getCountry();
- }
-
- @Nullable
- public static List getCountryIsosFromCountryCode(String countryCode) {
- return !isValid(countryCode) ? null :
- COUNTRY_TO_REGION_CODES.get(Integer.parseInt(countryCode.substring(1)));
- }
-
- /**
- * Country code extracted using shortest matching prefix like libPhoneNumber. See:
- * https://github.com/googlei18n/libphonenumber/blob/master/java/libphonenumber/src/com
- * /google/i18n/phonenumbers/PhoneNumberUtil.java#L2395
- */
- @Nullable
- private static String getCountryCodeForPhoneNumber(String normalizedPhoneNumber) {
- String phoneWithoutPlusPrefix = normalizedPhoneNumber.replaceFirst("^\\+", "");
- int numberLength = phoneWithoutPlusPrefix.length();
-
- for (int i = 1; i <= MAX_LENGTH_COUNTRY_CODE && i <= numberLength; i++) {
- String potentialCountryCode = phoneWithoutPlusPrefix.substring(0, i);
- Integer countryCodeKey = Integer.valueOf(potentialCountryCode);
-
- if (COUNTRY_TO_REGION_CODES.indexOfKey(countryCodeKey) >= 0) {
- return potentialCountryCode;
- }
- }
- return null;
- }
-
- @NonNull
- private static String getCountryCodeForPhoneNumberOrDefault(String normalizedPhoneNumber) {
- String code = getCountryCodeForPhoneNumber(normalizedPhoneNumber);
- return code == null ? DEFAULT_COUNTRY_CODE : code;
- }
-
- private static String stripCountryCode(String phoneNumber, String countryCode) {
- return phoneNumber.replaceFirst("^\\+?" + countryCode, "");
- }
-
- private static String stripPlusSign(String phoneNumber) {
- return phoneNumber.replaceFirst("^\\+?", "");
- }
-
- private static Locale getSimBasedLocale(@NonNull Context context) {
- TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- String countryIso = tm != null ? tm.getSimCountryIso() : null;
- return TextUtils.isEmpty(countryIso) ? null : new Locale("", countryIso);
- }
-
- private static Locale getOSLocale() {
- return Locale.getDefault();
- }
-
- private static SparseArray> createCountryCodeToRegionCodeMap() {
- SparseArray> map = new SparseArray<>(MAX_COUNTRY_CODES);
-
- map.put(1, asList(
- "US", "AG", "AI", "AS", "BB", "BM", "BS", "CA", "DM", "DO", "GD", "GU", "JM", "KN",
- "KY", "LC", "MP", "MS", "PR", "SX", "TC", "TT", "VC", "VG", "VI"));
- map.put(7, asList("RU", "KZ"));
- map.put(20, singletonList("EG"));
- map.put(27, singletonList("ZA"));
- map.put(30, singletonList("GR"));
- map.put(31, singletonList("NL"));
- map.put(32, singletonList("BE"));
- map.put(33, singletonList("FR"));
- map.put(34, singletonList("ES"));
- map.put(36, singletonList("HU"));
- map.put(39, singletonList("IT"));
- map.put(40, singletonList("RO"));
- map.put(41, singletonList("CH"));
- map.put(43, singletonList("AT"));
- map.put(44, asList("GB", "GG", "IM", "JE"));
- map.put(45, singletonList("DK"));
- map.put(46, singletonList("SE"));
- map.put(47, asList("NO", "SJ"));
- map.put(48, singletonList("PL"));
- map.put(49, singletonList("DE"));
- map.put(51, singletonList("PE"));
- map.put(52, singletonList("MX"));
- map.put(53, singletonList("CU"));
- map.put(54, singletonList("AR"));
- map.put(55, singletonList("BR"));
- map.put(56, singletonList("CL"));
- map.put(57, singletonList("CO"));
- map.put(58, singletonList("VE"));
- map.put(60, singletonList("MY"));
- map.put(61, asList("AU", "CC", "CX"));
- map.put(62, singletonList("ID"));
- map.put(63, singletonList("PH"));
- map.put(64, singletonList("NZ"));
- map.put(65, singletonList("SG"));
- map.put(66, singletonList("TH"));
- map.put(81, singletonList("JP"));
- map.put(82, singletonList("KR"));
- map.put(84, singletonList("VN"));
- map.put(86, singletonList("CN"));
- map.put(90, singletonList("TR"));
- map.put(91, singletonList("IN"));
- map.put(92, singletonList("PK"));
- map.put(93, singletonList("AF"));
- map.put(94, singletonList("LK"));
- map.put(95, singletonList("MM"));
- map.put(98, singletonList("IR"));
- map.put(211, singletonList("SS"));
- map.put(212, asList("MA", "EH"));
- map.put(213, singletonList("DZ"));
- map.put(216, singletonList("TN"));
- map.put(218, singletonList("LY"));
- map.put(220, singletonList("GM"));
- map.put(221, singletonList("SN"));
- map.put(222, singletonList("MR"));
- map.put(223, singletonList("ML"));
- map.put(224, singletonList("GN"));
- map.put(225, singletonList("CI"));
- map.put(226, singletonList("BF"));
- map.put(227, singletonList("NE"));
- map.put(228, singletonList("TG"));
- map.put(229, singletonList("BJ"));
- map.put(230, singletonList("MU"));
- map.put(231, singletonList("LR"));
- map.put(232, singletonList("SL"));
- map.put(233, singletonList("GH"));
- map.put(234, singletonList("NG"));
- map.put(235, singletonList("TD"));
- map.put(236, singletonList("CF"));
- map.put(237, singletonList("CM"));
- map.put(238, singletonList("CV"));
- map.put(239, singletonList("ST"));
- map.put(240, singletonList("GQ"));
- map.put(241, singletonList("GA"));
- map.put(242, singletonList("CG"));
- map.put(243, singletonList("CD"));
- map.put(244, singletonList("AO"));
- map.put(245, singletonList("GW"));
- map.put(246, singletonList("IO"));
- map.put(247, singletonList("AC"));
- map.put(248, singletonList("SC"));
- map.put(249, singletonList("SD"));
- map.put(250, singletonList("RW"));
- map.put(251, singletonList("ET"));
- map.put(252, singletonList("SO"));
- map.put(253, singletonList("DJ"));
- map.put(254, singletonList("KE"));
- map.put(255, singletonList("TZ"));
- map.put(256, singletonList("UG"));
- map.put(257, singletonList("BI"));
- map.put(258, singletonList("MZ"));
- map.put(260, singletonList("ZM"));
- map.put(261, singletonList("MG"));
- map.put(262, asList("RE", "YT"));
- map.put(263, singletonList("ZW"));
- map.put(264, singletonList("NA"));
- map.put(265, singletonList("MW"));
- map.put(266, singletonList("LS"));
- map.put(267, singletonList("BW"));
- map.put(268, singletonList("SZ"));
- map.put(269, singletonList("KM"));
- map.put(290, asList("SH", "TA"));
- map.put(291, singletonList("ER"));
- map.put(297, singletonList("AW"));
- map.put(298, singletonList("FO"));
- map.put(299, singletonList("GL"));
- map.put(350, singletonList("GI"));
- map.put(351, singletonList("PT"));
- map.put(352, singletonList("LU"));
- map.put(353, singletonList("IE"));
- map.put(354, singletonList("IS"));
- map.put(355, singletonList("AL"));
- map.put(356, singletonList("MT"));
- map.put(357, singletonList("CY"));
- map.put(358, asList("FI", "AX"));
- map.put(359, singletonList("BG"));
- map.put(370, singletonList("LT"));
- map.put(371, singletonList("LV"));
- map.put(372, singletonList("EE"));
- map.put(373, singletonList("MD"));
- map.put(374, singletonList("AM"));
- map.put(375, singletonList("BY"));
- map.put(376, singletonList("AD"));
- map.put(377, singletonList("MC"));
- map.put(378, singletonList("SM"));
- map.put(379, singletonList("VA"));
- map.put(380, singletonList("UA"));
- map.put(381, singletonList("RS"));
- map.put(382, singletonList("ME"));
- map.put(383, singletonList("XK"));
- map.put(385, singletonList("HR"));
- map.put(386, singletonList("SI"));
- map.put(387, singletonList("BA"));
- map.put(389, singletonList("MK"));
- map.put(420, singletonList("CZ"));
- map.put(421, singletonList("SK"));
- map.put(423, singletonList("LI"));
- map.put(500, singletonList("FK"));
- map.put(501, singletonList("BZ"));
- map.put(502, singletonList("GT"));
- map.put(503, singletonList("SV"));
- map.put(504, singletonList("HN"));
- map.put(505, singletonList("NI"));
- map.put(506, singletonList("CR"));
- map.put(507, singletonList("PA"));
- map.put(508, singletonList("PM"));
- map.put(509, singletonList("HT"));
- map.put(590, asList("GP", "BL", "MF"));
- map.put(591, singletonList("BO"));
- map.put(592, singletonList("GY"));
- map.put(593, singletonList("EC"));
- map.put(594, singletonList("GF"));
- map.put(595, singletonList("PY"));
- map.put(596, singletonList("MQ"));
- map.put(597, singletonList("SR"));
- map.put(598, singletonList("UY"));
- map.put(599, asList("CW", "BQ"));
- map.put(670, singletonList("TL"));
- map.put(672, singletonList("NF"));
- map.put(673, singletonList("BN"));
- map.put(674, singletonList("NR"));
- map.put(675, singletonList("PG"));
- map.put(676, singletonList("TO"));
- map.put(677, singletonList("SB"));
- map.put(678, singletonList("VU"));
- map.put(679, singletonList("FJ"));
- map.put(680, singletonList("PW"));
- map.put(681, singletonList("WF"));
- map.put(682, singletonList("CK"));
- map.put(683, singletonList("NU"));
- map.put(685, singletonList("WS"));
- map.put(686, singletonList("KI"));
- map.put(687, singletonList("NC"));
- map.put(688, singletonList("TV"));
- map.put(689, singletonList("PF"));
- map.put(690, singletonList("TK"));
- map.put(691, singletonList("FM"));
- map.put(692, singletonList("MH"));
- map.put(800, singletonList("001"));
- map.put(808, singletonList("001"));
- map.put(850, singletonList("KP"));
- map.put(852, singletonList("HK"));
- map.put(853, singletonList("MO"));
- map.put(855, singletonList("KH"));
- map.put(856, singletonList("LA"));
- map.put(870, singletonList("001"));
- map.put(878, singletonList("001"));
- map.put(880, singletonList("BD"));
- map.put(881, singletonList("001"));
- map.put(882, singletonList("001"));
- map.put(883, singletonList("001"));
- map.put(886, singletonList("TW"));
- map.put(888, singletonList("001"));
- map.put(960, singletonList("MV"));
- map.put(961, singletonList("LB"));
- map.put(962, singletonList("JO"));
- map.put(963, singletonList("SY"));
- map.put(964, singletonList("IQ"));
- map.put(965, singletonList("KW"));
- map.put(966, singletonList("SA"));
- map.put(967, singletonList("YE"));
- map.put(968, singletonList("OM"));
- map.put(970, singletonList("PS"));
- map.put(971, singletonList("AE"));
- map.put(972, singletonList("IL"));
- map.put(973, singletonList("BH"));
- map.put(974, singletonList("QA"));
- map.put(975, singletonList("BT"));
- map.put(976, singletonList("MN"));
- map.put(977, singletonList("NP"));
- map.put(979, singletonList("001"));
- map.put(992, singletonList("TJ"));
- map.put(993, singletonList("TM"));
- map.put(994, singletonList("AZ"));
- map.put(995, singletonList("GE"));
- map.put(996, singletonList("KG"));
- map.put(998, singletonList("UZ"));
-
- return map;
- }
-
- private static void initCountryCodeByIsoMap() {
- Map map = new HashMap<>(MAX_COUNTRIES);
-
- for (int i = 0; i < COUNTRY_TO_REGION_CODES.size(); i++) {
- int code = COUNTRY_TO_REGION_CODES.keyAt(i);
- List regions = COUNTRY_TO_REGION_CODES.get(code);
-
- for (String region : regions) {
- if (region.equals("001")) { continue; }
- if (map.containsKey(region)) {
- throw new IllegalStateException("Duplicate regions for country code: " + code);
- }
-
- map.put(region, code);
- }
- }
-
- // TODO Figure out why these exceptions exist.
- // This map used to be hardcoded so this is the diff from the generated version.
- map.remove("TA");
- map.put("HM", 672);
- map.put("GS", 500);
-
- COUNTRY_TO_ISO_CODES = Collections.unmodifiableMap(map);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/PrivacyDisclosureUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/PrivacyDisclosureUtils.java
deleted file mode 100644
index 38d8164df..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/PrivacyDisclosureUtils.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import android.content.Context;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.util.ui.PreambleHandler;
-
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PrivacyDisclosureUtils {
-
- private static final int NO_TOS_OR_PP = -1;
-
- public static void setupTermsOfServiceAndPrivacyPolicyText(Context context,
- FlowParameters flowParameters,
- TextView termsText) {
- PreambleHandler.setup(context,
- flowParameters,
- getGlobalTermsStringResource(flowParameters),
- termsText);
- }
-
- public static void setupTermsOfServiceFooter(Context context,
- FlowParameters flowParameters,
- TextView footerText) {
- PreambleHandler.setup(context,
- flowParameters,
- getGlobalTermsFooterStringResource(flowParameters),
- footerText);
- }
-
- public static void setupTermsOfServiceAndPrivacyPolicySmsText(Context context,
- FlowParameters flowParameters,
- TextView termsText) {
- PreambleHandler.setup(context,
- flowParameters,
- R.string.fui_verify_phone_number,
- getTermsSmsStringResource(flowParameters),
- termsText);
- }
-
- @StringRes
- private static int getGlobalTermsStringResource(FlowParameters flowParameters) {
- boolean termsOfServiceUrlProvided = flowParameters.isTermsOfServiceUrlProvided();
- boolean privacyPolicyUrlProvided = flowParameters.isPrivacyPolicyUrlProvided();
-
- if (termsOfServiceUrlProvided && privacyPolicyUrlProvided) {
- return R.string.fui_tos_and_pp;
- }
-
- return NO_TOS_OR_PP;
- }
-
- @StringRes
- private static int getGlobalTermsFooterStringResource(FlowParameters flowParameters) {
- boolean termsOfServiceUrlProvided = flowParameters.isTermsOfServiceUrlProvided();
- boolean privacyPolicyUrlProvided = flowParameters.isPrivacyPolicyUrlProvided();
-
- if (termsOfServiceUrlProvided && privacyPolicyUrlProvided) {
- return R.string.fui_tos_and_pp_footer;
- }
-
- return NO_TOS_OR_PP;
- }
-
- @StringRes
- private static int getTermsSmsStringResource(FlowParameters flowParameters) {
- boolean termsOfServiceUrlProvided = flowParameters.isTermsOfServiceUrlProvided();
- boolean privacyPolicyUrlProvided = flowParameters.isPrivacyPolicyUrlProvided();
-
- if (termsOfServiceUrlProvided && privacyPolicyUrlProvided) {
- return R.string.fui_sms_terms_of_service_and_privacy_policy_extended;
- }
-
- return NO_TOS_OR_PP;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderAvailability.java b/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderAvailability.java
deleted file mode 100644
index 3d9d7e4a3..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderAvailability.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class ProviderAvailability {
- public static final boolean IS_GITHUB_AVAILABLE =
- exists("com.firebase.ui.auth.data.remote.GitHubSignInHandler");
- public static final boolean IS_FACEBOOK_AVAILABLE =
- exists("com.facebook.login.LoginManager");
- public static final boolean IS_TWITTER_AVAILABLE =
- exists("com.twitter.sdk.android.core.identity.TwitterAuthClient");
-
- private ProviderAvailability() {
- throw new AssertionError("No instance for you!");
- }
-
- private static boolean exists(String name) {
- boolean exists;
- try {
- Class.forName(name);
- exists = true;
- } catch (ClassNotFoundException e) {
- exists = false;
- }
- return exists;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java
deleted file mode 100644
index 65fe147ef..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/ProviderUtils.java
+++ /dev/null
@@ -1,280 +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.util.data;
-
-import android.text.TextUtils;
-
-import com.firebase.ui.auth.AuthUI;
-import com.firebase.ui.auth.ErrorCodes;
-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.google.android.gms.tasks.Continuation;
-import com.google.android.gms.tasks.Task;
-import com.google.android.gms.tasks.Tasks;
-import com.google.firebase.auth.AuthCredential;
-import com.google.firebase.auth.EmailAuthProvider;
-import com.google.firebase.auth.FacebookAuthProvider;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.GithubAuthProvider;
-import com.google.firebase.auth.GoogleAuthProvider;
-import com.google.firebase.auth.PhoneAuthProvider;
-import com.google.firebase.auth.SignInMethodQueryResult;
-import com.google.firebase.auth.TwitterAuthProvider;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-
-import static com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class ProviderUtils {
- private static final String GOOGLE_ACCOUNT_TYPE = "https://accounts.google.com";
- private static final String FACEBOOK_ACCOUNT_TYPE = "https://www.facebook.com";
- private static final String TWITTER_ACCOUNT_TYPE = "https://twitter.com";
- private static final String GITHUB_IDENTITY = "https://github.com";
- private static final String PHONE_IDENTITY = "https://phone.firebase";
-
- private ProviderUtils() {
- throw new AssertionError("No instance for you!");
- }
-
- @Nullable
- public static AuthCredential getAuthCredential(IdpResponse response) {
- if (response.hasCredentialForLinking()) {
- return response.getCredentialForLinking();
- }
- switch (response.getProviderType()) {
- case GoogleAuthProvider.PROVIDER_ID:
- return GoogleAuthProvider.getCredential(response.getIdpToken(), null);
- case FacebookAuthProvider.PROVIDER_ID:
- return FacebookAuthProvider.getCredential(response.getIdpToken());
- default:
- return null;
- }
- }
-
- @Nullable
- public static String idpResponseToAccountType(@Nullable IdpResponse response) {
- if (response == null) {
- return null;
- }
- return providerIdToAccountType(response.getProviderType());
- }
-
- @NonNull
- @AuthUI.SupportedProvider
- public static String signInMethodToProviderId(@NonNull String method) {
- switch (method) {
- case GoogleAuthProvider.GOOGLE_SIGN_IN_METHOD:
- return GoogleAuthProvider.PROVIDER_ID;
- case FacebookAuthProvider.FACEBOOK_SIGN_IN_METHOD:
- return FacebookAuthProvider.PROVIDER_ID;
- case TwitterAuthProvider.TWITTER_SIGN_IN_METHOD:
- return TwitterAuthProvider.PROVIDER_ID;
- case GithubAuthProvider.GITHUB_SIGN_IN_METHOD:
- return GithubAuthProvider.PROVIDER_ID;
- case PhoneAuthProvider.PHONE_SIGN_IN_METHOD:
- return PhoneAuthProvider.PROVIDER_ID;
- case EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD:
- return EmailAuthProvider.PROVIDER_ID;
- case EmailAuthProvider.EMAIL_LINK_SIGN_IN_METHOD:
- return EMAIL_LINK_PROVIDER;
- default:
- return method;
- }
- }
-
- /**
- * Translate a Firebase Auth provider ID (such as {@link GoogleAuthProvider#PROVIDER_ID}) to a
- * Credentials API account type.
- */
- public static String providerIdToAccountType(
- @AuthUI.SupportedProvider @NonNull String providerId) {
- switch (providerId) {
- case GoogleAuthProvider.PROVIDER_ID:
- return GOOGLE_ACCOUNT_TYPE;
- case FacebookAuthProvider.PROVIDER_ID:
- return FACEBOOK_ACCOUNT_TYPE;
- case TwitterAuthProvider.PROVIDER_ID:
- return TWITTER_ACCOUNT_TYPE;
- case GithubAuthProvider.PROVIDER_ID:
- return GITHUB_IDENTITY;
- case PhoneAuthProvider.PROVIDER_ID:
- return PHONE_IDENTITY;
- // The account type for email/password creds is null.
- case EmailAuthProvider.PROVIDER_ID:
- default:
- return null;
- }
- }
-
- @AuthUI.SupportedProvider
- public static String accountTypeToProviderId(@NonNull String accountType) {
- switch (accountType) {
- case GOOGLE_ACCOUNT_TYPE:
- return GoogleAuthProvider.PROVIDER_ID;
- case FACEBOOK_ACCOUNT_TYPE:
- return FacebookAuthProvider.PROVIDER_ID;
- case TWITTER_ACCOUNT_TYPE:
- return TwitterAuthProvider.PROVIDER_ID;
- case GITHUB_IDENTITY:
- return GithubAuthProvider.PROVIDER_ID;
- case PHONE_IDENTITY:
- return PhoneAuthProvider.PROVIDER_ID;
- default:
- return null;
- }
- }
-
- public static String providerIdToProviderName(@NonNull String providerId) {
- switch (providerId) {
- case GoogleAuthProvider.PROVIDER_ID:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_google);
- case FacebookAuthProvider.PROVIDER_ID:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_facebook);
- case TwitterAuthProvider.PROVIDER_ID:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_twitter);
- case GithubAuthProvider.PROVIDER_ID:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_github);
- case PhoneAuthProvider.PROVIDER_ID:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_phone);
- case EmailAuthProvider.PROVIDER_ID:
- case EMAIL_LINK_PROVIDER:
- return AuthUI.getApplicationContext().getString(R.string.fui_idp_name_email);
- default:
- return null;
- }
- }
-
- @Nullable
- public static AuthUI.IdpConfig getConfigFromIdps(List idps, String id) {
- for (AuthUI.IdpConfig idp : idps) {
- if (idp.getProviderId().equals(id)) {
- return idp;
- }
- }
- return null;
- }
-
- @NonNull
- public static AuthUI.IdpConfig getConfigFromIdpsOrThrow(List idps,
- String id) {
- AuthUI.IdpConfig config = getConfigFromIdps(idps, id);
- if (config == null) {
- throw new IllegalStateException("Provider " + id + " not found.");
- }
- return config;
- }
-
- public static Task> fetchSortedProviders(@NonNull FirebaseAuth auth,
- @NonNull final FlowParameters params,
- @NonNull String email) {
- if (TextUtils.isEmpty(email)) {
- return Tasks.forException(new NullPointerException("Email cannot be empty"));
- }
-
- return auth.fetchSignInMethodsForEmail(email)
- .continueWithTask(new Continuation>>() {
- @Override
- public Task> then(@NonNull Task task) {
- List methods = task.getResult().getSignInMethods();
- if (methods == null) {
- methods = new ArrayList<>();
- }
-
- List allowedProviders = new ArrayList<>(params.providers.size());
- for (AuthUI.IdpConfig provider : params.providers) {
- allowedProviders.add(provider.getProviderId());
- }
-
- List lastSignedInProviders = new ArrayList<>(methods.size());
- for (String method : methods) {
- String id = signInMethodToProviderId(method);
- if (allowedProviders.contains(id)) {
- lastSignedInProviders.add(0, id);
- }
- }
-
- // In this case the developer has configured EMAIL_LINK sign in but the
- // user is a password user. The valid use case here is that the developer
- // is using admin-created accounts and combining email-link sign in with
- // setAllowNewAccounts(false). So we manually enable EMAIL_LINK. See:
- // https://github.com/firebase/FirebaseUI-Android/issues/1762#issuecomment-661115293
- if (allowedProviders.contains(EMAIL_LINK_PROVIDER)
- && methods.contains(EmailAuthProvider.EMAIL_PASSWORD_SIGN_IN_METHOD)
- && !methods.contains(EMAIL_LINK_PROVIDER)) {
- lastSignedInProviders.add(0, signInMethodToProviderId(EMAIL_LINK_PROVIDER));
- }
-
- if (task.isSuccessful() && lastSignedInProviders.isEmpty()
- && !methods.isEmpty()) {
- // There is an existing user who only has unsupported sign in methods
- return Tasks.forException(new FirebaseUiException(ErrorCodes.DEVELOPER_ERROR));
- }
- // Reorder providers from most to least usable. Usability is determined by
- // how many steps a user needs to perform to log in.
- reorderPriorities(lastSignedInProviders);
-
- return Tasks.forResult(lastSignedInProviders);
- }
-
- private void reorderPriorities(List providers) {
- // Prioritize Google over everything else
- // Prioritize email-password sign in second
- // De-prioritize email link sign in
- changePriority(providers, EmailAuthProvider.PROVIDER_ID, true);
- changePriority(providers, GoogleAuthProvider.PROVIDER_ID, true);
- changePriority(providers, EMAIL_LINK_PROVIDER, false);
- }
-
- private void changePriority(List providers,
- String id,
- boolean maximizePriority) {
- if (providers.remove(id)) {
- if (maximizePriority) {
- providers.add(0, id);
- } else {
- providers.add(id);
- }
- }
- }
- });
- }
-
- public static Task fetchTopProvider(
- @NonNull FirebaseAuth auth,
- @NonNull FlowParameters params,
- @NonNull String email) {
- return fetchSortedProviders(auth, params, email)
- .continueWithTask(task -> {
- if (!task.isSuccessful()) {
- return Tasks.forException(task.getException());
- }
- List providers = task.getResult();
-
- if (providers.isEmpty()) {
- return Tasks.forResult(null);
- } else {
- return Tasks.forResult(providers.get(0));
- }
- });
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/SessionUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/data/SessionUtils.java
deleted file mode 100644
index 76104307b..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/SessionUtils.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import java.util.Random;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class SessionUtils {
-
- private static final String VALID_CHARS =
- "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
-
- /**
- * Generates a random alpha numeric string.
- * @param length the desired length of the generated string.
- * @return a randomly generated string with the desired number of characters.
- */
- public static String generateRandomAlphaNumericString(int length) {
- StringBuilder sb = new StringBuilder();
- Random random = new Random();
- for (int i = 0; i < length; i++) {
- sb.append(VALID_CHARS.charAt(random.nextInt(length)));
- }
- return sb.toString();
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/data/TaskFailureLogger.java b/auth/src/main/java/com/firebase/ui/auth/util/data/TaskFailureLogger.java
deleted file mode 100644
index c83ded4ea..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/data/TaskFailureLogger.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.firebase.ui.auth.util.data;
-
-import android.util.Log;
-
-import com.google.android.gms.tasks.OnFailureListener;
-
-import androidx.annotation.NonNull;
-
-public class TaskFailureLogger implements OnFailureListener {
- private String mTag;
- private String mMessage;
-
- public TaskFailureLogger(@NonNull String tag, @NonNull String message) {
- mTag = tag;
- mMessage = message;
- }
-
- @Override
- public void onFailure(@NonNull Exception e) {
- Log.w(mTag, mMessage, e);
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/package-info.java b/auth/src/main/java/com/firebase/ui/auth/util/package-info.java
deleted file mode 100644
index fb47a43b3..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Utilities to simplify interactions with {@code GoogleApiClient} and the APIs it provides, such as
- * Google Sign-in and Smart Lock for Passwords. The contents of this package should be considered an
- * implementation detail and not part of the main API.
- */
-package com.firebase.ui.auth.util;
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/BucketedTextChangeListener.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/BucketedTextChangeListener.java
deleted file mode 100644
index 2f35b393c..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/BucketedTextChangeListener.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2015 Twitter, Inc.
- *
- * 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.
- *
- * Modifications copyright (C) 2017 Google Inc
- */
-
-package com.firebase.ui.auth.util.ui;
-
-import android.annotation.SuppressLint;
-import android.text.Editable;
-import android.text.TextUtils;
-import android.text.TextWatcher;
-import android.widget.EditText;
-
-import java.util.Collections;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * Listens for changes to a text field that has hyphens and replaces with the character being typed:
- * ------
- * 7-----
- * 76----
- * 764---
- * 7641--
- * 76417-
- * 764176
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public final class BucketedTextChangeListener implements TextWatcher {
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public interface ContentChangeCallback {
- /**
- * Idempotent function invoked by the listener when the edit text changes and is of expected
- * length
- */
- void whenComplete();
-
- /**
- * Idempotent function invoked by the listener when the edit text changes and is not of
- * expected length
- */
- void whileIncomplete();
- }
-
- private final EditText mEditText;
- private final ContentChangeCallback mCallback;
- private final String[] mPostFixes;
- private final String mPlaceHolder;
- private final int mExpectedContentLength;
-
- public BucketedTextChangeListener(EditText editText, int expectedContentLength, String
- placeHolder, ContentChangeCallback callback) {
- mEditText = editText;
- mExpectedContentLength = expectedContentLength;
- mPostFixes = generatePostfixArray(placeHolder, expectedContentLength);
- mCallback = callback;
- mPlaceHolder = placeHolder;
- }
-
- /**
- * For example, passing in ("-", 6) would return the following result:
- * {"", "-", "--", "---", "----", "-----", "------"}
- *
- * @param repeatableChar the char to repeat to the specified length
- * @param length the maximum length of repeated chars
- * @return an increasing sequence array of chars up the specified length
- */
- private static String[] generatePostfixArray(CharSequence repeatableChar, int length) {
- final String[] ret = new String[length + 1];
-
- for (int i = 0; i <= length; i++) {
- ret[i] = TextUtils.join("", Collections.nCopies(i, repeatableChar));
- }
-
- return ret;
- }
-
- @SuppressLint("SetTextI18n")
- @Override
- public void onTextChanged(
- CharSequence s, int ignoredParam1, int ignoredParam2, int ignoredParam3) {
- // The listener is expected to be used in conjunction with the SpacedEditText.
-
- // Approach
- // 1) Strip all spaces and hyphens introduced by the SET for aesthetics
- final String numericContents = s.toString()
- .replaceAll(" ", "")
- .replaceAll(mPlaceHolder, "");
-
- // 2) Trim the content to acceptable length.
- final int enteredContentLength = Math.min(numericContents.length(), mExpectedContentLength);
- final String enteredContent = numericContents.substring(0, enteredContentLength);
-
- // 3) Reset the text to be the content + required hyphens. The SET automatically inserts
- // spaces requires for aesthetics. This requires removing and resetting the listener to
- // avoid recursion.
- mEditText.removeTextChangedListener(this);
- mEditText.setText(enteredContent + mPostFixes[mExpectedContentLength - enteredContentLength]);
- mEditText.setSelection(enteredContentLength);
- mEditText.addTextChangedListener(this);
-
- // 4) Callback listeners waiting on content to be of expected length
- if (enteredContentLength == mExpectedContentLength && mCallback != null) {
- mCallback.whenComplete();
- } else if (mCallback != null) {
- mCallback.whileIncomplete();
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void afterTextChanged(Editable s) {}
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/FlowUtils.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/FlowUtils.java
deleted file mode 100644
index f236e2f47..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/FlowUtils.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package com.firebase.ui.auth.util.ui;
-
-import android.app.Activity;
-import android.app.PendingIntent;
-import android.content.IntentSender;
-
-import com.firebase.ui.auth.IdpResponse;
-import com.firebase.ui.auth.data.model.IntentRequiredException;
-import com.firebase.ui.auth.data.model.PendingIntentRequiredException;
-import com.firebase.ui.auth.ui.FragmentBase;
-import com.firebase.ui.auth.ui.HelperActivityBase;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public final class FlowUtils {
- private FlowUtils() {
- throw new AssertionError("No instance for you!");
- }
-
- public static boolean unhandled(@NonNull HelperActivityBase activity, @Nullable Exception e) {
- if (e instanceof IntentRequiredException) {
- IntentRequiredException typed = (IntentRequiredException) e;
- activity.startActivityForResult(typed.getIntent(), typed.getRequestCode());
- return false;
- } else if (e instanceof PendingIntentRequiredException) {
- PendingIntentRequiredException typed = (PendingIntentRequiredException) e;
- startIntentSenderForResult(activity, typed.getPendingIntent(), typed.getRequestCode());
- return false;
- }
-
- return true;
- }
-
- public static boolean unhandled(@NonNull FragmentBase fragment, @Nullable Exception e) {
- if (e instanceof IntentRequiredException) {
- IntentRequiredException typed = (IntentRequiredException) e;
- fragment.startActivityForResult(typed.getIntent(), typed.getRequestCode());
- return false;
- } else if (e instanceof PendingIntentRequiredException) {
- PendingIntentRequiredException typed = (PendingIntentRequiredException) e;
- startIntentSenderForResult(fragment, typed.getPendingIntent(), typed.getRequestCode());
- return false;
- }
-
- return true;
- }
-
- private static void startIntentSenderForResult(HelperActivityBase activity,
- PendingIntent intent,
- int requestCode) {
- try {
- activity.startIntentSenderForResult(
- intent.getIntentSender(), requestCode, null, 0, 0, 0);
- } catch (IntentSender.SendIntentException e) {
- activity.finish(Activity.RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
-
- private static void startIntentSenderForResult(FragmentBase fragment,
- PendingIntent intent,
- int requestCode) {
- try {
- fragment.startIntentSenderForResult(
- intent.getIntentSender(), requestCode, null, 0, 0, 0, null);
- } catch (IntentSender.SendIntentException e) {
- HelperActivityBase activity = (HelperActivityBase) fragment.requireActivity();
- activity.finish(Activity.RESULT_CANCELED, IdpResponse.getErrorIntent(e));
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/ImeHelper.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/ImeHelper.java
deleted file mode 100644
index 9421db3b0..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/ImeHelper.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.firebase.ui.auth.util.ui;
-
-import android.view.KeyEvent;
-import android.view.inputmethod.EditorInfo;
-import android.widget.EditText;
-import android.widget.TextView;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class ImeHelper {
-
- @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
- public interface DonePressedListener {
- void onDonePressed();
- }
-
- public static void setImeOnDoneListener(EditText doneEditText,
- final DonePressedListener listener) {
- doneEditText.setOnEditorActionListener((view, actionId, event) -> {
- if (event != null && event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
- if (event.getAction() == KeyEvent.ACTION_UP) {
- listener.onDonePressed();
- }
-
- // We need to return true even if we didn't handle the event to continue
- // receiving future callbacks.
- return true;
- } else if (actionId == EditorInfo.IME_ACTION_DONE) {
- listener.onDonePressed();
- return true;
- }
- return false;
- });
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/PreambleHandler.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/PreambleHandler.java
deleted file mode 100644
index fb3b75e20..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/PreambleHandler.java
+++ /dev/null
@@ -1,146 +0,0 @@
-package com.firebase.ui.auth.util.ui;
-
-import android.content.Context;
-import android.net.Uri;
-import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
-import android.text.method.LinkMovementMethod;
-import android.text.style.URLSpan;
-import android.view.View;
-import android.widget.TextView;
-
-import com.firebase.ui.auth.R;
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.google.android.material.color.MaterialColors;
-
-import java.lang.ref.WeakReference;
-
-import androidx.annotation.ColorInt;
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.StringRes;
-import androidx.browser.customtabs.CustomTabColorSchemeParams;
-import androidx.browser.customtabs.CustomTabsIntent;
-import androidx.core.content.ContextCompat;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PreambleHandler {
- private static final String BTN_TARGET = "%BTN%";
- private static final String TOS_TARGET = "%TOS%";
- private static final String PP_TARGET = "%PP%";
- private static final int NO_BUTTON = -1;
-
- private final Context mContext;
- private final FlowParameters mFlowParameters;
- private final int mButtonText;
-
- private SpannableStringBuilder mBuilder;
-
- private PreambleHandler(Context context, FlowParameters parameters, @StringRes int buttonText) {
- mContext = context;
- mFlowParameters = parameters;
- mButtonText = buttonText;
- }
-
- public static void setup(Context context,
- FlowParameters parameters,
- @StringRes int textViewText,
- TextView textView) {
- setup(context, parameters, NO_BUTTON, textViewText, textView);
- }
-
- public static void setup(Context context,
- FlowParameters parameters,
- @StringRes int buttonText,
- @StringRes int textViewText,
- TextView textView) {
- PreambleHandler handler = new PreambleHandler(context, parameters, buttonText);
- handler.initPreamble(textViewText);
- handler.setPreamble(textView);
- }
-
- private void setPreamble(TextView textView) {
- textView.setMovementMethod(LinkMovementMethod.getInstance());
- textView.setText(mBuilder);
- }
-
- private void initPreamble(@StringRes int textViewText) {
- String withTargets = getPreambleStringWithTargets(textViewText, mButtonText != NO_BUTTON);
- if (withTargets == null) {
- return;
- }
-
- mBuilder = new SpannableStringBuilder(withTargets);
-
- replaceTarget(BTN_TARGET, mButtonText);
- replaceUrlTarget(TOS_TARGET, R.string.fui_terms_of_service, mFlowParameters.termsOfServiceUrl);
- replaceUrlTarget(PP_TARGET, R.string.fui_privacy_policy, mFlowParameters.privacyPolicyUrl);
- }
-
- private void replaceTarget(String target, @StringRes int replacementRes) {
- int targetIndex = mBuilder.toString().indexOf(target);
- if (targetIndex != -1) {
- String replacement = mContext.getString(replacementRes);
- mBuilder.replace(targetIndex, targetIndex + target.length(), replacement);
- }
- }
-
- private void replaceUrlTarget(String target, @StringRes int replacementRes, String url) {
- int targetIndex = mBuilder.toString().indexOf(target);
- if (targetIndex != -1) {
- String replacement = mContext.getString(replacementRes);
- mBuilder.replace(targetIndex, targetIndex + target.length(), replacement);
-
- int end = targetIndex + replacement.length();
- mBuilder.setSpan(new CustomTabsSpan(mContext, url), targetIndex, end, 0);
- }
- }
-
- @Nullable
- private String getPreambleStringWithTargets(@StringRes int textViewText, boolean hasButton) {
- boolean termsOfServiceUrlProvided = !TextUtils.isEmpty(mFlowParameters.termsOfServiceUrl);
- boolean privacyPolicyUrlProvided = !TextUtils.isEmpty(mFlowParameters.privacyPolicyUrl);
-
- if (termsOfServiceUrlProvided && privacyPolicyUrlProvided) {
- Object[] targets = hasButton ?
- new Object[]{BTN_TARGET, TOS_TARGET, PP_TARGET}
- : new Object[]{TOS_TARGET, PP_TARGET};
- return mContext.getString(textViewText, targets);
- }
-
- return null;
- }
-
- private static final class CustomTabsSpan extends URLSpan {
- private final WeakReference mContext;
- private final String mUrl;
- private final CustomTabsIntent mCustomTabsIntent;
-
- public CustomTabsSpan(Context context, String url) {
- super(url);
- mContext = new WeakReference<>(context);
- mUrl = url;
-
- @ColorInt int defaultToolbarColor = ContextCompat.getColor(context,
- R.color.design_default_color_primary);
- @ColorInt int toolbarColor = MaterialColors.getColor(context,
- R.attr.colorSurface,
- defaultToolbarColor);
-
- mCustomTabsIntent = new CustomTabsIntent.Builder()
- .setDefaultColorSchemeParams(new CustomTabColorSchemeParams.Builder()
- .setToolbarColor(toolbarColor)
- .build())
- .setShowTitle(true)
- .build();
- }
-
- @Override
- public void onClick(View widget) {
- Context context = mContext.get();
- if (context != null) {
- mCustomTabsIntent.launchUrl(context, Uri.parse(mUrl));
- }
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/TextHelper.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/TextHelper.java
deleted file mode 100644
index cc0260c45..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/TextHelper.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package com.firebase.ui.auth.util.ui;
-
-import android.graphics.Typeface;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.style.StyleSpan;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class TextHelper {
-
- public static void boldAllOccurencesOfText(@NonNull SpannableStringBuilder builder,
- @NonNull String text,
- @NonNull String textToBold) {
- int fromIndex = 0;
- while (fromIndex < text.length()) {
- int start = text.indexOf(textToBold, fromIndex);
- int end = start + textToBold.length();
- if (start == -1 || end > text.length()) {
- break;
- }
- builder.setSpan(new StyleSpan(Typeface.BOLD),
- start,
- end,
- Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
- fromIndex = end + 1;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/BaseValidator.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/BaseValidator.java
deleted file mode 100644
index 2b1dc455d..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/BaseValidator.java
+++ /dev/null
@@ -1,47 +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.util.ui.fieldvalidators;
-
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class BaseValidator {
- protected TextInputLayout mErrorContainer;
- protected String mErrorMessage = "";
- protected String mEmptyMessage;
-
- public BaseValidator(TextInputLayout errorContainer) {
- mErrorContainer = errorContainer;
- }
-
- protected boolean isValid(CharSequence charSequence) {
- return true;
- }
-
- public boolean validate(CharSequence charSequence) {
- if (mEmptyMessage != null && (charSequence == null || charSequence.length() == 0)) {
- mErrorContainer.setError(mEmptyMessage);
- return false;
- } else if (isValid(charSequence)) {
- mErrorContainer.setError("");
- return true;
- } else {
- mErrorContainer.setError(mErrorMessage);
- return false;
- }
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/EmailFieldValidator.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/EmailFieldValidator.java
deleted file mode 100644
index affc36602..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/EmailFieldValidator.java
+++ /dev/null
@@ -1,36 +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.util.ui.fieldvalidators;
-
-import android.util.Patterns;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class EmailFieldValidator extends BaseValidator {
- public EmailFieldValidator(TextInputLayout errorContainer) {
- super(errorContainer);
- mErrorMessage = mErrorContainer.getResources().getString(R.string.fui_invalid_email_address);
- mEmptyMessage = mErrorContainer.getResources().getString(R.string.fui_missing_email_address);
- }
-
- @Override
- protected boolean isValid(CharSequence charSequence) {
- return Patterns.EMAIL_ADDRESS.matcher(charSequence).matches();
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/NoOpValidator.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/NoOpValidator.java
deleted file mode 100644
index 6945f92a7..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/NoOpValidator.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.firebase.ui.auth.util.ui.fieldvalidators;
-
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.RestrictTo;
-
-/**
- * Validator that is always valid.
- */
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class NoOpValidator extends BaseValidator {
-
- public NoOpValidator(TextInputLayout errorContainer) {
- super(errorContainer);
- }
-
- @Override
- protected boolean isValid(CharSequence charSequence) {
- return true;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/PasswordFieldValidator.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/PasswordFieldValidator.java
deleted file mode 100644
index be2d755a6..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/PasswordFieldValidator.java
+++ /dev/null
@@ -1,37 +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.util.ui.fieldvalidators;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class PasswordFieldValidator extends BaseValidator {
- private int mMinLength;
-
- public PasswordFieldValidator(TextInputLayout errorContainer, int minLength) {
- super(errorContainer);
- mMinLength = minLength;
- mErrorMessage = mErrorContainer.getResources()
- .getQuantityString(R.plurals.fui_error_weak_password, mMinLength, mMinLength);
- }
-
- @Override
- protected boolean isValid(CharSequence charSequence) {
- return charSequence.length() >= mMinLength;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/RequiredFieldValidator.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/RequiredFieldValidator.java
deleted file mode 100644
index 9cc817cac..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/RequiredFieldValidator.java
+++ /dev/null
@@ -1,38 +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.util.ui.fieldvalidators;
-
-import com.firebase.ui.auth.R;
-import com.google.android.material.textfield.TextInputLayout;
-
-import androidx.annotation.RestrictTo;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class RequiredFieldValidator extends BaseValidator {
- public RequiredFieldValidator(TextInputLayout errorContainer) {
- super(errorContainer);
- mErrorMessage = mErrorContainer.getResources().getString(R.string.fui_required_field);
- }
-
- public RequiredFieldValidator(TextInputLayout errorContainer, String errorMessage) {
- super(errorContainer);
- mErrorMessage = errorMessage;
- }
-
- @Override
- protected boolean isValid(CharSequence charSequence) {
- return charSequence != null && charSequence.length() > 0;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/package-info.java b/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/package-info.java
deleted file mode 100644
index 9ed929a57..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/util/ui/fieldvalidators/package-info.java
+++ /dev/null
@@ -1,20 +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.
- */
-
-/**
- * Contains utility classes for validating {@link android.widget.EditText} field contents. The
- * contents of this package should be considered an implementation detail and not part of the main
- * API.
- */
-package com.firebase.ui.auth.util.ui.fieldvalidators;
diff --git a/auth/src/main/java/com/firebase/ui/auth/viewmodel/AuthViewModelBase.java b/auth/src/main/java/com/firebase/ui/auth/viewmodel/AuthViewModelBase.java
deleted file mode 100644
index 01c4b582f..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/viewmodel/AuthViewModelBase.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.firebase.ui.auth.viewmodel;
-
-import android.app.Application;
-
-import com.firebase.ui.auth.data.model.FlowParameters;
-import com.firebase.ui.auth.data.model.Resource;
-import com.firebase.ui.auth.util.GoogleApiUtils;
-import com.google.android.gms.auth.api.identity.SignInClient;
-import com.google.firebase.FirebaseApp;
-import com.google.firebase.auth.FirebaseAuth;
-import com.google.firebase.auth.FirebaseUser;
-
-import androidx.annotation.Nullable;
-import androidx.annotation.RestrictTo;
-import androidx.annotation.VisibleForTesting;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class AuthViewModelBase extends OperableViewModel> {
- private SignInClient mSignInClient;
- private FirebaseAuth mAuth;
-
- protected AuthViewModelBase(Application application) {
- super(application);
- }
-
- @Override
- protected void onCreate() {
- FirebaseApp app = FirebaseApp.getInstance(getArguments().appName);
- mAuth = FirebaseAuth.getInstance(app);
- mSignInClient = GoogleApiUtils.getSignInClient(getApplication());
- }
-
- @Nullable
- public FirebaseUser getCurrentUser() {
- return mAuth.getCurrentUser();
- }
-
- protected FirebaseAuth getAuth() {
- return mAuth;
- }
-
- protected SignInClient getSignInClient() {
- return mSignInClient;
- }
-
- @VisibleForTesting
- public void initializeForTesting(FlowParameters parameters,
- FirebaseAuth auth,
- SignInClient client) {
- setArguments(parameters);
- mAuth = auth;
- mSignInClient = client;
- }
-}
diff --git a/auth/src/main/java/com/firebase/ui/auth/viewmodel/OperableViewModel.java b/auth/src/main/java/com/firebase/ui/auth/viewmodel/OperableViewModel.java
deleted file mode 100644
index b7e8d2f82..000000000
--- a/auth/src/main/java/com/firebase/ui/auth/viewmodel/OperableViewModel.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package com.firebase.ui.auth.viewmodel;
-
-import android.app.Application;
-
-import androidx.annotation.RestrictTo;
-import androidx.lifecycle.LiveData;
-import androidx.lifecycle.MutableLiveData;
-
-@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public abstract class OperableViewModel