package com.shukria.kiosklauncher.service import android.accessibilityservice.AccessibilityService import android.accessibilityservice.GestureDescription import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.graphics.Path import android.util.DisplayMetrics import android.util.Log import android.view.WindowManager import android.view.accessibility.AccessibilityEvent import com.shukria.kiosklauncher.MyDeviceAdminReceiver class RemoteControlService : AccessibilityService() { companion object { private const val TAG = "RemoteControlService" private var instance: RemoteControlService? = null fun injectTap(xFraction: Float, yFraction: Float) { val svc = instance ?: run { Log.w(TAG, "injectTap: service not running. Enable via:\n" + " adb shell settings put secure enabled_accessibility_services " + "com.shukria.kiosklauncher/.service.RemoteControlService\n" + " adb shell settings put secure accessibility_enabled 1") return } svc.performTap(xFraction, yFraction) } fun isRunning() = instance != null /** * Silently enables this AccessibilityService using Device Owner privileges. * No user interaction or adb required. Safe to call on every app start — idempotent. * Falls back with a log message if the app is not the Device Owner. */ fun autoEnable(context: Context) { val dpm = context.getSystemService(Context.DEVICE_POLICY_SERVICE) as DevicePolicyManager val admin = ComponentName(context, MyDeviceAdminReceiver::class.java) if (!dpm.isDeviceOwnerApp(context.packageName)) { Log.w(TAG, "Not Device Owner — enable manually: Settings > Accessibility > TMS Remote Control") return } val serviceName = "${context.packageName}/.service.RemoteControlService" try { dpm.setPermittedAccessibilityServices(admin, null) dpm.setSecureSetting(admin, "enabled_accessibility_services", serviceName) dpm.setSecureSetting(admin, "accessibility_enabled", "1") Log.i(TAG, "RemoteControlService auto-enabled via Device Owner") } catch (e: Exception) { Log.e(TAG, "Auto-enable failed: ${e.message}") } } } override fun onServiceConnected() { super.onServiceConnected() instance = this Log.i(TAG, "RemoteControlService connected — remote touch injection ready") } override fun onAccessibilityEvent(event: AccessibilityEvent?) {} override fun onInterrupt() {} override fun onDestroy() { instance = null super.onDestroy() } @Suppress("DEPRECATION") private fun performTap(xFraction: Float, yFraction: Float) { val wm = getSystemService(WINDOW_SERVICE) as WindowManager val metrics = DisplayMetrics().also { wm.defaultDisplay.getRealMetrics(it) } val screenX = xFraction * metrics.widthPixels val screenY = yFraction * metrics.heightPixels val path = Path().apply { moveTo(screenX, screenY) } val stroke = GestureDescription.StrokeDescription(path, 0L, 50L) val gesture = GestureDescription.Builder().addStroke(stroke).build() val dispatched = dispatchGesture(gesture, object : GestureResultCallback() { override fun onCompleted(gestureDescription: GestureDescription) { Log.v(TAG, "Tap injected at screen (${screenX.toInt()}, ${screenY.toInt()})") } override fun onCancelled(gestureDescription: GestureDescription) { Log.w(TAG, "Tap cancelled at (${screenX.toInt()}, ${screenY.toInt()})") } }, null) if (!dispatched) { Log.w(TAG, "dispatchGesture returned false — check canPerformGestures in accessibility config") } } }