Skip to content

Commit d2a7236

Browse files
committed
example app
1 parent 968e0e1 commit d2a7236

File tree

4 files changed

+374
-191
lines changed

4 files changed

+374
-191
lines changed

composeapp/src/main/AndroidManifest.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@
2424
</intent-filter>
2525
</activity>
2626

27+
<activity
28+
android:name=".HighLevelApiDemoActivity"
29+
android:label="High-Level API Demo"
30+
android:exported="false"
31+
android:theme="@style/Theme.FirebaseUIAndroid" />
32+
2733
<activity
2834
android:name=".AuthFlowControllerDemoActivity"
29-
android:label="AuthFlowController Demo"
35+
android:label="Low-Level API Demo"
3036
android:exported="false"
3137
android:theme="@style/Theme.FirebaseUIAndroid" />
3238
</application>

composeapp/src/main/java/com/firebase/composeapp/AuthFlowControllerDemoActivity.kt

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,19 @@ fun AuthFlowDemo(
176176
onCancelAuth: () -> Unit
177177
) {
178178
val authState by authController.authStateFlow.collectAsState(AuthState.Idle)
179-
val currentUser = FirebaseAuth.getInstance().currentUser
179+
var currentUser by remember { mutableStateOf(FirebaseAuth.getInstance().currentUser) }
180+
181+
// Observe Firebase auth state changes
182+
DisposableEffect(Unit) {
183+
val authStateListener = FirebaseAuth.AuthStateListener { auth ->
184+
currentUser = auth.currentUser
185+
}
186+
FirebaseAuth.getInstance().addAuthStateListener(authStateListener)
187+
188+
onDispose {
189+
FirebaseAuth.getInstance().removeAuthStateListener(authStateListener)
190+
}
191+
}
180192

181193
Column(
182194
modifier = Modifier
@@ -186,13 +198,20 @@ fun AuthFlowDemo(
186198
verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically)
187199
) {
188200
Text(
189-
text = "AuthFlowController Demo",
201+
text = "⚙️ Low-Level API Demo",
190202
style = MaterialTheme.typography.headlineMedium,
191203
textAlign = TextAlign.Center
192204
)
193205

194206
Text(
195-
text = "This demonstrates the new AuthFlowController API for lifecycle-safe authentication management.",
207+
text = "AuthFlowController with ActivityResultLauncher",
208+
style = MaterialTheme.typography.titleMedium,
209+
textAlign = TextAlign.Center,
210+
color = MaterialTheme.colorScheme.primary
211+
)
212+
213+
Text(
214+
text = "This demonstrates manual control over the authentication flow with lifecycle-safe management.",
196215
style = MaterialTheme.typography.bodyMedium,
197216
textAlign = TextAlign.Center,
198217
color = MaterialTheme.colorScheme.onSurfaceVariant
@@ -238,7 +257,7 @@ fun AuthFlowDemo(
238257
}
239258

240259
// Current User Card
241-
if (currentUser != null) {
260+
currentUser?.let { user ->
242261
Card(
243262
modifier = Modifier.fillMaxWidth(),
244263
colors = CardDefaults.cardColors(
@@ -254,11 +273,11 @@ fun AuthFlowDemo(
254273
style = MaterialTheme.typography.labelLarge
255274
)
256275
Text(
257-
text = "Email: ${currentUser.email ?: "N/A"}",
276+
text = "Email: ${user.email ?: "N/A"}",
258277
style = MaterialTheme.typography.bodyMedium
259278
)
260279
Text(
261-
text = "UID: ${currentUser.uid}",
280+
text = "UID: ${user.uid}",
262281
style = MaterialTheme.typography.bodySmall,
263282
color = MaterialTheme.colorScheme.onPrimaryContainer.copy(alpha = 0.7f)
264283
)
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package com.firebase.composeapp
2+
3+
import android.os.Bundle
4+
import android.util.Log
5+
import androidx.activity.ComponentActivity
6+
import androidx.activity.compose.setContent
7+
import androidx.compose.foundation.layout.Arrangement
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.Spacer
10+
import androidx.compose.foundation.layout.fillMaxSize
11+
import androidx.compose.foundation.layout.height
12+
import androidx.compose.material3.MaterialTheme
13+
import androidx.compose.material3.Surface
14+
import androidx.compose.material3.Button
15+
import androidx.compose.material3.CircularProgressIndicator
16+
import androidx.compose.material3.Text
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.Alignment
19+
import androidx.compose.ui.text.style.TextAlign
20+
import androidx.compose.ui.unit.dp
21+
import androidx.compose.runtime.Composable
22+
import com.firebase.ui.auth.compose.AuthException
23+
import com.firebase.ui.auth.compose.AuthState
24+
import com.firebase.ui.auth.compose.FirebaseAuthUI
25+
import com.firebase.ui.auth.compose.configuration.PasswordRule
26+
import com.firebase.ui.auth.compose.configuration.authUIConfiguration
27+
import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider
28+
import com.firebase.ui.auth.compose.configuration.theme.AuthUITheme
29+
import com.firebase.ui.auth.compose.ui.screens.EmailSignInLinkHandlerActivity
30+
import com.firebase.ui.auth.compose.ui.screens.FirebaseAuthScreen
31+
import com.firebase.ui.auth.compose.ui.screens.AuthSuccessUiContext
32+
import com.google.firebase.FirebaseApp
33+
import com.google.firebase.auth.actionCodeSettings
34+
35+
class HighLevelApiDemoActivity : ComponentActivity() {
36+
companion object {
37+
private const val USE_AUTH_EMULATOR = true
38+
private const val AUTH_EMULATOR_HOST = "10.0.2.2"
39+
private const val AUTH_EMULATOR_PORT = 9099
40+
}
41+
42+
override fun onCreate(savedInstanceState: Bundle?) {
43+
super.onCreate(savedInstanceState)
44+
45+
FirebaseApp.initializeApp(applicationContext)
46+
val authUI = FirebaseAuthUI.getInstance()
47+
48+
if (USE_AUTH_EMULATOR) {
49+
authUI.auth.useEmulator(AUTH_EMULATOR_HOST, AUTH_EMULATOR_PORT)
50+
}
51+
52+
val emailLink = intent.getStringExtra(EmailSignInLinkHandlerActivity.EXTRA_EMAIL_LINK)
53+
54+
val configuration = authUIConfiguration {
55+
context = applicationContext
56+
providers {
57+
provider(
58+
AuthProvider.Email(
59+
isDisplayNameRequired = true,
60+
isEmailLinkForceSameDeviceEnabled = true,
61+
isEmailLinkSignInEnabled = false,
62+
emailLinkActionCodeSettings = actionCodeSettings {
63+
url = "https://temp-test-aa342.firebaseapp.com"
64+
handleCodeInApp = true
65+
setAndroidPackageName(
66+
"com.firebase.composeapp",
67+
true,
68+
null
69+
)
70+
},
71+
isNewAccountsAllowed = true,
72+
minimumPasswordLength = 8,
73+
passwordValidationRules = listOf(
74+
PasswordRule.MinimumLength(8),
75+
PasswordRule.RequireLowercase,
76+
PasswordRule.RequireUppercase,
77+
)
78+
)
79+
)
80+
provider(
81+
AuthProvider.Phone(
82+
defaultNumber = null,
83+
defaultCountryCode = null,
84+
allowedCountries = emptyList(),
85+
smsCodeLength = 6,
86+
timeout = 120L,
87+
isInstantVerificationEnabled = true
88+
)
89+
)
90+
provider(
91+
AuthProvider.Facebook(
92+
applicationId = "792556260059222"
93+
)
94+
)
95+
}
96+
tosUrl = "https://policies.google.com/terms?hl=en-NG&fg=1"
97+
privacyPolicyUrl = "https://policies.google.com/privacy?hl=en-NG&fg=1"
98+
}
99+
100+
setContent {
101+
AuthUITheme {
102+
Surface(
103+
modifier = Modifier.fillMaxSize(),
104+
color = MaterialTheme.colorScheme.background
105+
) {
106+
FirebaseAuthScreen(
107+
configuration = configuration,
108+
authUI = authUI,
109+
emailLink = emailLink,
110+
onSignInSuccess = { result ->
111+
Log.d("HighLevelApiDemoActivity", "Authentication success: ${result.user?.uid}")
112+
},
113+
onSignInFailure = { exception: AuthException ->
114+
Log.e("HighLevelApiDemoActivity", "Authentication failed", exception)
115+
},
116+
onSignInCancelled = {
117+
Log.d("HighLevelApiDemoActivity", "Authentication cancelled")
118+
},
119+
authenticatedContent = { state, uiContext ->
120+
AppAuthenticatedContent(state, uiContext)
121+
}
122+
)
123+
}
124+
}
125+
}
126+
}
127+
}
128+
129+
@Composable
130+
private fun AppAuthenticatedContent(
131+
state: AuthState,
132+
uiContext: AuthSuccessUiContext
133+
) {
134+
val stringProvider = uiContext.stringProvider
135+
when (state) {
136+
is AuthState.Success -> {
137+
val user = uiContext.authUI.getCurrentUser()
138+
val identifier = user?.email ?: user?.phoneNumber ?: user?.uid.orEmpty()
139+
Column(
140+
modifier = Modifier.fillMaxSize(),
141+
horizontalAlignment = Alignment.CenterHorizontally,
142+
verticalArrangement = Arrangement.Center
143+
) {
144+
if (identifier.isNotBlank()) {
145+
Text(
146+
text = stringProvider.signedInAs(identifier),
147+
textAlign = TextAlign.Center
148+
)
149+
Spacer(modifier = Modifier.height(16.dp))
150+
}
151+
152+
Button(onClick = uiContext.onManageMfa) {
153+
Text(stringProvider.manageMfaAction)
154+
}
155+
Spacer(modifier = Modifier.height(8.dp))
156+
Button(onClick = uiContext.onSignOut) {
157+
Text(stringProvider.signOutAction)
158+
}
159+
}
160+
}
161+
162+
is AuthState.RequiresEmailVerification -> {
163+
val email = uiContext.authUI.getCurrentUser()?.email ?: stringProvider.emailProvider
164+
Column(
165+
modifier = Modifier.fillMaxSize(),
166+
horizontalAlignment = Alignment.CenterHorizontally,
167+
verticalArrangement = Arrangement.Center
168+
) {
169+
Text(
170+
text = stringProvider.verifyEmailInstruction(email),
171+
textAlign = TextAlign.Center,
172+
style = MaterialTheme.typography.bodyMedium
173+
)
174+
Spacer(modifier = Modifier.height(16.dp))
175+
Button(onClick = { uiContext.authUI.getCurrentUser()?.sendEmailVerification() }) {
176+
Text(stringProvider.resendVerificationEmailAction)
177+
}
178+
Spacer(modifier = Modifier.height(8.dp))
179+
Button(onClick = uiContext.onReloadUser) {
180+
Text(stringProvider.verifiedEmailAction)
181+
}
182+
Spacer(modifier = Modifier.height(8.dp))
183+
Button(onClick = uiContext.onSignOut) {
184+
Text(stringProvider.signOutAction)
185+
}
186+
}
187+
}
188+
189+
is AuthState.RequiresProfileCompletion -> {
190+
Column(
191+
modifier = Modifier.fillMaxSize(),
192+
horizontalAlignment = Alignment.CenterHorizontally,
193+
verticalArrangement = Arrangement.Center
194+
) {
195+
Text(
196+
text = stringProvider.profileCompletionMessage,
197+
textAlign = TextAlign.Center
198+
)
199+
if (state.missingFields.isNotEmpty()) {
200+
Spacer(modifier = Modifier.height(12.dp))
201+
Text(
202+
text = stringProvider.profileMissingFieldsMessage(state.missingFields.joinToString()),
203+
textAlign = TextAlign.Center
204+
)
205+
}
206+
Spacer(modifier = Modifier.height(16.dp))
207+
Button(onClick = uiContext.onSignOut) {
208+
Text(stringProvider.signOutAction)
209+
}
210+
}
211+
}
212+
213+
else -> {
214+
Column(
215+
modifier = Modifier.fillMaxSize(),
216+
horizontalAlignment = Alignment.CenterHorizontally,
217+
verticalArrangement = Arrangement.Center
218+
) {
219+
CircularProgressIndicator()
220+
}
221+
}
222+
}
223+
}

0 commit comments

Comments
 (0)