release: v0.1.8 — configuration Firebase saisie dans l'application
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
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()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user