release: v0.1.8 — configuration Firebase saisie dans l'application

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
tuxgyverandClaude Sonnet 5 committed 2026-09-21 13:39:37 +02:00
1 parent eecad06eea
commit f33776bbb5
14 files changed
+210 -53

No files matched your search

@@ -6,37 +6,65 @@ import com.google.firebase.FirebaseOptions
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import com.jttools.datacontrol.BuildConfig
import com.jttools.datacontrol.log.AppLog
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.tasks.await
import javax.inject.Inject
import javax.inject.Singleton
/** État du lien Firebase : [generation] change à chaque (ré)initialisation, pour relancer les écoutes. */
data class GatewayState(val generation: Int = 0, val enabled: Boolean = false, val projectId: String? = null)
/**
* Accès à Firebase (Auth anonyme + Firestore) initialisé depuis `firebase.properties`
* (voir build.gradle.kts), sans plugin google-services : si la config est absente,
* [enabled] est faux et toutes les fonctions distantes restent inertes.
* Accès à Firebase (Auth anonyme + Firestore). La configuration se saisit dans l'application
* (Réglages → Contrôle parental) et peut changer à tout moment ; à défaut, on retombe sur
* `firebase.properties` (compilation). Sans configuration, [enabled] est faux et tout reste inerte.
*/
@Singleton
class FirebaseGateway @Inject constructor(
@ApplicationContext private val context: Context
) {
val enabled: Boolean = BuildConfig.FIREBASE_API_KEY.isNotBlank() &&
BuildConfig.FIREBASE_APP_ID.isNotBlank() &&
BuildConfig.FIREBASE_PROJECT_ID.isNotBlank()
private val _state = MutableStateFlow(GatewayState())
val state: StateFlow<GatewayState> = _state.asStateFlow()
val enabled: Boolean get() = _state.value.enabled
private val app: FirebaseApp? by lazy {
if (!enabled) return@lazy null
runCatching {
FirebaseApp.getApps(context).firstOrNull()
?: FirebaseApp.initializeApp(
@Volatile private var app: FirebaseApp? = null
private var currentKey: String? = null
private fun buildConfigFallback(): FirebaseConfig? =
if (BuildConfig.FIREBASE_API_KEY.isNotBlank() && BuildConfig.FIREBASE_APP_ID.isNotBlank() &&
BuildConfig.FIREBASE_PROJECT_ID.isNotBlank()
) FirebaseConfig(BuildConfig.FIREBASE_API_KEY, BuildConfig.FIREBASE_APP_ID, BuildConfig.FIREBASE_PROJECT_ID)
else null
/** Applique la configuration saisie [saved] (ou, à défaut, celle de compilation). Sans effet si elle n'a pas changé. */
@Synchronized
fun apply(saved: FirebaseConfig?) {
val cfg = saved ?: buildConfigFallback()
val key = cfg?.let { "${it.apiKey}|${it.appId}|${it.projectId}" }
if (key == currentKey && (key == null || app != null)) return
app?.let { runCatching { it.delete() } }
app = null
currentKey = key
if (cfg != null) {
app = runCatching {
FirebaseApp.initializeApp(
context,
FirebaseOptions.Builder()
.setApiKey(BuildConfig.FIREBASE_API_KEY)
.setApplicationId(BuildConfig.FIREBASE_APP_ID)
.setProjectId(BuildConfig.FIREBASE_PROJECT_ID)
.setApiKey(cfg.apiKey)
.setApplicationId(cfg.appId)
.setProjectId(cfg.projectId)
.build()
)
}.getOrNull()
}.onFailure { AppLog.e("Firebase", "Initialisation impossible", it) }.getOrNull()
}
val ok = app != null
AppLog.i("Firebase", if (ok) "Configuré (projet ${cfg?.projectId})" else "Non configuré")
_state.value = GatewayState(_state.value.generation + 1, ok, cfg?.projectId?.takeIf { ok })
}
val db: FirebaseFirestore? get() = app?.let { FirebaseFirestore.getInstance(it) }
@@ -46,6 +74,8 @@ class FirebaseGateway @Inject constructor(
suspend fun ensureSignedIn(): String? {
val a = auth ?: return null
a.currentUser?.let { return it.uid }
return runCatching { a.signInAnonymously().await().user?.uid }.getOrNull()
return runCatching { a.signInAnonymously().await().user?.uid }
.onFailure { AppLog.w("Firebase", "Connexion anonyme impossible : ${it.message}") }
.getOrNull()
}
}