release: v0.1.4 — onglet Journal (diagnostic VPN), limitation des applications (plages horaires, temps d'écran)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
tuxgyverandClaude Sonnet 5 committed 2026-09-20 18:03:00 +02:00
1 parent 82d47fcd72
commit 6086e8aeff
23 files changed
+816 -40

No files matched your search

@@ -0,0 +1,67 @@
package com.jttools.datacontrol.log
import android.content.Context
import android.util.Log
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.concurrent.Executors
/**
* Journal de diagnostic : garde les [MAX_LINES] dernières lignes en mémoire et dans un fichier
* (survit au redémarrage de l'app), consultable dans l'onglet « Journal ».
*/
object AppLog {
private const val MAX_LINES = 400
private const val FILE_NAME = "journal.txt"
private val io = Executors.newSingleThreadExecutor()
private val fmt = SimpleDateFormat("MM-dd HH:mm:ss", Locale.FRANCE)
private val lines = ArrayDeque<String>()
private val _entries = MutableStateFlow<List<String>>(emptyList())
val entries: StateFlow<List<String>> = _entries.asStateFlow()
@Volatile private var file: File? = null
fun init(context: Context) {
if (file != null) return
val f = File(context.filesDir, FILE_NAME)
file = f
synchronized(lines) {
runCatching { if (f.exists()) lines.addAll(f.readLines().takeLast(MAX_LINES)) }
_entries.value = lines.toList()
}
i("App", "Journal ouvert")
}
fun i(tag: String, message: String) = add("I", tag, message)
fun w(tag: String, message: String) = add("W", tag, message)
fun e(tag: String, message: String, t: Throwable? = null) =
add("E", tag, if (t == null) message else "$message — ${t.javaClass.simpleName}: ${t.message}")
private fun add(level: String, tag: String, message: String) {
Log.println(when (level) { "E" -> Log.ERROR; "W" -> Log.WARN; else -> Log.INFO }, "DataControl/$tag", message)
val line = "${fmt.format(Date())} $level $tag: $message"
synchronized(lines) {
lines.addLast(line)
while (lines.size > MAX_LINES) lines.removeFirst()
_entries.value = lines.toList()
val snapshot = lines.toList()
io.execute { runCatching { file?.writeText(snapshot.joinToString("\n")) } }
}
}
fun clear() {
synchronized(lines) {
lines.clear()
_entries.value = emptyList()
io.execute { runCatching { file?.writeText("") } }
}
}
fun asText(): String = synchronized(lines) { lines.joinToString("\n") }
}