feat: partage d'écran Android 5+ (MediaProjection) et blocage des installations
- Partage d'écran par MediaProjection (ScreenCaptureService + ScreenConsentActivity) quand la capture par accessibilité n'est pas possible (Android < 11 ou service coupé). En mode propriétaire, le service d'accessibilité valide seul le consentement système. - Blocage de l'installation d'applications (Play Store compris) piloté par le parent (policy block_installs → OwnerPolicy DISALLOW_INSTALL_APPS en mode propriétaire). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R9CBCVP5oDu2sfoYPisNCQ
This commit is contained in:
1 parent
665d6e2cb3
commit
d4aa0e1962
14 files changed
+328
-17
No files matched your search
@@ -3,8 +3,10 @@ package com.jttools.datacontrol.remote
|
||||
import android.app.admin.DevicePolicyManager
|
||||
import android.content.Context
|
||||
import android.media.AudioManager
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import com.jttools.datacontrol.admin.AdminController
|
||||
import com.jttools.datacontrol.guard.AppGuardService
|
||||
import com.jttools.datacontrol.data.SettingsRepository
|
||||
import com.jttools.datacontrol.guard.ParentalLockActivity
|
||||
import com.jttools.datacontrol.log.AppLog
|
||||
@@ -43,6 +45,9 @@ class RemoteControl @Inject constructor(
|
||||
|
||||
val screenSharing: Boolean get() = _screenShareUntil.value > SystemClock.elapsedRealtime()
|
||||
|
||||
/** Vrai pendant qu'on attend le consentement MediaProjection (le dialogue système est à l'écran). */
|
||||
@Volatile var awaitingScreenConsent = false
|
||||
|
||||
private var started = false
|
||||
|
||||
fun start(scope: CoroutineScope) {
|
||||
@@ -88,14 +93,11 @@ class RemoteControl @Inject constructor(
|
||||
"punlock" -> parentalLock(false)
|
||||
"mute" -> setMuted(true)
|
||||
"unmute" -> setMuted(false)
|
||||
"screen_start" -> {
|
||||
val seconds = cmd.optInt("seconds", 120).coerceIn(10, 300)
|
||||
_screenShareUntil.value = SystemClock.elapsedRealtime() + seconds * 1000L
|
||||
AppLog.i("Commande", "Partage d'écran démarré ($seconds s)")
|
||||
"done" to "partage démarré"
|
||||
}
|
||||
"screen_start" -> startScreenShare(cmd.optInt("seconds", 120).coerceIn(10, 300))
|
||||
"screen_stop" -> {
|
||||
_screenShareUntil.value = 0L
|
||||
awaitingScreenConsent = false
|
||||
ScreenCaptureService.stop(context)
|
||||
AppLog.i("Commande", "Partage d'écran arrêté")
|
||||
"done" to ""
|
||||
}
|
||||
@@ -123,6 +125,22 @@ class RemoteControl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
/** Démarre le partage d'écran : capture silencieuse par l'accessibilité (Android 11+) ou MediaProjection. */
|
||||
private fun startScreenShare(seconds: Int): Pair<String, String> {
|
||||
_screenShareUntil.value = SystemClock.elapsedRealtime() + seconds * 1000L
|
||||
val silent = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && AppGuardService.isEnabled(context)
|
||||
if (silent) {
|
||||
awaitingScreenConsent = false
|
||||
AppLog.i("Commande", "Partage d'écran démarré ($seconds s, accessibilité)")
|
||||
} else if (!ScreenCaptureService.running) {
|
||||
awaitingScreenConsent = true
|
||||
runCatching { context.startActivity(ScreenConsentActivity.intent(context)) }
|
||||
.onFailure { AppLog.w("Commande", "Lancement du partage MediaProjection impossible : ${it.message}") }
|
||||
AppLog.i("Commande", "Partage d'écran démarré ($seconds s, MediaProjection)")
|
||||
}
|
||||
return "done" to "partage démarré"
|
||||
}
|
||||
|
||||
private fun lock(): Pair<String, String> {
|
||||
val dpm = context.getSystemService(DevicePolicyManager::class.java)
|
||||
if (!AdminController.isActive(context)) {
|
||||
|
||||
Reference in new issue
Block a user