glasses: launch guest on Shizuku VirtualDisplay + binocular render

- VmShizukuService is a Shizuku UserService only; remove its <service> manifest entry (ServiceStarter instantiates it reflectively, an Android service entry causes ClassCastException)
- VirtualDisplayFrameSource: await binder via ShizukuVdClient.bindForResult(5s), expose virtualDisplayId + shizukuBinder, stop the UserService on release
- VmGlassesController: serialize acquire via Mutex, use double-buffered SurfaceTexture (DisplayManagerService rejects single-buffer), launch the guest on the VD with ShizukuVdBinder.launchActivity
- GlassesScreen: render controller.frameSource (Running) directly; drop the LaunchedEffect acquire
- DebugTestReceiver: register at runtime from MainActivity (background manifest receivers are blocked on Android 12+)
This commit is contained in:
2026-09-25 17:19:49 +03:00
parent 5afdde70e1
commit 03f1d7dcc7
6 changed files with 112 additions and 89 deletions
@@ -1,5 +1,7 @@
package pw.binom.rayneovm.glasses
import android.content.Intent
import android.content.IntentFilter
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
@@ -26,10 +28,6 @@ class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// По доке Shizuku (https://github.com/RikkaApps/Shizuku-API): подписываемся на
// listener-ы здесь, после super.onCreate, но ДО setContent. ShizukuProvider
// (rikka.shizuku.ShizukuProvider в манифесте) уже инициализировал binder через
// ContentProvider.onCreate — pingBinder() сразу вернёт true.
ShizukuBootstrap.attach(this)
val controller = VmGlassesController.fromContext(this)
@@ -42,18 +40,11 @@ class MainActivity : ComponentActivity() {
.background(Color.Black)
) {
val shizukuState by ShizukuBootstrap.state.collectAsState()
val vmState by controller.state.collectAsState()
DriveShizukuState(controller, shizukuState)
GlassesScreen(
controller = controller,
onRequestShizukuPermission = { ShizukuBootstrap.refresh() },
)
// Как только VM ушёл в Running (запущен гость) — сворачиваем свой Activity,
// чтобы SurfaceView гостя оказался поверх и был виден на очках.
// Service продолжает жить в фоне (WebSocket/mDNS не рвутся).
if (vmState is GlassesScreenState.Running) {
(this as? ComponentActivity)?.finish()
}
}
}
}
@@ -65,10 +56,6 @@ class MainActivity : ComponentActivity() {
ShizukuBootstrap.refresh()
}
/**
* По доке Shizuku, мы должны вручную передать результат диалога в Shizuku -
* иначе он не узнает, что пользователь нажал Allow.
*/
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,