- Firebase et le pare-feu local (trou noir, liste blanche par application, coupure cyclique locale) sont retirés - Client WireGuard officiel (GoBackend) : tout le trafic passe par le serveur familyd, qui filtre - Appairage par code ou lien familyd:// ; clé privée WireGuard et jeton chiffrés par l'Android Keystore - Remontées par le tunnel : état, temps d'écran, applications installées, évènements (file persistante) - Mode propriétaire (Device Owner) : VPN toujours actif avec blocage hors VPN, réglages verrouillés, garde-fou si le serveur ne répond plus pendant 15 min - Plan de la refonte : docs/PLAN-wireguard.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ
133 lines
5.5 KiB
Kotlin
133 lines
5.5 KiB
Kotlin
package com.jttools.datacontrol.ui
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.NotificationManager
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.net.Uri
|
|
import android.net.VpnService
|
|
import android.os.Build
|
|
import android.os.PowerManager
|
|
import android.provider.Settings
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.result.ActivityResultLauncher
|
|
import androidx.activity.result.contract.ActivityResultContracts
|
|
import androidx.core.content.ContextCompat
|
|
import com.jttools.datacontrol.vpn.TunnelManager
|
|
|
|
/**
|
|
* Regroupe les autorisations système nécessaires (VPN, notifications, batterie) pour que
|
|
* les écrans Compose puissent les demander sans dépendre directement de l'Activity.
|
|
*/
|
|
class SystemActions(private val activity: ComponentActivity) {
|
|
|
|
private var onVpnResult: ((Boolean) -> Unit)? = null
|
|
private var onNotificationResult: ((Boolean) -> Unit)? = null
|
|
|
|
private val vpnLauncher: ActivityResultLauncher<Intent> = activity.registerForActivityResult(
|
|
ActivityResultContracts.StartActivityForResult()
|
|
) { result ->
|
|
onVpnResult?.invoke(result.resultCode == android.app.Activity.RESULT_OK)
|
|
}
|
|
|
|
private val notificationLauncher: ActivityResultLauncher<String> = activity.registerForActivityResult(
|
|
ActivityResultContracts.RequestPermission()
|
|
) { granted -> onNotificationResult?.invoke(granted) }
|
|
|
|
private var onLocationResult: ((Boolean) -> Unit)? = null
|
|
private var onBackgroundLocationResult: ((Boolean) -> Unit)? = null
|
|
private var onAdminResult: ((Boolean) -> Unit)? = null
|
|
|
|
private val locationLauncher: ActivityResultLauncher<Array<String>> = activity.registerForActivityResult(
|
|
ActivityResultContracts.RequestMultiplePermissions()
|
|
) { granted -> onLocationResult?.invoke(granted.values.any { it }) }
|
|
|
|
private val backgroundLocationLauncher: ActivityResultLauncher<String> = activity.registerForActivityResult(
|
|
ActivityResultContracts.RequestPermission()
|
|
) { granted -> onBackgroundLocationResult?.invoke(granted) }
|
|
|
|
private val adminLauncher: ActivityResultLauncher<Intent> = activity.registerForActivityResult(
|
|
ActivityResultContracts.StartActivityForResult()
|
|
) { result -> onAdminResult?.invoke(result.resultCode == android.app.Activity.RESULT_OK) }
|
|
|
|
fun hasLocationPermission(): Boolean = ContextCompat.checkSelfPermission(
|
|
activity, android.Manifest.permission.ACCESS_FINE_LOCATION
|
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
|
|
fun hasBackgroundLocationPermission(): Boolean =
|
|
Build.VERSION.SDK_INT < Build.VERSION_CODES.Q || ContextCompat.checkSelfPermission(
|
|
activity, android.Manifest.permission.ACCESS_BACKGROUND_LOCATION
|
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
|
|
fun requestLocationPermission(onResult: (Boolean) -> Unit) {
|
|
onLocationResult = onResult
|
|
locationLauncher.launch(
|
|
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION)
|
|
)
|
|
}
|
|
|
|
/** À demander APRÈS la position au premier plan (exigence Android 11+). */
|
|
fun requestBackgroundLocationPermission(onResult: (Boolean) -> Unit) {
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { onResult(true); return }
|
|
onBackgroundLocationResult = onResult
|
|
backgroundLocationLauncher.launch(android.Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
|
}
|
|
|
|
fun isGuardEnabled(): Boolean = com.jttools.datacontrol.guard.AppGuardService.isEnabled(activity)
|
|
|
|
fun openAccessibilitySettings() {
|
|
activity.startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS))
|
|
}
|
|
|
|
fun isAdminActive(): Boolean = com.jttools.datacontrol.admin.AdminController.isActive(activity)
|
|
|
|
fun requestAdmin(onResult: (Boolean) -> Unit) {
|
|
onAdminResult = onResult
|
|
adminLauncher.launch(com.jttools.datacontrol.admin.AdminController.requestIntent(activity))
|
|
}
|
|
|
|
fun deactivateAdmin() = com.jttools.datacontrol.admin.AdminController.deactivate(activity)
|
|
|
|
fun isVpnPrepared(): Boolean = TunnelManager.isPrepared(activity)
|
|
|
|
fun requestVpnPermission(onResult: (Boolean) -> Unit) {
|
|
val intent = VpnService.prepare(activity)
|
|
if (intent == null) {
|
|
onResult(true)
|
|
} else {
|
|
onVpnResult = onResult
|
|
vpnLauncher.launch(intent)
|
|
}
|
|
}
|
|
|
|
fun hasNotificationPermission(): Boolean {
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return true
|
|
return ContextCompat.checkSelfPermission(
|
|
activity, android.Manifest.permission.POST_NOTIFICATIONS
|
|
) == android.content.pm.PackageManager.PERMISSION_GRANTED
|
|
}
|
|
|
|
fun requestNotificationPermission(onResult: (Boolean) -> Unit) {
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
onResult(true)
|
|
return
|
|
}
|
|
onNotificationResult = onResult
|
|
notificationLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
|
|
}
|
|
|
|
fun isIgnoringBatteryOptimizations(): Boolean {
|
|
val pm = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
return pm.isIgnoringBatteryOptimizations(activity.packageName)
|
|
}
|
|
|
|
@SuppressLint("BatteryLife")
|
|
fun requestIgnoreBatteryOptimizations() {
|
|
val intent = Intent(
|
|
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
|
Uri.parse("package:${activity.packageName}")
|
|
)
|
|
activity.startActivity(intent)
|
|
}
|
|
}
|