diff --git a/app/src/main/java/com/firebaseui/android/demo/HighLevelApiDemoActivity.kt b/app/src/main/java/com/firebaseui/android/demo/HighLevelApiDemoActivity.kt
index 145be42eb..8aabec3b0 100644
--- a/app/src/main/java/com/firebaseui/android/demo/HighLevelApiDemoActivity.kt
+++ b/app/src/main/java/com/firebaseui/android/demo/HighLevelApiDemoActivity.kt
@@ -50,6 +50,9 @@ import com.firebase.ui.auth.configuration.AuthUITransitions
import com.firebase.ui.auth.configuration.PasswordRule
import com.firebase.ui.auth.configuration.authUIConfiguration
import com.firebase.ui.auth.configuration.auth_provider.AuthProvider
+import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProvider
+import com.firebase.ui.auth.configuration.string_provider.AuthUIStringProviderSample.CustomAuthUIStringProvider
+import com.firebase.ui.auth.configuration.string_provider.DefaultAuthUIStringProvider
import com.firebase.ui.auth.configuration.theme.AuthUIAsset
import com.firebase.ui.auth.configuration.theme.AuthUITheme
import com.firebase.ui.auth.ui.screens.AuthSuccessUiContext
@@ -71,6 +74,17 @@ class HighLevelApiDemoActivity : ComponentActivity() {
providerButtonShape = ShapeDefaults.ExtraLarge
)
+ class CustomAuthUIStringProvider(
+ private val defaultProvider: AuthUIStringProvider
+ ) : AuthUIStringProvider by defaultProvider {
+
+ override val loadingSigningInAnonymously: String
+ get() = "Overriding signing in anonymously loading message..."
+ }
+
+ val customStringProvider =
+ CustomAuthUIStringProvider(DefaultAuthUIStringProvider(applicationContext))
+
val configuration = authUIConfiguration {
context = applicationContext
theme = customTheme
@@ -79,6 +93,7 @@ class HighLevelApiDemoActivity : ComponentActivity() {
privacyPolicyUrl = "https://policies.google.com/privacy"
isAnonymousUpgradeEnabled = false
isMfaEnabled = false
+ stringProvider = customStringProvider
transitions = AuthUITransitions(
enterTransition = { slideInHorizontally { it } },
exitTransition = { slideOutHorizontally { -it } },
@@ -197,7 +212,10 @@ class HighLevelApiDemoActivity : ComponentActivity() {
authUI = authUI,
emailLink = emailLink,
onSignInSuccess = { result ->
- Log.d("HighLevelApiDemoActivity", "Authentication success: ${result.user?.uid}")
+ Log.d(
+ "HighLevelApiDemoActivity",
+ "Authentication success: ${result.user?.uid}"
+ )
},
onSignInFailure = { exception: AuthException ->
Log.e("HighLevelApiDemoActivity", "Authentication failed", exception)
@@ -327,7 +345,8 @@ private fun AppAuthenticatedContent(
}
is AuthState.RequiresEmailVerification -> {
- val email = uiContext.authUI.getCurrentUser().getDisplayEmail(stringProvider.emailProvider)
+ val email =
+ uiContext.authUI.getCurrentUser().getDisplayEmail(stringProvider.emailProvider)
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
@@ -489,7 +508,8 @@ private fun ChangePasswordDialog(
var updateError by remember { mutableStateOf(null) }
val emailProvider = remember(configuration) {
- configuration.providers.filterIsInstance().firstOrNull()
+ configuration.providers.filterIsInstance()
+ .firstOrNull()
}
val passwordValidator = remember(emailProvider, stringProvider) {
com.firebase.ui.auth.configuration.validators.PasswordValidator(
diff --git a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
index 45300cf23..5dba9843e 100644
--- a/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/FirebaseAuthUI.kt
@@ -426,7 +426,7 @@ class FirebaseAuthUI private constructor(
suspend fun signOut(context: Context) {
try {
// Update state to loading
- updateAuthState(AuthState.Loading("Signing out..."))
+ updateAuthState(AuthState.Loading(context.getString(R.string.fui_loading_signing_out)))
// Sign out from Firebase Auth
auth.signOut()
@@ -555,7 +555,7 @@ class FirebaseAuthUI private constructor(
)
// Update state to loading
- updateAuthState(AuthState.Loading("Deleting account..."))
+ updateAuthState(AuthState.Loading(context.getString(R.string.fui_loading_deleting_account)))
// Delete the user account
currentUser.delete().await()
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
index baf9cef82..65ea606dd 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProvider+FirebaseAuthUI.kt
@@ -6,6 +6,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import com.firebase.ui.auth.AuthException
import com.firebase.ui.auth.AuthState
import com.firebase.ui.auth.FirebaseAuthUI
+import com.firebase.ui.auth.configuration.AuthUIConfiguration
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.launch
import kotlinx.coroutines.tasks.await
@@ -19,14 +20,14 @@ import kotlinx.coroutines.tasks.await
* @see createOrLinkUserWithEmailAndPassword for upgrading anonymous accounts
*/
@Composable
-internal fun FirebaseAuthUI.rememberAnonymousSignInHandler(): () -> Unit {
+internal fun FirebaseAuthUI.rememberAnonymousSignInHandler(config: AuthUIConfiguration): () -> Unit {
val context = androidx.compose.ui.platform.LocalContext.current
val coroutineScope = rememberCoroutineScope()
return remember(this) {
{
coroutineScope.launch {
try {
- signInAnonymously()
+ signInAnonymously(config)
} catch (e: AuthException) {
// Already an AuthException, don't re-wrap it
updateAuthState(AuthState.Error(e))
@@ -107,9 +108,9 @@ internal fun FirebaseAuthUI.rememberAnonymousSignInHandler(): () -> Unit {
* @see createOrLinkUserWithEmailAndPassword for email/password upgrade
* @see signInWithPhoneAuthCredential for phone authentication upgrade
*/
-internal suspend fun FirebaseAuthUI.signInAnonymously() {
+internal suspend fun FirebaseAuthUI.signInAnonymously(config: AuthUIConfiguration) {
try {
- updateAuthState(AuthState.Loading("Signing in anonymously..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningInAnonymously))
val result = auth.signInAnonymously().await()
updateAuthStateWithResult(result, defaultIsNewUser = true)
} catch (e: CancellationException) {
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
index 515ad6afc..1e480eda9 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProvider+FirebaseAuthUI.kt
@@ -178,7 +178,7 @@ internal suspend fun FirebaseAuthUI.createOrLinkUserWithEmailAndPassword(
}
}
- updateAuthState(AuthState.Loading("Creating user..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingCreatingUser))
val result = if (shouldLinkCredential) {
auth.currentUser?.linkWithCredential(requireNotNull(pendingCredential))?.await()
} else {
@@ -344,7 +344,7 @@ internal suspend fun FirebaseAuthUI.signInWithEmailAndPassword(
skipCredentialSave: Boolean = false,
): AuthResult? {
try {
- updateAuthState(AuthState.Loading("Signing in..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningIn))
// In reauth mode build a credential and go through signInAndLinkWithCredential so
// signInOrReauth routes to FirebaseUser.reauthenticate() instead of signInWithCredential().
if (config.isReauthenticationMode) {
@@ -645,7 +645,7 @@ internal suspend fun FirebaseAuthUI.signInAndLinkWithCredential(
photoUrl: Uri? = null,
): AuthResult? {
try {
- updateAuthState(AuthState.Loading("Signing in user..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingLinkingCredential))
val result = if (canUpgradeAnonymous(config, auth) || canLinkCredential(config, auth)) {
auth.currentUser?.linkWithCredential(credential)?.await()
} else {
@@ -830,7 +830,7 @@ internal suspend fun FirebaseAuthUI.sendSignInLinkToEmail(
persistenceManager: PersistenceManager = EmailLinkPersistenceManager.default,
) {
try {
- updateAuthState(AuthState.Loading("Sending sign in email link..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSendingEmailLink))
// Get anonymousUserId if can upgrade anonymously else default to empty string.
// NOTE: check for empty string instead of null to validate anonymous user ID matches
@@ -988,7 +988,7 @@ internal suspend fun FirebaseAuthUI.signInWithEmailLink(
persistenceManager: PersistenceManager = EmailLinkPersistenceManager.default,
): AuthResult? {
try {
- updateAuthState(AuthState.Loading("Signing in with email link..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningInWithEmailLink))
// Validate link format
if (!auth.isSignInWithEmailLink(emailLink)) {
@@ -1256,10 +1256,11 @@ private suspend fun FirebaseAuthUI.handleEmailLinkCredentialLinkingFlow(
*/
internal suspend fun FirebaseAuthUI.sendPasswordResetEmail(
email: String,
+ config: AuthUIConfiguration,
actionCodeSettings: ActionCodeSettings? = null,
) {
try {
- updateAuthState(AuthState.Loading("Sending password reset email..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSendingPasswordResetEmail))
auth.sendPasswordResetEmail(email, actionCodeSettings).await()
updateAuthState(AuthState.PasswordResetLinkSent())
} catch (e: CancellationException) {
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
index 674f02d33..d6a9622e7 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/FacebookAuthProvider+FirebaseAuthUI.kt
@@ -114,7 +114,7 @@ internal fun FirebaseAuthUI.rememberSignInWithFacebookLauncher(
return {
updateAuthState(
- AuthState.Loading("Signing in with facebook...")
+ AuthState.Loading(config.stringProvider.loadingSigningInWithFacebook)
)
try {
(testLoginManagerProvider ?: loginManagerProvider).logOut()
@@ -155,7 +155,7 @@ internal suspend fun FirebaseAuthUI.signInWithFacebook(
) {
try {
updateAuthState(
- AuthState.Loading("Signing in with facebook...")
+ AuthState.Loading(config.stringProvider.loadingSigningInWithFacebook)
)
val profileData = provider.fetchFacebookProfile(accessToken)
val credential = credentialProvider.getCredential(accessToken.token)
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
index 496e1cd44..89837df3e 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/GoogleAuthProvider+FirebaseAuthUI.kt
@@ -119,7 +119,7 @@ internal suspend fun FirebaseAuthUI.signInWithGoogle(
) {
var idTokenFromResult: String? = null
try {
- updateAuthState(AuthState.Loading("Signing in with google..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningInWithGoogle))
// Request OAuth scopes if specified (before sign-in)
if (provider.scopes.isNotEmpty()) {
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
index ef974785f..add6bb235 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/OAuthProvider+FirebaseAuthUI.kt
@@ -129,7 +129,7 @@ internal suspend fun FirebaseAuthUI.signInWithProvider(
provider: AuthProvider.OAuth,
) {
try {
- updateAuthState(AuthState.Loading("Signing in with ${provider.providerName}..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningInWithProvider(provider.providerName)))
// Build OAuth provider with scopes and custom parameters
val oauthProvider = OAuthProvider
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
index dd8662064..24487dd58 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt
@@ -106,12 +106,13 @@ internal suspend fun FirebaseAuthUI.verifyPhoneNumber(
provider: AuthProvider.Phone,
activity: Activity?,
phoneNumber: String,
+ config: AuthUIConfiguration,
multiFactorSession: MultiFactorSession? = null,
forceResendingToken: PhoneAuthProvider.ForceResendingToken? = null,
verifier: AuthProvider.Phone.Verifier = AuthProvider.Phone.DefaultVerifier(),
) {
try {
- updateAuthState(AuthState.Loading("Verifying phone number..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingVerifyingPhoneNumber))
val result = provider.verifyPhoneNumberAwait(
auth = auth,
activity = activity,
@@ -206,7 +207,7 @@ internal suspend fun FirebaseAuthUI.submitVerificationCode(
credentialProvider: AuthProvider.Phone.CredentialProvider = AuthProvider.Phone.DefaultCredentialProvider(),
): AuthResult? {
try {
- updateAuthState(AuthState.Loading("Submitting verification code..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSubmittingVerificationCode))
val credential = credentialProvider.getCredential(verificationId, code)
return signInWithPhoneAuthCredential(
context = context,
@@ -297,7 +298,7 @@ internal suspend fun FirebaseAuthUI.signInWithPhoneAuthCredential(
credential: PhoneAuthCredential,
): AuthResult? {
try {
- updateAuthState(AuthState.Loading("Signing in with phone..."))
+ updateAuthState(AuthState.Loading(config.stringProvider.loadingSigningInWithPhone))
val result = signInAndLinkWithCredential(
config = config,
credential = credential,
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt
index bc7a8acdb..bc2ca3b43 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/AuthUIStringProvider.kt
@@ -49,6 +49,51 @@ interface AuthUIStringProvider {
/** Loading text displayed during initialization or processing states */
val initializing: String
+ /** Progress dialog message shown while signing in anonymously */
+ val loadingSigningInAnonymously: String
+
+ /** Progress dialog message shown while signing in with Google */
+ val loadingSigningInWithGoogle: String
+
+ /** Progress dialog message shown while signing in with Facebook */
+ val loadingSigningInWithFacebook: String
+
+ /** Progress dialog message shown while signing in with a named OAuth provider. [providerName] is the display name of the provider. */
+ fun loadingSigningInWithProvider(providerName: String): String
+
+ /** Progress dialog message shown while verifying a phone number */
+ val loadingVerifyingPhoneNumber: String
+
+ /** Progress dialog message shown while submitting an SMS verification code */
+ val loadingSubmittingVerificationCode: String
+
+ /** Progress dialog message shown while completing phone number sign-in */
+ val loadingSigningInWithPhone: String
+
+ /** Progress dialog message shown while creating a new user account */
+ val loadingCreatingUser: String
+
+ /** Progress dialog message shown while signing in with email and password */
+ val loadingSigningIn: String
+
+ /** Progress dialog message shown while linking a credential to the current account */
+ val loadingLinkingCredential: String
+
+ /** Progress dialog message shown while sending the sign-in email link */
+ val loadingSendingEmailLink: String
+
+ /** Progress dialog message shown while completing an email link sign-in */
+ val loadingSigningInWithEmailLink: String
+
+ /** Progress dialog message shown while sending the password reset email */
+ val loadingSendingPasswordResetEmail: String
+
+ /** Progress dialog message shown while signing the user out */
+ val loadingSigningOut: String
+
+ /** Progress dialog message shown while deleting the user's account */
+ val loadingDeletingAccount: String
+
/** Text for Google Provider */
val googleProvider: String
diff --git a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt
index 3d2b9772d..cda581acb 100644
--- a/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/configuration/string_provider/DefaultAuthUIStringProvider.kt
@@ -38,7 +38,41 @@ class DefaultAuthUIStringProvider(
* Common Strings
*/
override val initializing: String
- get() = "Initializing"
+ get() = localizedContext.getString(R.string.fui_initializing)
+
+ /**
+ * Loading State Strings
+ */
+ override val loadingSigningInAnonymously: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in_anonymously)
+ override val loadingSigningInWithGoogle: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in_with_google)
+ override val loadingSigningInWithFacebook: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in_with_facebook)
+ override fun loadingSigningInWithProvider(providerName: String): String =
+ localizedContext.getString(R.string.fui_loading_signing_in_with_provider, providerName)
+ override val loadingVerifyingPhoneNumber: String
+ get() = localizedContext.getString(R.string.fui_loading_verifying_phone_number)
+ override val loadingSubmittingVerificationCode: String
+ get() = localizedContext.getString(R.string.fui_loading_submitting_verification_code)
+ override val loadingSigningInWithPhone: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in_with_phone)
+ override val loadingCreatingUser: String
+ get() = localizedContext.getString(R.string.fui_loading_creating_user)
+ override val loadingSigningIn: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in)
+ override val loadingLinkingCredential: String
+ get() = localizedContext.getString(R.string.fui_loading_linking_credential)
+ override val loadingSendingEmailLink: String
+ get() = localizedContext.getString(R.string.fui_loading_sending_email_link)
+ override val loadingSigningInWithEmailLink: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_in_with_email_link)
+ override val loadingSendingPasswordResetEmail: String
+ get() = localizedContext.getString(R.string.fui_loading_sending_password_reset)
+ override val loadingSigningOut: String
+ get() = localizedContext.getString(R.string.fui_loading_signing_out)
+ override val loadingDeletingAccount: String
+ get() = localizedContext.getString(R.string.fui_loading_deleting_account)
/**
* Auth Provider strings
@@ -278,7 +312,7 @@ class DefaultAuthUIStringProvider(
* Multi-Factor Authentication Strings
*/
override val enterTOTPCode: String
- get() = "Enter TOTP Code"
+ get() = localizedContext.getString(R.string.fui_enter_totp_code)
/**
* Provider Picker Strings
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
index 2dfba2fec..ce57fae71 100644
--- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/FirebaseAuthScreen.kt
@@ -986,7 +986,7 @@ private fun FirebaseAuthUI.rememberOnProviderSelected(
val twitterProvider = config.providers.filterIsInstance().firstOrNull()
val genericOAuthProviders = config.providers.filterIsInstance()
- val onSignInAnonymously = anonymousProvider?.let { rememberAnonymousSignInHandler() }
+ val onSignInAnonymously = anonymousProvider?.let { rememberAnonymousSignInHandler(config) }
val onSignInWithGoogle = googleProvider?.let { rememberGoogleSignInHandler(context, config, it) }
val onSignInWithFacebook = facebookProvider?.let { rememberSignInWithFacebookLauncher(context, config, it) }
val onSignInWithApple = appleProvider?.let { rememberOAuthSignInHandler(context, activity, config, it) }
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
index 2c134c67e..af3949f4e 100644
--- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/email/EmailAuthScreen.kt
@@ -331,6 +331,7 @@ fun EmailAuthScreen(
try {
authUI.sendPasswordResetEmail(
email = emailTextValue.value,
+ config = configuration,
actionCodeSettings = configuration.passwordResetActionCodeSettings,
)
} catch (e: Exception) {
diff --git a/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
index 26161da78..8b3a69c6c 100644
--- a/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
+++ b/auth/src/main/java/com/firebase/ui/auth/ui/screens/phone/PhoneAuthScreen.kt
@@ -284,6 +284,7 @@ fun PhoneAuthScreen(
provider = provider,
activity = activity,
phoneNumber = fullPhoneNumber,
+ config = configuration,
)
} catch (e: Exception) {
// Error will be handled by authState flow
@@ -319,6 +320,7 @@ fun PhoneAuthScreen(
activity = activity,
provider = provider,
phoneNumber = fullPhoneNumber,
+ config = configuration,
forceResendingToken = forceResendingToken.value,
)
resendTimerSeconds.intValue = provider.timeout.toInt() // Restart timer
diff --git a/auth/src/main/res/values/strings.xml b/auth/src/main/res/values/strings.xml
index cc5cfa6b3..ad2e50279 100644
--- a/auth/src/main/res/values/strings.xml
+++ b/auth/src/main/res/values/strings.xml
@@ -3,7 +3,25 @@
@string/app_name
- Loading…
+ Loading...
+ Initializing
+
+
+ Signing in as guest...
+ Signing in with Google...
+ Signing in with Facebook...
+ Signing in with %1$s...
+ Verifying phone number...
+ Submitting verification code...
+ Signing in with phone...
+ Creating account...
+ Signing in...
+ Signing in...
+ Sending sign-in link...
+ Signing in with email link...
+ Sending password reset email...
+ Signing out...
+ Deleting account...
Sign in
Continue
By continuing, you are indicating that you accept our %1$s and %2$s.
@@ -69,6 +87,7 @@
Authenticator app
Unknown method
Enrolled on %1$s
+ Enter TOTP code
Scan the QR code or enter the secret key in your authenticator app
Choose a verification method
Add an extra layer of security
@@ -112,13 +131,13 @@
That email address isn\'t correct
Enter your email address to continue
Please enter a first and last name.
- Checking for existing accounts…
+ Checking for existing accounts...
Sign up
First & last name
Save
- Signing up…
+ Signing up...
- Password not strong enough. Use at least %1$d character and a mix of letters and numbers
- Password not strong enough. Use at least %1$d characters and a mix of letters and numbers
@@ -147,7 +166,7 @@
You\'ve already used %1$s.
You can connect your %2$s account with %1$s by signing in with email link below.\n\nFor this flow to successfully connect your %2$s account with this email, you have to open the link on the same device or browser.
- Signing in…
+ Signing in...
Trouble signing in?
@@ -157,7 +176,7 @@
reset your password.
Send
Follow the instructions sent to %1$s to recover your password.
- Sending…
+ Sending...
That email address doesn\'t match an existing account
@@ -203,7 +222,7 @@
Enter the 6-digit code we sent to
Resend code in %1$s
Verify your phone number
- Verifying…
+ Verifying...
Wrong code. Try again.
This phone number has been used too many times
There was a problem verifying your phone number
diff --git a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt
index 7d5a75e3d..51e9cca91 100644
--- a/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt
+++ b/auth/src/test/java/com/firebase/ui/auth/FirebaseAuthUITest.kt
@@ -18,6 +18,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.test.core.app.ApplicationProvider
+import com.firebase.ui.auth.AuthState
import com.firebase.ui.auth.configuration.auth_provider.AuthProvider
import com.firebase.ui.auth.configuration.authUIConfiguration
import com.google.android.gms.tasks.TaskCompletionSource
@@ -30,6 +31,8 @@ import com.google.firebase.auth.FirebaseAuthRecentLoginRequiredException
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.auth.UserInfo
import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -362,6 +365,28 @@ class FirebaseAuthUITest {
verify(mockAuth).signOut()
}
+ @Test
+ @Config(qualifiers = "es")
+ fun `delete() emits Loading state with translated message`() = runTest {
+ val mockAuth = mock(FirebaseAuth::class.java)
+ val mockUser = mock(FirebaseUser::class.java)
+ `when`(mockAuth.currentUser).thenReturn(mockUser)
+ val taskCompletionSource = TaskCompletionSource()
+ `when`(mockUser.delete()).thenReturn(taskCompletionSource.task)
+
+ val instance = FirebaseAuthUI.create(defaultApp, mockAuth)
+ val context = ApplicationProvider.getApplicationContext()
+
+ // Queue delete first; first{} suspends and lets the scheduler run it
+ val job = launch { runCatching { instance.delete(context) } }
+ val loadingState = instance.authStateFlow().first { it is AuthState.Loading }
+
+ assertThat((loadingState as AuthState.Loading).message)
+ .isEqualTo("test_loading_deleting_account")
+
+ job.cancel()
+ }
+
@Test
fun `signOut() handles Firebase exception and maps to AuthException`() = runTest {
// Setup mock auth that throws exception
diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt
index df4f6bcde..6e71c4170 100644
--- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt
+++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/AnonymousAuthProviderFirebaseAuthUITest.kt
@@ -19,6 +19,7 @@ import androidx.test.core.app.ApplicationProvider
import com.firebase.ui.auth.AuthException
import com.firebase.ui.auth.AuthState
import com.firebase.ui.auth.FirebaseAuthUI
+import com.firebase.ui.auth.configuration.AuthUIConfiguration
import com.firebase.ui.auth.configuration.authUIConfiguration
import com.google.android.gms.tasks.TaskCompletionSource
import com.google.common.truth.Truth.assertThat
@@ -33,6 +34,7 @@ import com.google.firebase.auth.FirebaseAuthUserCollisionException
import com.google.firebase.auth.FirebaseUser
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.After
import org.junit.Before
@@ -57,6 +59,7 @@ class AnonymousAuthProviderFirebaseAuthUITest {
private lateinit var firebaseApp: FirebaseApp
private lateinit var applicationContext: Context
+ private lateinit var config: AuthUIConfiguration
@Before
fun setUp() {
@@ -78,6 +81,14 @@ class AnonymousAuthProviderFirebaseAuthUITest {
.setProjectId("fake-project-id")
.build()
)
+
+ config = authUIConfiguration {
+ context = applicationContext
+ providers {
+ provider(AuthProvider.Anonymous)
+ provider(AuthProvider.Email(emailLinkActionCodeSettings = null, passwordValidationRules = emptyList()))
+ }
+ }
}
@After
@@ -107,7 +118,7 @@ class AnonymousAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
- instance.signInAnonymously()
+ instance.signInAnonymously(config)
verify(mockFirebaseAuth).signInAnonymously()
@@ -115,6 +126,24 @@ class AnonymousAuthProviderFirebaseAuthUITest {
assertThat(finalState).isEqualTo(AuthState.Success(result = mockAuthResult, user = mockUser, isNewUser = true))
}
+ @Test
+ @Config(qualifiers = "es")
+ fun `signInAnonymously - emits Loading state with translated message`() = runTest {
+ val taskCompletionSource = TaskCompletionSource()
+ `when`(mockFirebaseAuth.signInAnonymously()).thenReturn(taskCompletionSource.task)
+
+ val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
+
+ // Queue signInAnonymously first; first{} suspends and lets the scheduler run it
+ val job = launch { runCatching { instance.signInAnonymously(config) } }
+ val loadingState = instance.authStateFlow().first { it is AuthState.Loading }
+
+ assertThat((loadingState as AuthState.Loading).message)
+ .isEqualTo("test_loading_signing_in_anonymously")
+
+ job.cancel()
+ }
+
@Test
fun `signInAnonymously - handles network error`() = runTest {
val networkException = FirebaseNetworkException("Network error")
@@ -126,7 +155,7 @@ class AnonymousAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.signInAnonymously()
+ instance.signInAnonymously(config)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.NetworkException) {
assertThat(e.cause).isEqualTo(networkException)
@@ -149,7 +178,7 @@ class AnonymousAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.signInAnonymously()
+ instance.signInAnonymously(config)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.AuthCancelledException) {
assertThat(e.message).contains("cancelled")
@@ -173,7 +202,7 @@ class AnonymousAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.signInAnonymously()
+ instance.signInAnonymously(config)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.UnknownException) {
assertThat(e.cause).isEqualTo(genericException)
diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt
index e39fc38eb..b06489e3d 100644
--- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt
+++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/EmailAuthProviderFirebaseAuthUITest.kt
@@ -20,6 +20,7 @@ import com.firebase.ui.auth.R
import com.firebase.ui.auth.AuthException
import com.firebase.ui.auth.AuthState
import com.firebase.ui.auth.FirebaseAuthUI
+import com.firebase.ui.auth.configuration.AuthUIConfiguration
import com.firebase.ui.auth.configuration.PasswordRule
import com.firebase.ui.auth.configuration.authUIConfiguration
import com.firebase.ui.auth.util.EmailLinkPersistenceManager
@@ -82,6 +83,7 @@ class EmailAuthProviderFirebaseAuthUITest {
private lateinit var firebaseApp: FirebaseApp
private lateinit var applicationContext: Context
+ private lateinit var emailConfig: AuthUIConfiguration
@Before
fun setUp() {
@@ -103,6 +105,13 @@ class EmailAuthProviderFirebaseAuthUITest {
.setProjectId("fake-project-id")
.build()
)
+
+ emailConfig = authUIConfiguration {
+ context = applicationContext
+ providers {
+ provider(AuthProvider.Email(emailLinkActionCodeSettings = null, passwordValidationRules = emptyList()))
+ }
+ }
}
@After
@@ -817,7 +826,7 @@ class EmailAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
- instance.sendPasswordResetEmail("test@example.com")
+ instance.sendPasswordResetEmail("test@example.com", emailConfig)
verify(mockFirebaseAuth).sendPasswordResetEmail(
ArgumentMatchers.eq("test@example.com"),
@@ -841,7 +850,7 @@ class EmailAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
- instance.sendPasswordResetEmail("test@example.com", actionCodeSettings)
+ instance.sendPasswordResetEmail("test@example.com", emailConfig, actionCodeSettings)
verify(mockFirebaseAuth).sendPasswordResetEmail("test@example.com", actionCodeSettings)
@@ -865,7 +874,7 @@ class EmailAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.sendPasswordResetEmail("test@example.com")
+ instance.sendPasswordResetEmail("test@example.com", emailConfig)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.UserNotFoundException) {
assertThat(e.cause).isEqualTo(userNotFoundException)
@@ -888,7 +897,7 @@ class EmailAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.sendPasswordResetEmail("test@example.com")
+ instance.sendPasswordResetEmail("test@example.com", emailConfig)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.InvalidCredentialsException) {
assertThat(e.cause).isEqualTo(invalidEmailException)
@@ -908,7 +917,7 @@ class EmailAuthProviderFirebaseAuthUITest {
val instance = FirebaseAuthUI.create(firebaseApp, mockFirebaseAuth)
try {
- instance.sendPasswordResetEmail("test@example.com")
+ instance.sendPasswordResetEmail("test@example.com", emailConfig)
assertThat(false).isTrue() // Should not reach here
} catch (e: AuthException.AuthCancelledException) {
assertThat(e.message).contains("cancelled")
diff --git a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt
index 81386183c..3efe90c5b 100644
--- a/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt
+++ b/auth/src/test/java/com/firebase/ui/auth/configuration/auth_provider/PhoneAuthProviderFirebaseAuthUITest.kt
@@ -18,6 +18,7 @@ import android.content.Context
import androidx.test.core.app.ApplicationProvider
import com.firebase.ui.auth.AuthState
import com.firebase.ui.auth.FirebaseAuthUI
+import com.firebase.ui.auth.configuration.AuthUIConfiguration
import com.firebase.ui.auth.configuration.authUIConfiguration
import com.google.android.gms.tasks.TaskCompletionSource
import com.google.common.truth.Truth.assertThat
@@ -71,6 +72,7 @@ class PhoneAuthProviderFirebaseAuthUITest {
private lateinit var firebaseApp: FirebaseApp
private lateinit var applicationContext: Context
+ private lateinit var phoneConfig: AuthUIConfiguration
@Before
fun setUp() {
@@ -92,6 +94,13 @@ class PhoneAuthProviderFirebaseAuthUITest {
.setProjectId("fake-project-id")
.build()
)
+
+ phoneConfig = authUIConfiguration {
+ context = applicationContext
+ providers {
+ provider(AuthProvider.Phone(defaultNumber = null, defaultCountryCode = null, allowedCountries = null))
+ }
+ }
}
@After
@@ -136,6 +145,7 @@ class PhoneAuthProviderFirebaseAuthUITest {
provider = phoneProvider,
activity = null,
phoneNumber = "+1234567890",
+ config = phoneConfig,
verifier = mockPhoneAuthVerifier
)
@@ -179,6 +189,7 @@ class PhoneAuthProviderFirebaseAuthUITest {
provider = phoneProvider,
activity = null,
phoneNumber = "+1234567890",
+ config = phoneConfig,
verifier = mockPhoneAuthVerifier
)
@@ -224,6 +235,7 @@ class PhoneAuthProviderFirebaseAuthUITest {
provider = phoneProvider,
activity = null,
phoneNumber = "+1234567890",
+ config = phoneConfig,
forceResendingToken = mockToken,
verifier = mockPhoneAuthVerifier
)
@@ -268,6 +280,7 @@ class PhoneAuthProviderFirebaseAuthUITest {
provider = phoneProvider,
activity = null,
phoneNumber = "+1234567890",
+ config = phoneConfig,
verifier = mockPhoneAuthVerifier
)
diff --git a/auth/src/test/res/values-es/strings.xml b/auth/src/test/res/values-es/strings.xml
new file mode 100644
index 000000000..97b1fa87d
--- /dev/null
+++ b/auth/src/test/res/values-es/strings.xml
@@ -0,0 +1,5 @@
+
+
+ test_loading_signing_in_anonymously
+ test_loading_deleting_account
+