Skip to content

Commit c2c4cc6

Browse files
committed
feat: PhoneAuthScreen, enter phone number and verification code
1 parent 34485d6 commit c2c4cc6

File tree

103 files changed

+1766
-12
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+1766
-12
lines changed

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,9 @@ interface AuthUIStringProvider {
159159
/** Invalid phone number error */
160160
val invalidPhoneNumber: String
161161

162+
/** Missing phone number error */
163+
val missingPhoneNumber: String
164+
162165
/** Phone verification code entry form title */
163166
val enterConfirmationCode: String
164167

@@ -171,6 +174,9 @@ interface AuthUIStringProvider {
171174
/** Resend code link text */
172175
val resendCode: String
173176

177+
/** Resend code with timer */
178+
fun resendCodeTimer(seconds: Int): String
179+
174180
/** Verifying progress text */
175181
val verifying: String
176182

@@ -180,6 +186,42 @@ interface AuthUIStringProvider {
180186
/** SMS terms of service warning */
181187
val smsTermsOfService: String
182188

189+
/** Enter phone number title */
190+
val enterPhoneNumberTitle: String
191+
192+
/** Phone number hint */
193+
val phoneNumberHint: String
194+
195+
/** Send verification code button text */
196+
val sendVerificationCode: String
197+
198+
/** Enter verification code title with phone number */
199+
fun enterVerificationCodeTitle(phoneNumber: String): String
200+
201+
/** Verification code hint */
202+
val verificationCodeHint: String
203+
204+
/** Verify code button text */
205+
val verifyCode: String
206+
207+
/** Change phone number link text */
208+
val changePhoneNumber: String
209+
210+
/** Missing verification code error */
211+
val missingVerificationCode: String
212+
213+
/** Invalid verification code error */
214+
val invalidVerificationCode: String
215+
216+
/** Country code label */
217+
val countryCode: String
218+
219+
/** Select country dialog title */
220+
val selectCountry: String
221+
222+
/** Search countries hint */
223+
val searchCountries: String
224+
183225
// Provider Picker Strings
184226
/** Common button text for sign in */
185227
val signInDefault: String

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@ class DefaultAuthUIStringProvider(
159159
get() = localizedContext.getString(R.string.fui_country_hint)
160160
override val invalidPhoneNumber: String
161161
get() = localizedContext.getString(R.string.fui_invalid_phone_number)
162+
override val missingPhoneNumber: String
163+
get() = localizedContext.getString(R.string.fui_required_field)
162164
override val enterConfirmationCode: String
163165
get() = localizedContext.getString(R.string.fui_enter_confirmation_code)
164166
override val verifyPhoneNumber: String
@@ -167,13 +169,53 @@ class DefaultAuthUIStringProvider(
167169
get() = localizedContext.getString(R.string.fui_resend_code_in)
168170
override val resendCode: String
169171
get() = localizedContext.getString(R.string.fui_resend_code)
172+
173+
override fun resendCodeTimer(seconds: Int): String =
174+
localizedContext.getString(R.string.fui_resend_code_in, seconds)
175+
170176
override val verifying: String
171177
get() = localizedContext.getString(R.string.fui_verifying)
172178
override val incorrectCodeDialogBody: String
173179
get() = localizedContext.getString(R.string.fui_incorrect_code_dialog_body)
174180
override val smsTermsOfService: String
175181
get() = localizedContext.getString(R.string.fui_sms_terms_of_service)
176182

183+
override val enterPhoneNumberTitle: String
184+
get() = localizedContext.getString(R.string.fui_verify_phone_number_title)
185+
186+
override val phoneNumberHint: String
187+
get() = localizedContext.getString(R.string.fui_phone_hint)
188+
189+
override val sendVerificationCode: String
190+
get() = localizedContext.getString(R.string.fui_next_default)
191+
192+
override fun enterVerificationCodeTitle(phoneNumber: String): String =
193+
localizedContext.getString(R.string.fui_enter_confirmation_code) + " " + phoneNumber
194+
195+
override val verificationCodeHint: String
196+
get() = localizedContext.getString(R.string.fui_enter_confirmation_code)
197+
198+
override val verifyCode: String
199+
get() = localizedContext.getString(R.string.fui_verify_phone_number)
200+
201+
override val changePhoneNumber: String
202+
get() = localizedContext.getString(R.string.fui_change_phone_number)
203+
204+
override val missingVerificationCode: String
205+
get() = localizedContext.getString(R.string.fui_required_field)
206+
207+
override val invalidVerificationCode: String
208+
get() = localizedContext.getString(R.string.fui_incorrect_code_dialog_body)
209+
210+
override val countryCode: String
211+
get() = localizedContext.getString(R.string.fui_country_hint)
212+
213+
override val selectCountry: String
214+
get() = localizedContext.getString(R.string.fui_country_hint)
215+
216+
override val searchCountries: String
217+
get() = localizedContext.getString(R.string.fui_country_hint)
218+
177219
/**
178220
* Multi-Factor Authentication Strings
179221
*/
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2025 Google Inc. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5+
* in compliance with the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the
10+
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
11+
* express or implied. See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
15+
package com.firebase.ui.auth.compose.configuration.validators
16+
17+
import com.firebase.ui.auth.compose.configuration.string_provider.AuthUIStringProvider
18+
19+
internal class VerificationCodeValidator(override val stringProvider: AuthUIStringProvider) :
20+
FieldValidator {
21+
private var _validationStatus = FieldValidationStatus(hasError = false, errorMessage = null)
22+
23+
override val hasError: Boolean
24+
get() = _validationStatus.hasError
25+
26+
override val errorMessage: String
27+
get() = _validationStatus.errorMessage ?: ""
28+
29+
override fun validate(value: String): Boolean {
30+
if (value.isEmpty()) {
31+
_validationStatus = FieldValidationStatus(
32+
hasError = true,
33+
errorMessage = stringProvider.missingVerificationCode
34+
)
35+
return false
36+
}
37+
38+
// Verification codes are typically 6 digits
39+
val digitsOnly = value.replace(Regex("[^0-9]"), "")
40+
if (digitsOnly.length != 6) {
41+
_validationStatus = FieldValidationStatus(
42+
hasError = true,
43+
errorMessage = stringProvider.invalidVerificationCode
44+
)
45+
return false
46+
}
47+
48+
_validationStatus = FieldValidationStatus(hasError = false, errorMessage = null)
49+
return true
50+
}
51+
}

auth/src/main/java/com/firebase/ui/auth/compose/data/CountryUtils.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,44 @@ object CountryUtils {
6868
}
6969
}
7070

71+
/**
72+
* Searches for countries by name, country code, or dial code.
73+
* Supports partial matching and diacritic-insensitive search.
74+
*
75+
* @param query The search query (country name, country code, or dial code).
76+
* @return List of countries matching the query, sorted by relevance.
77+
*/
78+
fun search(query: String): List<CountryData> {
79+
val trimmedQuery = query.trim()
80+
if (trimmedQuery.isEmpty()) return emptyList()
81+
82+
val normalizedQuery = normalizeString(trimmedQuery)
83+
val uppercaseQuery = trimmedQuery.uppercase()
84+
85+
return ALL_COUNTRIES.filter { country ->
86+
// Match by country name (partial, case-insensitive, diacritic-insensitive)
87+
normalizeString(country.name).contains(normalizedQuery, ignoreCase = true) ||
88+
// Match by country code (partial, case-insensitive)
89+
country.countryCode.uppercase().contains(uppercaseQuery) ||
90+
// Match by dial code (partial)
91+
country.dialCode.contains(trimmedQuery)
92+
}.sortedWith(
93+
compareBy(
94+
// Prioritize exact matches first
95+
{ country ->
96+
when {
97+
country.countryCode.uppercase() == uppercaseQuery -> 0
98+
country.dialCode == trimmedQuery -> 1
99+
normalizeString(country.name) == normalizedQuery -> 2
100+
else -> 3
101+
}
102+
},
103+
// Then sort alphabetically by name
104+
{ country -> country.name }
105+
)
106+
)
107+
}
108+
71109
/**
72110
* Filters countries by allowed country codes.
73111
*

auth/src/main/java/com/firebase/ui/auth/compose/ui/components/AuthTextField.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ fun AuthTextField(
130130
{
131131
Icon(
132132
imageVector = Icons.Default.Email,
133-
contentDescription = ""
133+
contentDescription = "Email Input Icon"
134134
)
135135
}
136136
}
@@ -139,7 +139,7 @@ fun AuthTextField(
139139
{
140140
Icon(
141141
imageVector = Icons.Default.Lock,
142-
contentDescription = ""
142+
contentDescription = "Password Input Icon"
143143
)
144144
}
145145
}

0 commit comments

Comments
 (0)