Skip to content

Commit 34cd98c

Browse files
committed
Merge branch 'feat/accesibility' of https://github.com/firebase/FirebaseUI-Android into feat/accesibility
2 parents 29136d8 + be60c21 commit 34cd98c

File tree

154 files changed

+1291
-321
lines changed

Some content is hidden

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

154 files changed

+1291
-321
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,11 @@ class AuthUIConfigurationBuilder {
6262
"At least one provider must be configured"
6363
}
6464

65-
// No unsupported providers
65+
// No unsupported providers (allow predefined providers and custom OIDC providers starting with "oidc.")
6666
val supportedProviderIds = Provider.entries.map { it.id }.toSet()
67-
val unknownProviders = providers.filter { it.providerId !in supportedProviderIds }
67+
val unknownProviders = providers.filter { provider ->
68+
provider.providerId !in supportedProviderIds && !provider.providerId.startsWith("oidc.")
69+
}
6870
require(unknownProviders.isEmpty()) {
6971
"Unknown providers: ${unknownProviders.joinToString { it.providerId }}"
7072
}

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

Lines changed: 120 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,22 @@ class AuthProvidersBuilder {
7474
/**
7575
* Enum class to represent all possible providers.
7676
*/
77-
internal enum class Provider(val id: String, val isSocialProvider: Boolean = false) {
78-
GOOGLE(GoogleAuthProvider.PROVIDER_ID, isSocialProvider = true),
79-
FACEBOOK(FacebookAuthProvider.PROVIDER_ID, isSocialProvider = true),
80-
TWITTER(TwitterAuthProvider.PROVIDER_ID, isSocialProvider = true),
81-
GITHUB(GithubAuthProvider.PROVIDER_ID, isSocialProvider = true),
82-
EMAIL(EmailAuthProvider.PROVIDER_ID),
83-
PHONE(PhoneAuthProvider.PROVIDER_ID),
84-
ANONYMOUS("anonymous"),
85-
MICROSOFT("microsoft.com"),
86-
YAHOO("yahoo.com"),
87-
APPLE("apple.com");
77+
internal enum class Provider(
78+
val id: String,
79+
val providerName: String,
80+
val isSocialProvider: Boolean = false,
81+
) {
82+
GOOGLE(GoogleAuthProvider.PROVIDER_ID, providerName = "Google", isSocialProvider = true),
83+
FACEBOOK(FacebookAuthProvider.PROVIDER_ID, providerName = "Facebook", isSocialProvider = true),
84+
TWITTER(TwitterAuthProvider.PROVIDER_ID, providerName = "Twitter", isSocialProvider = true),
85+
GITHUB(GithubAuthProvider.PROVIDER_ID, providerName = "Github", isSocialProvider = true),
86+
EMAIL(EmailAuthProvider.PROVIDER_ID, providerName = "Email"),
87+
PHONE(PhoneAuthProvider.PROVIDER_ID, providerName = "Phone"),
88+
ANONYMOUS("anonymous", providerName = "Anonymous"),
89+
MICROSOFT("microsoft.com", providerName = "Microsoft", isSocialProvider = true),
90+
YAHOO("yahoo.com", providerName = "Yahoo", isSocialProvider = true),
91+
APPLE("apple.com", providerName = "Apple", isSocialProvider = true),
92+
LINE("oidc.line", providerName = "LINE", isSocialProvider = true);
8893

8994
companion object {
9095
fun fromId(id: String): Provider? {
@@ -93,83 +98,20 @@ internal enum class Provider(val id: String, val isSocialProvider: Boolean = fal
9398
}
9499
}
95100

96-
/**
97-
* Base abstract class for OAuth authentication providers with common properties.
98-
*/
99-
abstract class OAuthProvider(
100-
override val providerId: String,
101-
102-
override val name: String,
103-
open val scopes: List<String> = emptyList(),
104-
open val customParameters: Map<String, String> = emptyMap(),
105-
) : AuthProvider(providerId = providerId, name = name)
106-
107101
/**
108102
* Base abstract class for authentication providers.
109103
*/
110-
abstract class AuthProvider(open val providerId: String, open val name: String) {
111-
112-
companion object {
113-
internal fun canUpgradeAnonymous(config: AuthUIConfiguration, auth: FirebaseAuth): Boolean {
114-
val currentUser = auth.currentUser
115-
return config.isAnonymousUpgradeEnabled
116-
&& currentUser != null
117-
&& currentUser.isAnonymous
118-
}
119-
120-
/**
121-
* Merges profile information (display name and photo URL) with the current user's profile.
122-
*
123-
* This method updates the user's profile only if the current profile is incomplete
124-
* (missing display name or photo URL). This prevents overwriting existing profile data.
125-
*
126-
* **Use case:**
127-
* After creating a new user account or linking credentials, update the profile with
128-
* information from the sign-up form or social provider.
129-
*
130-
* @param auth The [FirebaseAuth] instance
131-
* @param displayName The display name to set (if current is empty)
132-
* @param photoUri The photo URL to set (if current is null)
133-
*
134-
* **Note:** This operation always succeeds to minimize login interruptions.
135-
* Failures are logged but don't prevent sign-in completion.
136-
*/
137-
internal suspend fun mergeProfile(
138-
auth: FirebaseAuth,
139-
displayName: String?,
140-
photoUri: Uri?,
141-
) {
142-
try {
143-
val currentUser = auth.currentUser ?: return
144-
145-
// Only update if current profile is incomplete
146-
val currentDisplayName = currentUser.displayName
147-
val currentPhotoUrl = currentUser.photoUrl
148-
149-
if (!currentDisplayName.isNullOrEmpty() && currentPhotoUrl != null) {
150-
// Profile is complete, no need to update
151-
return
152-
}
153-
154-
// Build profile update with provided values
155-
val nameToSet =
156-
if (currentDisplayName.isNullOrEmpty()) displayName else currentDisplayName
157-
val photoToSet = currentPhotoUrl ?: photoUri
158-
159-
if (nameToSet != null || photoToSet != null) {
160-
val profileUpdates = UserProfileChangeRequest.Builder()
161-
.setDisplayName(nameToSet)
162-
.setPhotoUri(photoToSet)
163-
.build()
104+
abstract class AuthProvider(open val providerId: String, open val providerName: String) {
105+
/**
106+
* Base abstract class for OAuth authentication providers with common properties.
107+
*/
108+
abstract class OAuth(
109+
override val providerId: String,
164110

165-
currentUser.updateProfile(profileUpdates).await()
166-
}
167-
} catch (e: Exception) {
168-
// Log error but don't throw - profile update failure shouldn't prevent sign-in
169-
Log.e("AuthProvider.Email", "Error updating profile", e)
170-
}
171-
}
172-
}
111+
override val providerName: String,
112+
open val scopes: List<String> = emptyList(),
113+
open val customParameters: Map<String, String> = emptyMap(),
114+
) : AuthProvider(providerId = providerId, providerName = providerName)
173115

174116
/**
175117
* Email/Password authentication provider configuration.
@@ -212,7 +154,7 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
212154
* A list of custom password validation rules.
213155
*/
214156
val passwordValidationRules: List<PasswordRule>,
215-
) : AuthProvider(providerId = Provider.EMAIL.id, name = "Email") {
157+
) : AuthProvider(providerId = Provider.EMAIL.id, providerName = Provider.EMAIL.providerName) {
216158
companion object {
217159
const val SESSION_ID_LENGTH = 10
218160
val KEY_EMAIL = stringPreferencesKey("com.firebase.ui.auth.data.client.email")
@@ -338,7 +280,7 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
338280
* Enables instant verification of the phone number. Defaults to true.
339281
*/
340282
val isInstantVerificationEnabled: Boolean = true,
341-
) : AuthProvider(providerId = Provider.PHONE.id, name = "Phone") {
283+
) : AuthProvider(providerId = Provider.PHONE.id, providerName = Provider.PHONE.providerName) {
342284
/**
343285
* Sealed class representing the result of phone number verification.
344286
*
@@ -562,9 +504,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
562504
* A map of custom OAuth parameters.
563505
*/
564506
override val customParameters: Map<String, String> = emptyMap(),
565-
) : OAuthProvider(
507+
) : OAuth(
566508
providerId = Provider.GOOGLE.id,
567-
name = "Google",
509+
providerName = Provider.GOOGLE.providerName,
568510
scopes = scopes,
569511
customParameters = customParameters
570512
) {
@@ -691,9 +633,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
691633
* A map of custom OAuth parameters.
692634
*/
693635
override val customParameters: Map<String, String> = emptyMap(),
694-
) : OAuthProvider(
636+
) : OAuth(
695637
providerId = Provider.FACEBOOK.id,
696-
name = "Facebook",
638+
providerName = Provider.FACEBOOK.providerName,
697639
scopes = scopes,
698640
customParameters = customParameters
699641
) {
@@ -835,9 +777,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
835777
* A map of custom OAuth parameters.
836778
*/
837779
override val customParameters: Map<String, String>,
838-
) : OAuthProvider(
780+
) : OAuth(
839781
providerId = Provider.TWITTER.id,
840-
name = "Twitter",
782+
providerName = Provider.TWITTER.providerName,
841783
customParameters = customParameters
842784
)
843785

@@ -854,9 +796,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
854796
* A map of custom OAuth parameters.
855797
*/
856798
override val customParameters: Map<String, String>,
857-
) : OAuthProvider(
799+
) : OAuth(
858800
providerId = Provider.GITHUB.id,
859-
name = "Github",
801+
providerName = Provider.GITHUB.providerName,
860802
scopes = scopes,
861803
customParameters = customParameters
862804
)
@@ -879,9 +821,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
879821
* A map of custom OAuth parameters.
880822
*/
881823
override val customParameters: Map<String, String>,
882-
) : OAuthProvider(
824+
) : OAuth(
883825
providerId = Provider.MICROSOFT.id,
884-
name = "Microsoft",
826+
providerName = Provider.MICROSOFT.providerName,
885827
scopes = scopes,
886828
customParameters = customParameters
887829
)
@@ -899,9 +841,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
899841
* A map of custom OAuth parameters.
900842
*/
901843
override val customParameters: Map<String, String>,
902-
) : OAuthProvider(
844+
) : OAuth(
903845
providerId = Provider.YAHOO.id,
904-
name = "Yahoo",
846+
providerName = Provider.YAHOO.providerName,
905847
scopes = scopes,
906848
customParameters = customParameters
907849
)
@@ -924,9 +866,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
924866
* A map of custom OAuth parameters.
925867
*/
926868
override val customParameters: Map<String, String>,
927-
) : OAuthProvider(
869+
) : OAuth(
928870
providerId = Provider.APPLE.id,
929-
name = "Apple",
871+
providerName = Provider.APPLE.providerName,
930872
scopes = scopes,
931873
customParameters = customParameters
932874
)
@@ -936,7 +878,7 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
936878
*/
937879
object Anonymous : AuthProvider(
938880
providerId = Provider.ANONYMOUS.id,
939-
name = "Anonymous"
881+
providerName = Provider.ANONYMOUS.providerName
940882
) {
941883
internal fun validate(providers: List<AuthProvider>) {
942884
if (providers.size == 1 && providers.first() is Anonymous) {
@@ -948,14 +890,26 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
948890
}
949891
}
950892

893+
class Line(
894+
override val scopes: List<String>,
895+
override val customParameters: Map<String, String>,
896+
) : OAuth(
897+
providerId = Provider.LINE.id,
898+
providerName = Provider.LINE.providerName,
899+
scopes = scopes,
900+
customParameters = customParameters
901+
) {
902+
internal fun validate() {}
903+
}
904+
951905
/**
952906
* A generic OAuth provider for any unsupported provider.
953907
*/
954908
class GenericOAuth(
955909
/**
956910
* The provider name.
957911
*/
958-
override val name: String,
912+
override val providerName: String,
959913

960914
/**
961915
* The provider ID as configured in the Firebase console.
@@ -991,9 +945,9 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
991945
* An optional content color for the provider button.
992946
*/
993947
val contentColor: Color?,
994-
) : OAuthProvider(
948+
) : OAuth(
995949
providerId = providerId,
996-
name = name,
950+
providerName = providerName,
997951
scopes = scopes,
998952
customParameters = customParameters
999953
) {
@@ -1007,4 +961,66 @@ abstract class AuthProvider(open val providerId: String, open val name: String)
1007961
}
1008962
}
1009963
}
964+
965+
companion object {
966+
internal fun canUpgradeAnonymous(config: AuthUIConfiguration, auth: FirebaseAuth): Boolean {
967+
val currentUser = auth.currentUser
968+
return config.isAnonymousUpgradeEnabled
969+
&& currentUser != null
970+
&& currentUser.isAnonymous
971+
}
972+
973+
/**
974+
* Merges profile information (display name and photo URL) with the current user's profile.
975+
*
976+
* This method updates the user's profile only if the current profile is incomplete
977+
* (missing display name or photo URL). This prevents overwriting existing profile data.
978+
*
979+
* **Use case:**
980+
* After creating a new user account or linking credentials, update the profile with
981+
* information from the sign-up form or social provider.
982+
*
983+
* @param auth The [FirebaseAuth] instance
984+
* @param displayName The display name to set (if current is empty)
985+
* @param photoUri The photo URL to set (if current is null)
986+
*
987+
* **Note:** This operation always succeeds to minimize login interruptions.
988+
* Failures are logged but don't prevent sign-in completion.
989+
*/
990+
internal suspend fun mergeProfile(
991+
auth: FirebaseAuth,
992+
displayName: String?,
993+
photoUri: Uri?,
994+
) {
995+
try {
996+
val currentUser = auth.currentUser ?: return
997+
998+
// Only update if current profile is incomplete
999+
val currentDisplayName = currentUser.displayName
1000+
val currentPhotoUrl = currentUser.photoUrl
1001+
1002+
if (!currentDisplayName.isNullOrEmpty() && currentPhotoUrl != null) {
1003+
// Profile is complete, no need to update
1004+
return
1005+
}
1006+
1007+
// Build profile update with provided values
1008+
val nameToSet =
1009+
if (currentDisplayName.isNullOrEmpty()) displayName else currentDisplayName
1010+
val photoToSet = currentPhotoUrl ?: photoUri
1011+
1012+
if (nameToSet != null || photoToSet != null) {
1013+
val profileUpdates = UserProfileChangeRequest.Builder()
1014+
.setDisplayName(nameToSet)
1015+
.setPhotoUri(photoToSet)
1016+
.build()
1017+
1018+
currentUser.updateProfile(profileUpdates).await()
1019+
}
1020+
} catch (e: Exception) {
1021+
// Log error but don't throw - profile update failure shouldn't prevent sign-in
1022+
Log.e("AuthProvider.Email", "Error updating profile", e)
1023+
}
1024+
}
1025+
}
10101026
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ internal suspend fun FirebaseAuthUI.signInAndLinkWithCredential(
510510
val accountLinkingException = AuthException.AccountLinkingRequiredException(
511511
message = "An account already exists with the email ${email ?: ""}. " +
512512
"Please sign in with your existing account to link " +
513-
"your ${provider?.name ?: "this provider"} account.",
513+
"your ${provider?.providerName ?: "this provider"} account.",
514514
email = email,
515515
credential = credentialForException,
516516
cause = e

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ internal fun FirebaseAuthUI.rememberSignInWithFacebookLauncher(
9292
}
9393

9494
override fun onCancel() {
95-
// val cancelledException = AuthException.AuthCancelledException(
96-
// message = "Sign in with facebook was cancelled",
97-
// )
98-
// updateAuthState(AuthState.Error(cancelledException))
9995
updateAuthState(AuthState.Idle)
10096
}
10197

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ internal suspend fun FirebaseAuthUI.signInWithGoogle(
124124
val requestedScopes = provider.scopes.map { Scope(it) }
125125
authorizationProvider.authorize(context, requestedScopes)
126126
} catch (e: Exception) {
127+
// Continue with sign-in even if scope authorization fails
127128
val authException = AuthException.from(e)
128129
updateAuthState(AuthState.Error(authException))
129130
}

0 commit comments

Comments
 (0)