03f1d7dcc7
- 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+)
93 lines
3.6 KiB
Kotlin
93 lines
3.6 KiB
Kotlin
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
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.layout.Box
|
|
import androidx.compose.foundation.layout.fillMaxSize
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.darkColorScheme
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.CompositionLocalProvider
|
|
import androidx.compose.runtime.DisposableEffect
|
|
import androidx.compose.runtime.LaunchedEffect
|
|
import androidx.compose.runtime.collectAsState
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import pw.binom.rayneovm.glasses.protocol.VmGlassesController
|
|
import pw.binom.rayneovm.glasses.ui.GlassesScreen
|
|
import pw.binom.rayneovm.glasses.vm.GlassesScreenState
|
|
|
|
class MainActivity : ComponentActivity() {
|
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
super.onCreate(savedInstanceState)
|
|
enableEdgeToEdge()
|
|
ShizukuBootstrap.attach(this)
|
|
val controller = VmGlassesController.fromContext(this)
|
|
|
|
setContent {
|
|
CompositionLocalProvider(VmGlassesController.Local provides controller) {
|
|
MaterialTheme(colorScheme = darkColorScheme()) {
|
|
Box(
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.background(Color.Black)
|
|
) {
|
|
val shizukuState by ShizukuBootstrap.state.collectAsState()
|
|
DriveShizukuState(controller, shizukuState)
|
|
GlassesScreen(
|
|
controller = controller,
|
|
onRequestShizukuPermission = { ShizukuBootstrap.refresh() },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
override fun onResume() {
|
|
super.onResume()
|
|
ShizukuBootstrap.refresh()
|
|
}
|
|
|
|
override fun onRequestPermissionsResult(
|
|
requestCode: Int,
|
|
permissions: Array<String>,
|
|
grantResults: IntArray,
|
|
) {
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
if (requestCode == ShizukuBootstrap.REQUEST_CODE_PERMISSION) {
|
|
val grant = grantResults.firstOrNull() ?: -1
|
|
ShizukuBootstrap.onRequestPermissionResult(requestCode, grant)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private fun DriveShizukuState(controller: VmGlassesController, shizuku: ShizukuBootstrap.AllowState) {
|
|
LaunchedEffect(shizuku) {
|
|
when (shizuku) {
|
|
ShizukuBootstrap.AllowState.NotRunning,
|
|
ShizukuBootstrap.AllowState.Waiting,
|
|
ShizukuBootstrap.AllowState.NeedsPermission -> {
|
|
if (controller.state.value !is GlassesScreenState.Running) {
|
|
controller.setState(GlassesScreenState.ShizukuMissing)
|
|
}
|
|
}
|
|
ShizukuBootstrap.AllowState.Granted -> {
|
|
// Если мы сейчас в ShizukuMissing — переходим в NoAppSelected.
|
|
// Running не трогаем: приложение уже запущено, Shizuku-статус не должен его ронять.
|
|
if (controller.state.value is GlassesScreenState.ShizukuMissing) {
|
|
controller.setState(GlassesScreenState.NoAppSelected)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|