2 Commits
5 changed files with 38 additions and 7 deletions

No files matched your search

+4
View File
@@ -1,5 +1,9 @@
# Changelog # Changelog
## v0.1.10 – 2026-10-25
- 🐛 **Corrigé** — Configuration Firebase (Réglages → Contrôle parental) : le bloc collé est accepté même si le copier-coller a remplacé les guillemets par des guillemets typographiques (“ ” « »), et le contenu du fichier google-services.json de l'application Android est aussi reconnu.
## v0.1.9 – 2026-10-25 ## v0.1.9 – 2026-10-25
- 🔒 **Sécurité** — Retirer l'autorisation VPN ou activer un autre VPN ne désactive plus le contrôle : la notification passe en rouge, le parent est alerté, le tunnel est remonté après 30 s si possible et l'autorisation est redemandée à l'ouverture. - 🔒 **Sécurité** — Retirer l'autorisation VPN ou activer un autre VPN ne désactive plus le contrôle : la notification passe en rouge, le parent est alerté, le tunnel est remonté après 30 s si possible et l'autorisation est redemandée à l'ouverture.
+1 -1
View File
@@ -1,6 +1,6 @@
# DataControl # DataControl
🚀 v0.1.9 · 🤖 Android · 🟣 Kotlin · 🎨 Jetpack Compose · 🔥 Firebase · 🔒 pare-feu VPN local · 👨‍👩‍👧 contrôle parental · 🧪 tests · 📄 licence non déclarée 🚀 v0.1.10 · 🤖 Android · 🟣 Kotlin · 🎨 Jetpack Compose · 🔥 Firebase · 🔒 pare-feu VPN local · 👨‍👩‍👧 contrôle parental · 🧪 tests · 📄 licence non déclarée
**DataControl** est une application Android qui surveille en arrière-plan la consommation de données mobiles et coupe automatiquement l'accès à Internet en cas de dépassement du forfait — sans jamais toucher au Wi-Fi. **DataControl** est une application Android qui surveille en arrière-plan la consommation de données mobiles et coupe automatiquement l'accès à Internet en cas de dépassement du forfait — sans jamais toucher au Wi-Fi.
@@ -7,7 +7,7 @@ data class FirebaseConfig(val apiKey: String, val appId: String, val projectId:
companion object { companion object {
/** /**
* Lit ce que l'utilisateur colle : le bloc `firebaseConfig` de la console (JavaScript), un JSON, * 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? { fun parse(input: String): FirebaseConfig? {
val text = input.trim() 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")) build(json.optString("apiKey"), json.optString("appId"), json.optString("projectId"))
}.getOrNull() }.getOrNull()
} }
fun pick(name: String) = // Guillemets typographiques (souvent insérés par un copier-coller sur téléphone) → droits.
Regex("""["']?$name["']?\s*[:=]\s*["']([^"']+)["']""").find(text)?.groupValues?.get(1) val plain = text.replace(Regex("[“”„«»]"), "\"").replace(Regex("[‘’]"), "'")
return build(pick("apiKey"), pick("appId"), pick("projectId")) // 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? = private fun build(apiKey: String?, appId: String?, projectId: String?): FirebaseConfig? =
@@ -35,6 +35,25 @@ class FirebaseConfigTest {
assertEquals(expected, FirebaseConfig.parse(" fc1:${b64.chunked(20).joinToString("\n")} ")) 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`() { @Test fun `champ manquant ou illisible`() {
assertNull(FirebaseConfig.parse("""{"apiKey":"AIzaTest","projectId":"mon-projet"}""")) assertNull(FirebaseConfig.parse("""{"apiKey":"AIzaTest","projectId":"mon-projet"}"""))
assertNull(FirebaseConfig.parse("fc1:!!!")) assertNull(FirebaseConfig.parse("fc1:!!!"))
+2 -2
View File
@@ -1,2 +1,2 @@
versionName=0.1.9 versionName=0.1.10
versionCode=10 versionCode=11