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
16+
17+ import androidx.annotation.RestrictTo
18+ import com.google.firebase.FirebaseApp
19+ import com.google.firebase.auth.FirebaseAuth
20+ import com.google.firebase.auth.ktx.auth
21+ import com.google.firebase.ktx.Firebase
22+ import java.util.concurrent.ConcurrentHashMap
23+
24+ /* *
25+ * The central class that coordinates all authentication operations for Firebase Auth UI Compose.
26+ * This class manages UI state and provides methods for signing in, signing up, and managing
27+ * user accounts.
28+ *
29+ * <h2>Usage</h2>
30+ *
31+ * **Default app instance:**
32+ * ```kotlin
33+ * val authUI = FirebaseAuthUI.getInstance()
34+ * ```
35+ *
36+ * **Custom app instance:**
37+ * ```kotlin
38+ * val customApp = Firebase.app("secondary")
39+ * val authUI = FirebaseAuthUI.getInstance(customApp)
40+ * ```
41+ *
42+ * **Multi-tenancy with custom auth:**
43+ * ```kotlin
44+ * val customAuth = Firebase.auth(customApp).apply {
45+ * tenantId = "my-tenant-id"
46+ * }
47+ * val authUI = FirebaseAuthUI.create(customApp, customAuth)
48+ * ```
49+ *
50+ * @property app The [FirebaseApp] instance used for authentication
51+ * @property auth The [FirebaseAuth] instance used for authentication operations
52+ *
53+ * @since 10.0.0
54+ */
55+ class FirebaseAuthUI private constructor(
56+ val app : FirebaseApp ,
57+ val auth : FirebaseAuth
58+ ) {
59+ companion object {
60+ /* * Cache for singleton instances per FirebaseApp. Thread-safe via ConcurrentHashMap. */
61+ private val instanceCache = ConcurrentHashMap <String , FirebaseAuthUI >()
62+
63+ /* * Special key for the default app instance to distinguish from named instances. */
64+ private const val DEFAULT_APP_KEY = " __FIREBASE_UI_DEFAULT__"
65+
66+ /* *
67+ * Returns a cached singleton instance for the default Firebase app.
68+ *
69+ * This method ensures that the same instance is returned for the default app across the
70+ * entire application lifecycle. The instance is lazily created on first access and cached
71+ * for subsequent calls.
72+ *
73+ * **Example:**
74+ * ```kotlin
75+ * val authUI = FirebaseAuthUI.getInstance()
76+ * val user = authUI.auth.currentUser
77+ * ```
78+ *
79+ * @return The cached [FirebaseAuthUI] instance for the default app
80+ * @throws IllegalStateException if Firebase has not been initialized. Call
81+ * `FirebaseApp.initializeApp(Context)` before using this method.
82+ */
83+ @JvmStatic
84+ fun getInstance (): FirebaseAuthUI {
85+ val defaultApp = try {
86+ FirebaseApp .getInstance()
87+ } catch (e: IllegalStateException ) {
88+ throw IllegalStateException (
89+ " Default FirebaseApp is not initialized. " +
90+ " Make sure to call FirebaseApp.initializeApp(Context) first." ,
91+ e
92+ )
93+ }
94+
95+ return instanceCache.getOrPut(DEFAULT_APP_KEY ) {
96+ FirebaseAuthUI (defaultApp, Firebase .auth)
97+ }
98+ }
99+
100+ /* *
101+ * Returns a cached instance for a specific Firebase app.
102+ *
103+ * Each [FirebaseApp] gets its own distinct instance that is cached for subsequent calls
104+ * with the same app. This allows for multiple Firebase projects to be used within the
105+ * same application.
106+ *
107+ * **Example:**
108+ * ```kotlin
109+ * val secondaryApp = Firebase.app("secondary")
110+ * val authUI = FirebaseAuthUI.getInstance(secondaryApp)
111+ * ```
112+ *
113+ * @param app The [FirebaseApp] instance to use
114+ * @return The cached [FirebaseAuthUI] instance for the specified app
115+ */
116+ @JvmStatic
117+ fun getInstance (app : FirebaseApp ): FirebaseAuthUI {
118+ val cacheKey = app.name
119+ return instanceCache.getOrPut(cacheKey) {
120+ FirebaseAuthUI (app, Firebase .auth(app))
121+ }
122+ }
123+
124+ /* *
125+ * Creates a new instance with custom configuration, useful for multi-tenancy.
126+ *
127+ * This method always returns a new instance and does **not** use caching, allowing for
128+ * custom [FirebaseAuth] configurations such as tenant IDs or custom authentication states.
129+ * Use this when you need fine-grained control over the authentication instance.
130+ *
131+ * **Example - Multi-tenancy:**
132+ * ```kotlin
133+ * val app = Firebase.app("tenant-app")
134+ * val auth = Firebase.auth(app).apply {
135+ * tenantId = "customer-tenant-123"
136+ * }
137+ * val authUI = FirebaseAuthUI.create(app, auth)
138+ * ```
139+ *
140+ * @param app The [FirebaseApp] instance to use
141+ * @param auth The [FirebaseAuth] instance with custom configuration
142+ * @return A new [FirebaseAuthUI] instance with the provided dependencies
143+ */
144+ @JvmStatic
145+ fun create (app : FirebaseApp , auth : FirebaseAuth ): FirebaseAuthUI {
146+ return FirebaseAuthUI (app, auth)
147+ }
148+
149+ /* *
150+ * Clears all cached instances. This method is intended for testing purposes only.
151+ *
152+ * @suppress This is an internal API and should not be used in production code.
153+ * @RestrictTo RestrictTo.Scope.TESTS
154+ */
155+ @JvmStatic
156+ @RestrictTo(RestrictTo .Scope .TESTS )
157+ internal fun clearInstanceCache () {
158+ instanceCache.clear()
159+ }
160+
161+ /* *
162+ * Returns the current number of cached instances. This method is intended for testing
163+ * purposes only.
164+ *
165+ * @return The number of cached [FirebaseAuthUI] instances
166+ * @suppress This is an internal API and should not be used in production code.
167+ * @RestrictTo RestrictTo.Scope.TESTS
168+ */
169+ @JvmStatic
170+ @RestrictTo(RestrictTo .Scope .TESTS )
171+ internal fun getCacheSize (): Int {
172+ return instanceCache.size
173+ }
174+ }
175+ }
0 commit comments