Skip to content

Commit 8d8320c

Browse files
committed
wip: e2e tests for PhoneAuthScreen
1 parent c2c4cc6 commit 8d8320c

File tree

17 files changed

+798
-179
lines changed

17 files changed

+798
-179
lines changed

auth/build.gradle.kts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import com.android.build.gradle.internal.dsl.TestOptions
2-
31
plugins {
42
id("com.android.library")
53
id("com.vanniktech.maven.publish")
@@ -13,7 +11,7 @@ android {
1311

1412
defaultConfig {
1513
minSdk = Config.SdkVersions.min
16-
targetSdk =Config.SdkVersions.target
14+
targetSdk = Config.SdkVersions.target
1715

1816
buildConfigField("String", "VERSION_NAME", "\"${Config.version}\"")
1917

@@ -27,8 +25,8 @@ android {
2725
consumerProguardFiles("auth-proguard.pro")
2826
}
2927
}
30-
31-
compileOptions {
28+
29+
compileOptions {
3230
sourceCompatibility = JavaVersion.VERSION_17
3331
targetCompatibility = JavaVersion.VERSION_17
3432
}
@@ -82,8 +80,8 @@ dependencies {
8280
implementation(Config.Libs.Androidx.Compose.tooling)
8381
implementation(Config.Libs.Androidx.Compose.toolingPreview)
8482
implementation(Config.Libs.Androidx.Compose.activityCompose)
85-
implementation(Config.Libs.Androidx.materialDesign)
8683
implementation(Config.Libs.Androidx.activity)
84+
implementation(Config.Libs.Androidx.materialDesign)
8785
implementation(Config.Libs.Androidx.Compose.materialIconsExtended)
8886
implementation(Config.Libs.Androidx.datastorePreferences)
8987
// The new activity result APIs force us to include Fragment 1.3.0

auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/AuthProvider.kt

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.firebase.ui.auth.compose.configuration.auth_provider
1616

17+
import android.app.Activity
1718
import android.content.Context
1819
import android.net.Uri
1920
import android.util.Log
@@ -44,6 +45,7 @@ import com.google.firebase.auth.TwitterAuthProvider
4445
import com.google.firebase.auth.UserProfileChangeRequest
4546
import com.google.firebase.auth.actionCodeSettings
4647
import kotlinx.coroutines.tasks.await
48+
import kotlinx.serialization.Serializable
4749
import java.util.concurrent.TimeUnit
4850
import kotlin.coroutines.resume
4951
import kotlin.coroutines.resumeWithException
@@ -404,13 +406,15 @@ abstract class AuthProvider(open val providerId: String) {
404406
*/
405407
internal suspend fun verifyPhoneNumberAwait(
406408
auth: FirebaseAuth,
409+
activity: Activity?,
407410
phoneNumber: String,
408411
multiFactorSession: MultiFactorSession? = null,
409412
forceResendingToken: PhoneAuthProvider.ForceResendingToken?,
410413
verifier: Verifier = DefaultVerifier(),
411414
): VerifyPhoneNumberResult {
412415
return verifier.verifyPhoneNumber(
413416
auth,
417+
activity,
414418
phoneNumber,
415419
timeout,
416420
forceResendingToken,
@@ -425,11 +429,12 @@ abstract class AuthProvider(open val providerId: String) {
425429
internal interface Verifier {
426430
suspend fun verifyPhoneNumber(
427431
auth: FirebaseAuth,
432+
activity: Activity?,
428433
phoneNumber: String,
429434
timeout: Long,
430435
forceResendingToken: PhoneAuthProvider.ForceResendingToken?,
431436
multiFactorSession: MultiFactorSession?,
432-
isInstantVerificationEnabled: Boolean
437+
isInstantVerificationEnabled: Boolean,
433438
): VerifyPhoneNumberResult
434439
}
435440

@@ -439,18 +444,20 @@ abstract class AuthProvider(open val providerId: String) {
439444
internal class DefaultVerifier : Verifier {
440445
override suspend fun verifyPhoneNumber(
441446
auth: FirebaseAuth,
447+
activity: Activity?,
442448
phoneNumber: String,
443449
timeout: Long,
444450
forceResendingToken: PhoneAuthProvider.ForceResendingToken?,
445451
multiFactorSession: MultiFactorSession?,
446-
isInstantVerificationEnabled: Boolean
452+
isInstantVerificationEnabled: Boolean,
447453
): VerifyPhoneNumberResult {
448454
return suspendCoroutine { continuation ->
449455
val options = PhoneAuthOptions.newBuilder(auth)
450456
.setPhoneNumber(phoneNumber)
451457
.requireSmsValidation(!isInstantVerificationEnabled)
452458
.setTimeout(timeout, TimeUnit.SECONDS)
453-
.setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
459+
.setCallbacks(object :
460+
PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
454461
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
455462
continuation.resume(VerifyPhoneNumberResult.AutoVerified(credential))
456463
}
@@ -471,11 +478,14 @@ abstract class AuthProvider(open val providerId: String) {
471478
)
472479
}
473480
})
474-
if (forceResendingToken != null) {
475-
options.setForceResendingToken(forceResendingToken)
481+
activity?.let {
482+
options.setActivity(it)
476483
}
477-
if (multiFactorSession != null) {
478-
options.setMultiFactorSession(multiFactorSession)
484+
forceResendingToken?.let {
485+
options.setForceResendingToken(it)
486+
}
487+
multiFactorSession?.let {
488+
options.setMultiFactorSession(it)
479489
}
480490
PhoneAuthProvider.verifyPhoneNumber(options.build())
481491
}
@@ -495,7 +505,10 @@ abstract class AuthProvider(open val providerId: String) {
495505
* @suppress
496506
*/
497507
internal class DefaultCredentialProvider : CredentialProvider {
498-
override fun getCredential(verificationId: String, smsCode: String): PhoneAuthCredential {
508+
override fun getCredential(
509+
verificationId: String,
510+
smsCode: String,
511+
): PhoneAuthCredential {
499512
return PhoneAuthProvider.getCredential(verificationId, smsCode)
500513
}
501514
}

auth/src/main/java/com/firebase/ui/auth/compose/configuration/auth_provider/PhoneAuthProvider+FirebaseAuthUI.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.firebase.ui.auth.compose.configuration.auth_provider
22

3+
import android.app.Activity
34
import com.firebase.ui.auth.compose.AuthException
45
import com.firebase.ui.auth.compose.AuthState
56
import com.firebase.ui.auth.compose.FirebaseAuthUI
@@ -101,6 +102,7 @@ import kotlinx.coroutines.CancellationException
101102
*/
102103
internal suspend fun FirebaseAuthUI.verifyPhoneNumber(
103104
provider: AuthProvider.Phone,
105+
activity: Activity?,
104106
phoneNumber: String,
105107
multiFactorSession: MultiFactorSession? = null,
106108
forceResendingToken: PhoneAuthProvider.ForceResendingToken? = null,
@@ -110,6 +112,7 @@ internal suspend fun FirebaseAuthUI.verifyPhoneNumber(
110112
updateAuthState(AuthState.Loading("Verifying phone number..."))
111113
val result = provider.verifyPhoneNumberAwait(
112114
auth = auth,
115+
activity = activity,
113116
phoneNumber = phoneNumber,
114117
multiFactorSession = multiFactorSession,
115118
forceResendingToken = forceResendingToken,

auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/AuthUIStringProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ interface AuthUIStringProvider {
175175
val resendCode: String
176176

177177
/** Resend code with timer */
178-
fun resendCodeTimer(seconds: Int): String
178+
fun resendCodeTimer(timeFormatted: String): String
179179

180180
/** Verifying progress text */
181181
val verifying: String

auth/src/main/java/com/firebase/ui/auth/compose/configuration/string_provider/DefaultAuthUIStringProvider.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,8 +170,8 @@ class DefaultAuthUIStringProvider(
170170
override val resendCode: String
171171
get() = localizedContext.getString(R.string.fui_resend_code)
172172

173-
override fun resendCodeTimer(seconds: Int): String =
174-
localizedContext.getString(R.string.fui_resend_code_in, seconds)
173+
override fun resendCodeTimer(timeFormatted: String): String =
174+
localizedContext.getString(R.string.fui_resend_code_in, timeFormatted)
175175

176176
override val verifying: String
177177
get() = localizedContext.getString(R.string.fui_verifying)

auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/EnterVerificationCodeUI.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,10 @@ fun EnterVerificationCodeUI(
133133
) {
134134
Text(
135135
text = if (resendTimer > 0) {
136-
stringProvider.resendCodeTimer(resendTimer)
136+
val minutes = resendTimer / 60
137+
val seconds = resendTimer % 60
138+
val timeFormatted = "$minutes:${String.format("%02d", seconds)}"
139+
stringProvider.resendCodeTimer(timeFormatted)
137140
} else {
138141
stringProvider.resendCode
139142
},

auth/src/main/java/com/firebase/ui/auth/compose/ui/screens/phone/PhoneAuthScreen.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
package com.firebase.ui.auth.compose.ui.screens.phone
1616

17+
import android.app.Activity
1718
import android.content.Context
1819
import android.util.Log
1920
import androidx.compose.runtime.Composable
@@ -26,6 +27,7 @@ import androidx.compose.runtime.remember
2627
import androidx.compose.runtime.rememberCoroutineScope
2728
import androidx.compose.runtime.saveable.rememberSaveable
2829
import androidx.compose.ui.Modifier
30+
import androidx.compose.ui.platform.LocalContext
2931
import com.firebase.ui.auth.compose.AuthException
3032
import com.firebase.ui.auth.compose.AuthState
3133
import com.firebase.ui.auth.compose.FirebaseAuthUI
@@ -42,6 +44,7 @@ import com.google.firebase.auth.AuthResult
4244
import com.google.firebase.auth.PhoneAuthProvider
4345
import kotlinx.coroutines.delay
4446
import kotlinx.coroutines.launch
47+
import androidx.activity.compose.LocalActivity
4548

4649
enum class PhoneAuthStep {
4750
/**
@@ -138,6 +141,7 @@ fun PhoneAuthScreen(
138141
modifier: Modifier = Modifier,
139142
content: @Composable ((PhoneAuthContentState) -> Unit)? = null,
140143
) {
144+
val activity = LocalActivity.current
141145
val provider = configuration.providers.filterIsInstance<AuthProvider.Phone>().first()
142146
val stringProvider = DefaultAuthUIStringProvider(context)
143147
val coroutineScope = rememberCoroutineScope()
@@ -242,6 +246,7 @@ fun PhoneAuthScreen(
242246
try {
243247
authUI.verifyPhoneNumber(
244248
provider = provider,
249+
activity = activity,
245250
phoneNumber = fullPhoneNumber,
246251
)
247252
} catch (e: Exception) {
@@ -274,6 +279,7 @@ fun PhoneAuthScreen(
274279
coroutineScope.launch {
275280
try {
276281
authUI.verifyPhoneNumber(
282+
activity = activity,
277283
provider = provider,
278284
phoneNumber = fullPhoneNumber,
279285
forceResendingToken = forceResendingToken.value,
@@ -288,6 +294,7 @@ fun PhoneAuthScreen(
288294
resendTimer = resendTimerSeconds.intValue,
289295
onChangeNumberClick = {
290296
step.value = PhoneAuthStep.EnterPhoneNumber
297+
//phoneNumberValue.value = ""
291298
verificationCodeValue.value = ""
292299
verificationId.value = null
293300
forceResendingToken.value = null

auth/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
<string name="fui_verify_phone_number_title" translation_description="Phone number entry form title">Enter your phone number</string>
130130
<string name="fui_invalid_phone_number" translation_description="Inline error when phone number entered is invalid">Enter a valid phone number</string>
131131
<string name="fui_enter_confirmation_code" translation_description="Phone number verification code entry form title">Enter the 6-digit code we sent to</string>
132-
<string name="fui_resend_code_in" translation_description="Countdown timer text that the user needs to wait for before attempting to resend verification sms">Resend code in 0:%02d</string>
132+
<string name="fui_resend_code_in" translation_description="Countdown timer text that the user needs to wait for before attempting to resend verification sms">Resend code in %s</string>
133133
<string name="fui_verify_your_phone_title" translation_description="Button text to complete phone number verification">Verify your phone number</string>
134134
<string name="fui_verifying" translation_description="Progress dialog text while phone number is being verified">Verifying…</string>
135135
<string name="fui_incorrect_code_dialog_body" translation_description="Inline error when incorrect sms verification code is being used to verify">Wrong code. Try again.</string>

buildSrc/src/main/kotlin/Config.kt

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ object Config {
33
val submodules = listOf("auth", "common", "firestore", "database", "storage")
44

55
const val kotlinVersion = "2.2.0"
6+
const val kotlinSerializationVersion = "1.9.0"
67

78
object SdkVersions {
8-
const val compile = 35
9-
const val target = 35
9+
const val compile = 36
10+
const val target = 36
1011
const val min = 23
1112
}
1213

@@ -45,16 +46,24 @@ object Config {
4546
const val datastorePreferences = "androidx.datastore:datastore-preferences:1.1.1"
4647
const val credentials = "androidx.credentials:credentials:1.3.0"
4748
object Compose {
48-
const val bom = "androidx.compose:compose-bom:2025.08.00"
49+
const val bom = "androidx.compose:compose-bom:2025.10.00"
4950
const val ui = "androidx.compose.ui:ui"
5051
const val uiGraphics = "androidx.compose.ui:ui-graphics"
5152
const val toolingPreview = "androidx.compose.ui:ui-tooling-preview"
5253
const val tooling = "androidx.compose.ui:ui-tooling"
5354
const val foundation = "androidx.compose.foundation:foundation"
5455
const val material3 = "androidx.compose.material3:material3"
5556
const val materialIconsExtended = "androidx.compose.material:material-icons-extended"
56-
const val activityCompose = "androidx.activity:activity-compose:1.9.0"
57+
const val activityCompose = "androidx.activity:activity-compose:1.11.0"
5758
}
59+
60+
object Navigation {
61+
const val nav3Runtime = "androidx.navigation3:navigation3-runtime:1.0.0-alpha08"
62+
const val nav3UI = "androidx.navigation3:navigation3-ui:1.0.0-alpha08"
63+
const val lifecycleViewmodelNav3 = "androidx.lifecycle:lifecycle-viewmodel-compose:1.0.0-alpha04"
64+
}
65+
66+
const val kotlinxSerialization = "org.jetbrains.kotlinx:kotlinx-serialization-core:$kotlinSerializationVersion"
5867
}
5968

6069
object Firebase {

composeapp/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ plugins {
22
id("com.android.application")
33
id("org.jetbrains.kotlin.android")
44
id("org.jetbrains.kotlin.plugin.compose")
5+
id("org.jetbrains.kotlin.plugin.serialization") version Config.kotlinVersion
56
id("com.google.gms.google-services") apply false
67
}
78

@@ -52,6 +53,12 @@ dependencies {
5253
implementation(Config.Libs.Androidx.Compose.toolingPreview)
5354
implementation(Config.Libs.Androidx.Compose.material3)
5455

56+
// Navigation 3
57+
implementation(Config.Libs.Androidx.Navigation.nav3Runtime)
58+
implementation(Config.Libs.Androidx.Navigation.nav3UI)
59+
implementation(Config.Libs.Androidx.Navigation.lifecycleViewmodelNav3)
60+
implementation(Config.Libs.Androidx.kotlinxSerialization)
61+
5562
testImplementation(Config.Libs.Test.junit)
5663
androidTestImplementation(Config.Libs.Test.junitExt)
5764
androidTestImplementation(platform(Config.Libs.Androidx.Compose.bom))

0 commit comments

Comments
 (0)