glasses: пассивный Accessibility-сервис (авто-запуск + живучесть) + TASKS.md с планом

This commit is contained in:
2026-09-27 05:46:14 +03:00
parent 9914488258
commit 2125788e25
5 changed files with 264 additions and 4 deletions
@@ -0,0 +1,54 @@
package pw.binom.rayneovm.glasses
import android.accessibilityservice.AccessibilityService
import android.content.Intent
import android.util.Log
import android.view.accessibility.AccessibilityEvent
/**
* Пассивный Accessibility-сервис GlassesApp.
*
* Зачем: Accessibility-сервис — единственный «легальный» способ для обычного
* приложения (без root/Shizuku/system-app) получить:
* - живучесть процесса: систему сама поднимает и перезапускает сервис,
* - возможность открывать Activity из фона,
* - [onAccessibilityEvent] (в будущем — для отслеживания фокуса ввода и клавиатуры, задача #4).
*
* ВАЖНО: сервис НИЧЕГО не делает с чужими окнами — не читает содержимое
* ([android:canRetrieveWindowContent] = false) и не инжектит события. Это
* сознательно: минимум привилегий, минимум риска сломать ввод в гостевых приложениях.
*
* Регистрируется в манифесте через [VM_ACCESSIBILITY_SERVICE] с конфигом
* `res/xml/vm_accessibility_service.xml`. Активируется пользователем вручную:
* Настройки → Специальные возможности → RayNeo VM.
*/
class VmAccessibilityService : AccessibilityService() {
override fun onServiceConnected() {
super.onServiceConnected()
Log.i(TAG, "Accessibility-сервис подключён")
// Авто-старт: если основное приложение ещё не поднято — поднимаем его.
// Это и есть «легальный авто-запуск» из задачи #5.
runCatching { startActivity(Intent(this, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}) }
.onSuccess { Log.i(TAG, "MainActivity запущена из Accessibility-сервиса") }
.onFailure { Log.w(TAG, "Не удалось запустить MainActivity: ${it.message}") }
}
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
// Намеренно пусто. Слушаем только для того, чтобы сервис считался живым.
// Тут позже появится логика задачи #4 (фокус поля ввода → скрыть IME гостя).
}
override fun onInterrupt() {
// Ничего: сервис не выполняет длительных операций.
}
companion object {
private const val TAG = "VmAccessibilitySvc"
/** Действие для запроса состояния сервиса/проверки активности (диагностика). */
const val ACTION_PING = "pw.binom.rayneovm.glasses.ACCESSIBILITY_PING"
}
}