phone: keep-alive foreground service + notification perm; glasses: clear NoConnection on reconnect

- ConnectionKeepAliveService (foreground, PARTIAL_WAKE_LOCK + WIFI_MODE_FULL_HIGH_PERF,
  started in PhoneVmApp.onCreate): without it HONOR deep-sleeps the phone process on
  screen-off and the glasses lose the WebSocket link. Same mechanic as view-mate.
- MainActivity: request POST_NOTIFICATIONS so the FGS notification is visible.
- VmGlassesController.onPhoneConnected(): drop a stuck NoConnection state when the
  phone (re)connects; returns to Running if a guest is active, else NoAppSelected.
- VmService: call it from onConnected.
- stopGuest/switch: force-stop the previous guest before releasing its display
  (releasing first migrates the guest task to display 0 full-screen); VirtualDisplay
  frame source now destroys its UserService on release.
- Mercury status: retry the first pullGeneralStatus until it lands, refresh every 15s
  and on tab open; GlassesStatusScreen shows 'Ждём статус от очков…' instead of fake zeros.
- MainActivity(glasses): HOME candidate flag + finish() on a non-default display (recursion guard).
- README: Home-launcher setup/rollback section.
- BOOTSTRAP_TEST.md: post-reboot Shizuku bootstrap investigation log.
This commit is contained in:
2026-09-26 13:59:54 +03:00
parent 94f0ed28fc
commit 59069a741e
13 changed files with 436 additions and 12 deletions
@@ -3,6 +3,7 @@ package pw.binom.rayneovm.glasses
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import android.view.Display
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@@ -27,6 +28,14 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// HOME живёт на display 0. Если систему (или гостя на VirtualDisplay)
// угораздит запросить домашний экран внутри чужого дисплея — мы бы
// открылись сами внутри себя (зеркальная рекурсия), поэтому выходим.
val displayId = display?.displayId ?: Display.DEFAULT_DISPLAY
if (displayId != Display.DEFAULT_DISPLAY) {
finish()
return
}
enableEdgeToEdge()
ShizukuBootstrap.attach(this)
val controller = VmGlassesController.fromContext(this)