Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4

Expand All @@ -18,13 +19,13 @@ jobs:
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}

- name: Set up JDK 17
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '17'
java-version: '21'
distribution: 'temurin'

- name: Build with Gradle
- name: Build and Test
run: ./scripts/build.sh

- name: Print Logs
Expand Down
52 changes: 52 additions & 0 deletions .github/workflows/e2e_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: E2E Tests (Firebase Emulator)

on:
- pull_request
- push

jobs:
e2e-tests:
runs-on: ubuntu-latest
timeout-minutes: 45
steps:
- uses: actions/checkout@v4

- name: Cache Gradle packages
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}

- name: Firebase Emulator Cache
uses: actions/cache@v4
with:
path: ~/.cache/firebase/emulators
key: firebase-emulators-v3-${{ runner.os }}

- name: Install Node.js 20
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'

- name: Install Firebase Tools
run: |
npm i -g firebase-tools

- name: Start Firebase Auth Emulator
run: ./scripts/start-firebase-emulator.sh

- name: Run E2E Tests
run: |
./gradlew e2eTest

- name: Print Logs
if: failure()
run: ./scripts/print_build_logs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ class FirebaseAuthUI private constructor(
* @throws AuthException.UnknownException for other errors
* @since 10.0.0
*/
suspend fun signOut(context: Context) {
fun signOut(context: Context) {
try {
// Update state to loading
updateAuthState(AuthState.Loading("Signing out..."))
Expand Down Expand Up @@ -452,7 +452,7 @@ class FirebaseAuthUI private constructor(
*/
@JvmStatic
@RestrictTo(RestrictTo.Scope.TESTS)
internal fun clearInstanceCache() {
fun clearInstanceCache() {
instanceCache.clear()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ abstract class PasswordRule {
}

override fun getErrorMessage(stringProvider: AuthUIStringProvider): String {
return stringProvider.passwordTooShort.format(value)
return stringProvider.passwordTooShort(value)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ interface AuthUIStringProvider {
val passwordsDoNotMatch: String

/** Error message when password doesn't meet minimum length requirement. Should support string formatting with minimum length parameter. */
val passwordTooShort: String
fun passwordTooShort(minimumLength: Int): String

/** Error message when password is missing at least one uppercase letter (A-Z) */
val passwordMissingUppercase: String
Expand All @@ -102,14 +102,17 @@ interface AuthUIStringProvider {

// Email Authentication Strings
/** Title for email signup form */
val titleRegisterEmail: String
val signupPageTitle: String

/** Hint for email input field */
val emailHint: String

/** Hint for password input field */
val passwordHint: String

/** Hint for confirm password input field */
val confirmPasswordHint: String

/** Hint for new password input field */
val newPasswordHint: String

Expand All @@ -125,6 +128,24 @@ interface AuthUIStringProvider {
/** Trouble signing in link text */
val troubleSigningIn: String

/** Title for recover password page */
val recoverPasswordPageTitle: String

/** Button text for reset password */
val sendButtonText: String

/** Title for recover password link sent dialog */
val recoverPasswordLinkSentDialogTitle: String

/** Body for recover password link sent dialog */
fun recoverPasswordLinkSentDialogBody(email: String): String

/** Title for email sign in link sent dialog */
val emailSignInLinkSentDialogTitle: String

/** Body for email sign in link sent dialog */
fun emailSignInLinkSentDialogBody(email: String): String

// Phone Authentication Strings
/** Phone number entry form title */
val verifyPhoneNumberTitle: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import com.firebase.ui.auth.R
import java.util.Locale

class DefaultAuthUIStringProvider(
private val context: Context,
private val locale: Locale? = null,
context: Context,
locale: Locale? = null,
) : AuthUIStringProvider {
/**
* Allows overriding locale.
Expand Down Expand Up @@ -95,8 +95,10 @@ class DefaultAuthUIStringProvider(
get() = localizedContext.getString(R.string.fui_error_invalid_password)
override val passwordsDoNotMatch: String
get() = localizedContext.getString(R.string.fui_passwords_do_not_match)
override val passwordTooShort: String
get() = localizedContext.getString(R.string.fui_error_password_too_short)

override fun passwordTooShort(minimumLength: Int): String =
localizedContext.getString(R.string.fui_error_password_too_short, minimumLength)

override val passwordMissingUppercase: String
get() = localizedContext.getString(R.string.fui_error_password_missing_uppercase)
override val passwordMissingLowercase: String
Expand All @@ -109,12 +111,14 @@ class DefaultAuthUIStringProvider(
/**
* Email Authentication Strings
*/
override val titleRegisterEmail: String
override val signupPageTitle: String
get() = localizedContext.getString(R.string.fui_title_register_email)
override val emailHint: String
get() = localizedContext.getString(R.string.fui_email_hint)
override val passwordHint: String
get() = localizedContext.getString(R.string.fui_password_hint)
override val confirmPasswordHint: String
get() = localizedContext.getString(R.string.fui_confirm_password_hint)
override val newPasswordHint: String
get() = localizedContext.getString(R.string.fui_new_password_hint)
override val nameHint: String
Expand All @@ -126,6 +130,24 @@ class DefaultAuthUIStringProvider(
override val troubleSigningIn: String
get() = localizedContext.getString(R.string.fui_trouble_signing_in)

override val recoverPasswordPageTitle: String
get() = localizedContext.getString(R.string.fui_title_recover_password_activity)

override val sendButtonText: String
get() = localizedContext.getString(R.string.fui_button_text_send)

override val recoverPasswordLinkSentDialogTitle: String
get() = localizedContext.getString(R.string.fui_title_confirm_recover_password)

override fun recoverPasswordLinkSentDialogBody(email: String): String =
localizedContext.getString(R.string.fui_confirm_recovery_body, email)

override val emailSignInLinkSentDialogTitle: String
get() = localizedContext.getString(R.string.fui_email_link_header)

override fun emailSignInLinkSentDialogBody(email: String): String =
localizedContext.getString(R.string.fui_email_link_email_sent, email)

/**
* Phone Authentication Strings
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,27 @@ fun AuthTextField(
keyboardActions = keyboardActions,
visualTransformation = if (isSecureTextField && !passwordVisible)
PasswordVisualTransformation() else visualTransformation,
leadingIcon = leadingIcon,
leadingIcon = leadingIcon ?: when {
validator is EmailValidator -> {
{
Icon(
imageVector = Icons.Default.Email,
contentDescription = ""
)
}
}

isSecureTextField -> {
{
Icon(
imageVector = Icons.Default.Lock,
contentDescription = ""
)
}
}

else -> null
},
trailingIcon = trailingIcon ?: {
if (isSecureTextField) {
IconButton(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,22 @@ package com.firebase.ui.auth.compose.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
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.material.icons.Icons
import androidx.compose.material.icons.filled.Email
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
Expand Down Expand Up @@ -83,24 +81,25 @@ fun ResetPasswordUI(
AlertDialog(
title = {
Text(
text = "Reset Link Sent",
text = stringProvider.recoverPasswordLinkSentDialogTitle,
style = MaterialTheme.typography.headlineSmall
)
},
text = {
Text(
text = "Check your email $email",
text = stringProvider.recoverPasswordLinkSentDialogBody(email),
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Start
)
},
confirmButton = {
TextButton(
onClick = {
onGoToSignIn()
isDialogVisible.value = false
}
) {
Text("Dismiss")
Text(stringProvider.dismissAction)
}
},
onDismissRequest = {
Expand All @@ -114,7 +113,7 @@ fun ResetPasswordUI(
topBar = {
TopAppBar(
title = {
Text("Recover Password")
Text(stringProvider.recoverPasswordPageTitle)
},
colors = AuthUITheme.topAppBarColors
)
Expand All @@ -124,7 +123,8 @@ fun ResetPasswordUI(
modifier = Modifier
.padding(innerPadding)
.safeDrawingPadding()
.padding(horizontal = 16.dp),
.padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState()),
) {
AuthTextField(
value = email,
Expand All @@ -135,12 +135,6 @@ fun ResetPasswordUI(
},
onValueChange = { text ->
onEmailChange(text)
},
leadingIcon = {
Icon(
imageVector = Icons.Default.Email,
contentDescription = ""
)
}
)
Spacer(modifier = Modifier.height(8.dp))
Expand All @@ -154,7 +148,7 @@ fun ResetPasswordUI(
},
enabled = !isLoading,
) {
Text("Sign In")
Text(stringProvider.signInDefault.uppercase())
}
Spacer(modifier = Modifier.width(16.dp))
Button(
Expand All @@ -169,7 +163,7 @@ fun ResetPasswordUI(
.size(16.dp)
)
} else {
Text("Send")
Text(stringProvider.sendButtonText.uppercase())
}
}
}
Expand Down
Loading