glasses: пассивный Accessibility-сервис (авто-запуск + живучесть) + TASKS.md с планом
This commit is contained in:
@@ -79,6 +79,22 @@
|
||||
android:exported="false"
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
|
||||
<!-- Пассивный Accessibility-сервис (задача #5): живучесть процесса +
|
||||
легальный запуск Activity из фона. Ничего не читает и не инжектит.
|
||||
Активируется вручную: Настройки → Специальные возможности → RayNeo VM. -->
|
||||
<service
|
||||
android:name=".VmAccessibilityService"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
|
||||
<intent-filter>
|
||||
<action android:name="android.accessibilityservice.AccessibilityService" />
|
||||
</intent-filter>
|
||||
<meta-data
|
||||
android:name="android.accessibilityservice"
|
||||
android:resource="@xml/vm_accessibility_service" />
|
||||
</service>
|
||||
|
||||
<!-- VmShizukuService НЕ объявляется здесь как <service> — это Shizuku UserService,
|
||||
который ServiceStarter создаёт через reflection в отдельном isolated-процессе.
|
||||
Если объявить — Shizuku-сервер не сможет привести класс к IBinder
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">RayNeo VM</string>
|
||||
<string name="vm_accessibility_summary">RayNeo VM</string>
|
||||
<string name="vm_accessibility_description">Фоновый сервис RayNeo VM. Держит приложение работоспособным в фоне и позволяет ему самостоятельно запускаться. Не читает содержимое экрана и не управляет другими приложениями.</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Конфиг Accessibility-сервиса RayNeo VM.
|
||||
|
||||
Сервис — пассивный слушатель: он НЕ управляет чужими окнами и НЕ инжектит
|
||||
события. Нужен только как «легальный» живучий процесс, который система
|
||||
поднимает/перезапускает сама, и через который можно открывать Activity из фона.
|
||||
|
||||
Никаких eventTypes/feedbackType не запрашиваем сверх необходимого:
|
||||
eventTypes="typeWindowStateChanged" — минимально нужный, чтобы сервис считался
|
||||
активным и `onAccessibilityEvent` вызывался. canRetrieveWindowContent=false —
|
||||
содержимое окон нам не нужно, меньше привилегий.
|
||||
-->
|
||||
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:accessibilityEventTypes="typeWindowStateChanged"
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagDefault"
|
||||
android:canRetrieveWindowContent="false"
|
||||
android:description="@string/vm_accessibility_description"
|
||||
android:notificationTimeout="100"
|
||||
android:summary="@string/vm_accessibility_summary" />
|
||||
Reference in New Issue
Block a user