- VPN retiré : le contrôle reste activé (alerte parent, tunnel remonté après 30 s, autorisation redemandée à l'ouverture) - Horloge de référence : changer date/heure/fuseau ne contourne plus les plages horaires ni le temps d'écran (signalé au parent) - Mot de passe : PBKDF2 + attente croissante après 3 échecs ; conversion automatique des anciens hachages - Écrans protégés : aucun affichage avant chargement des réglages ; effacement du journal protégé - Signature release via keystore.properties (même clé que les versions publiées) - Sauvegarde Google : réglages, appairage et journal exclus - Alerte admin fiable (goAsync), relance refusée signalée, liste d'applis renvoyée à chaque installation, code d'appairage consommé - Journal thread-safe et sans recopie à chaque ligne ; correctifs lint - Tests unitaires (mot de passe, règles, versions, config Firebase) ; README aligné sur le comportement réel Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ
80 lines
3.4 KiB
Kotlin
80 lines
3.4 KiB
Kotlin
package com.jttools.datacontrol.guard
|
|
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.OnBackPressedCallback
|
|
import androidx.activity.compose.setContent
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
import androidx.compose.foundation.layout.Column
|
|
import androidx.compose.foundation.layout.Spacer
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.foundation.layout.height
|
|
import androidx.compose.foundation.layout.padding
|
|
import androidx.compose.foundation.layout.size
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.Lock
|
|
import androidx.compose.material3.Button
|
|
import androidx.compose.material3.Icon
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.Surface
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.text.style.TextAlign
|
|
import androidx.compose.ui.unit.dp
|
|
import com.jttools.datacontrol.ui.theme.DataControlTheme
|
|
|
|
/** Écran affiché par-dessus une application bloquée : explique pourquoi et renvoie à l'accueil. */
|
|
class BlockedActivity : ComponentActivity() {
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
enableEdgeToEdge()
|
|
// Retour : renvoie à l'accueil, jamais vers l'application bloquée.
|
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
|
override fun handleOnBackPressed() = goHome()
|
|
})
|
|
val label = intent.getStringExtra(EXTRA_LABEL).orEmpty()
|
|
val message = intent.getStringExtra(EXTRA_MESSAGE).orEmpty()
|
|
setContent {
|
|
DataControlTheme {
|
|
Surface(Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.errorContainer) {
|
|
Column(
|
|
Modifier.fillMaxSize().padding(32.dp),
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
verticalArrangement = Arrangement.Center
|
|
) {
|
|
Icon(Icons.Filled.Lock, contentDescription = null, modifier = Modifier.size(72.dp))
|
|
Spacer(Modifier.height(20.dp))
|
|
Text(label, style = MaterialTheme.typography.headlineSmall, textAlign = TextAlign.Center)
|
|
Spacer(Modifier.height(10.dp))
|
|
Text(message, style = MaterialTheme.typography.bodyLarge, textAlign = TextAlign.Center)
|
|
Spacer(Modifier.height(28.dp))
|
|
Button(onClick = { goHome() }) { Text("Retour à l'accueil") }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private fun goHome() {
|
|
startActivity(Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
|
|
finish()
|
|
}
|
|
|
|
companion object {
|
|
private const val EXTRA_LABEL = "label"
|
|
private const val EXTRA_MESSAGE = "message"
|
|
|
|
fun intent(context: Context, label: String, message: String): Intent =
|
|
Intent(context, BlockedActivity::class.java)
|
|
.putExtra(EXTRA_LABEL, label)
|
|
.putExtra(EXTRA_MESSAGE, message)
|
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
}
|
|
}
|