glasses: list all installed launchable apps on phone (Android 11+ queries)
- Add <intent MAIN+LAUNCHER> to <queries> so queryIntentActivities on the glasses side returns third-party apps (free.zona etc), not only system / RayNeo system apps. Without this entry Android 11+ package visibility filter strips apps whose package is not explicitly named. - Pass MATCH_ALL | GET_RESOLVED_FILTER to queryIntentActivities as a belt-and-suspenders measure (no visible effect with queries above, but safe on Android 12+ where the platform may still filter). - Default VmGlassesController state is NoAppSelected instead of Starting, so we don't show 'Подготавливаем очки...' on first launch when no LaunchAppCommand has been received yet. - DriveShizukuState leaves Running state alone on re-check (don't drop a running guest back to ShizukuMissing just because Shizuku was re-polled).
This commit is contained in:
@@ -29,6 +29,9 @@ import pw.binom.rayneovm.shared.Shutdown
|
||||
import pw.binom.rayneovm.shared.TouchEvent
|
||||
import pw.binom.rayneovm.shared.VmMessage
|
||||
import pw.binom.rayneovm.shared.VmProtocol
|
||||
import pw.binom.rayneovm.shared.GetAppList
|
||||
import pw.binom.rayneovm.shared.AppEntry
|
||||
import pw.binom.rayneovm.shared.AppListResponse
|
||||
|
||||
/**
|
||||
* Foreground service на стороне очков.
|
||||
@@ -111,16 +114,28 @@ class VmService : LifecycleService() {
|
||||
when (msg) {
|
||||
is LaunchAppCommand -> {
|
||||
Log.i(TAG, "LaunchAppCommand: ${msg.packageName}")
|
||||
// Реальный запуск — через Shizuku service (см. VmShizukuService).
|
||||
// Пока — переключаем состояние для UI.
|
||||
controller.setState(GlassesScreenState.Running(msg.packageName))
|
||||
// Реальный запуск: VirtualDisplay + launchActivity через Shizuku.
|
||||
// acquireFrameSource сам освобождает предыдущий VirtualDisplay, если был.
|
||||
controller.launchGuest(msg.packageName)
|
||||
}
|
||||
is TouchEvent -> Log.d(TAG, "Touch: ${msg.actions.size}")
|
||||
is VmKeyEvent -> Log.d(TAG, "Key: ${msg.keyCode}")
|
||||
is SetAudioMode -> Log.d(TAG, "SetAudioMode: ${msg.mode}")
|
||||
is Shutdown -> {
|
||||
Log.i(TAG, "Shutdown")
|
||||
controller.setState(GlassesScreenState.NoAppSelected)
|
||||
controller.stopGuest()
|
||||
}
|
||||
is GetAppList -> {
|
||||
Log.i(TAG, "GetAppList")
|
||||
val apps = listInstalledLaunchableApps()
|
||||
val response = AppListResponse(apps = apps)
|
||||
val json = VmProtocol.encode(response)
|
||||
try {
|
||||
server!!.send(connId, json)
|
||||
Log.i(TAG, "Sent app list: ${apps.size} apps")
|
||||
} catch (t: Throwable) {
|
||||
Log.e(TAG, "Failed to send app list", t)
|
||||
}
|
||||
}
|
||||
is Ping -> server?.send(connId, VmProtocol.encode(Pong))
|
||||
else -> Log.d(TAG, "Unhandled: $msg")
|
||||
@@ -218,6 +233,23 @@ class VmService : LifecycleService() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun listInstalledLaunchableApps(): List<AppEntry> {
|
||||
val pm = packageManager
|
||||
val mainIntent = android.content.Intent(android.content.Intent.ACTION_MAIN)
|
||||
.addCategory(android.content.Intent.CATEGORY_LAUNCHER)
|
||||
// MATCH_ALL: на Android 11+ без `<queries>` для конкретного пакета обычные флаги
|
||||
// могут не возвращать часть приложений. free.zona — пример.
|
||||
val flags = android.content.pm.PackageManager.MATCH_ALL or
|
||||
android.content.pm.PackageManager.GET_RESOLVED_FILTER
|
||||
val resolved = pm.queryIntentActivities(mainIntent, flags)
|
||||
return resolved.map { ri ->
|
||||
AppEntry(
|
||||
packageName = ri.activityInfo.packageName,
|
||||
displayName = ri.loadLabel(pm).toString(),
|
||||
)
|
||||
}.sortedBy { it.displayName.lowercase() }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val WS_PORT = 8080
|
||||
const val WS_PATH = "/vm"
|
||||
|
||||
Reference in New Issue
Block a user