From 11d7ed1fa664701032f6efadb3cc349d5c0ef1b5 Mon Sep 17 00:00:00 2001 From: tuxgyver Date: Sun, 25 Oct 2026 11:16:07 +0100 Subject: [PATCH] =?UTF-8?q?fix:=20configuration=20Firebase=20accept=C3=A9e?= =?UTF-8?q?=20avec=20guillemets=20typographiques=20ou=20google-services.js?= =?UTF-8?q?on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ --- .../datacontrol/remote/FirebaseConfig.kt | 16 ++++++++++++---- .../datacontrol/remote/FirebaseConfigTest.kt | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/jttools/datacontrol/remote/FirebaseConfig.kt b/app/src/main/java/com/jttools/datacontrol/remote/FirebaseConfig.kt index 4798b7d..106983e 100644 --- a/app/src/main/java/com/jttools/datacontrol/remote/FirebaseConfig.kt +++ b/app/src/main/java/com/jttools/datacontrol/remote/FirebaseConfig.kt @@ -7,7 +7,7 @@ data class FirebaseConfig(val apiKey: String, val appId: String, val projectId: companion object { /** * Lit ce que l'utilisateur colle : le bloc `firebaseConfig` de la console (JavaScript), un JSON, - * ou une chaîne « fc1:… » partagée par FamilyControl. Renvoie null si apiKey, appId ou projectId manque. + * le fichier google-services.json, ou une chaîne « fc1:… » partagée par FamilyControl. Renvoie null si apiKey, appId ou projectId manque. */ fun parse(input: String): FirebaseConfig? { val text = input.trim() @@ -17,9 +17,17 @@ data class FirebaseConfig(val apiKey: String, val appId: String, val projectId: build(json.optString("apiKey"), json.optString("appId"), json.optString("projectId")) }.getOrNull() } - fun pick(name: String) = - Regex("""["']?$name["']?\s*[:=]\s*["']([^"']+)["']""").find(text)?.groupValues?.get(1) - return build(pick("apiKey"), pick("appId"), pick("projectId")) + // Guillemets typographiques (souvent insérés par un copier-coller sur téléphone) → droits. + val plain = text.replace(Regex("[“”„«»]"), "\"").replace(Regex("[‘’]"), "'") + // Noms du bloc Web (apiKey…) ou du fichier google-services.json de l'application Android. + fun pick(vararg names: String) = names.firstNotNullOfOrNull { name -> + Regex("""["']?\b$name["']?\s*[:=]\s*["']\s*([^"'\s]+)\s*["']""").find(plain)?.groupValues?.get(1) + } + return build( + pick("apiKey", "current_key"), + pick("appId", "mobilesdk_app_id"), + pick("projectId", "project_id") + ) } private fun build(apiKey: String?, appId: String?, projectId: String?): FirebaseConfig? = diff --git a/app/src/test/java/com/jttools/datacontrol/remote/FirebaseConfigTest.kt b/app/src/test/java/com/jttools/datacontrol/remote/FirebaseConfigTest.kt index e53f5e4..e0ee7d7 100644 --- a/app/src/test/java/com/jttools/datacontrol/remote/FirebaseConfigTest.kt +++ b/app/src/test/java/com/jttools/datacontrol/remote/FirebaseConfigTest.kt @@ -35,6 +35,25 @@ class FirebaseConfigTest { assertEquals(expected, FirebaseConfig.parse(" fc1:${b64.chunked(20).joinToString("\n")} ")) } + @Test fun `guillemets typographiques`() { + val js = "apiKey: “AIzaTest”, projectId: «mon-projet», appId: ‘1:123:android:abc’" + assertEquals(expected, FirebaseConfig.parse(js)) + } + + @Test fun `fichier google-services json`() { + val gs = """ + { + "project_info": { "project_number": "123", "project_id": "mon-projet", "storage_bucket": "mon-projet.appspot.com" }, + "client": [ { + "client_info": { "mobilesdk_app_id": "1:123:android:abc", + "android_client_info": { "package_name": "com.jttools.datacontrol" } }, + "api_key": [ { "current_key": "AIzaTest" } ] + } ] + } + """.trimIndent() + assertEquals(expected, FirebaseConfig.parse(gs)) + } + @Test fun `champ manquant ou illisible`() { assertNull(FirebaseConfig.parse("""{"apiKey":"AIzaTest","projectId":"mon-projet"}""")) assertNull(FirebaseConfig.parse("fc1:!!!"))