fix(android): auto-wake opt-out + console glyphs default by form factor
An mDNS discovery miss was forcing connects through the Wake-on-LAN wait
even for a host that's already up; add a Settings toggle ("Auto-wake on
connect") that skips the mDNS-liveness gate and dials straight through
when off.
Also default the console UI's button glyphs by form factor instead of
always starting in TV-remote style: a phone/tablet only ever enters the
console UI via a real controller, so it should show gamepad glyphs from
the first frame, not a remote's select/back glyphs. TV keeps the remote
default.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -212,11 +212,12 @@ fun ConnectScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wake-aware connect. If the target is a saved host with a learned MAC that ISN'T currently
|
// Wake-aware connect. If auto-wake is on (Settings.autoWakeEnabled) and the target is a saved
|
||||||
// advertising (asleep/off), wake it and WAIT for it to reappear on mDNS (WakeController shows the
|
// host with a learned MAC that ISN'T currently advertising (asleep/off, or just missing from
|
||||||
// "Waking…" overlay) before dialing — discovery stays running meanwhile so we can see it come
|
// mDNS), wake it and WAIT for it to reappear on mDNS (WakeController shows the "Waking…" overlay)
|
||||||
// back. A fire-and-forget packet + the connect timeout wasn't enough for a cold boot. Otherwise
|
// before dialing — discovery stays running meanwhile so we can see it come back. A fire-and-forget
|
||||||
// dial straight through.
|
// packet + the connect timeout wasn't enough for a cold boot. Otherwise (auto-wake off, no MAC, or
|
||||||
|
// already seen live) dial straight through.
|
||||||
fun doConnect(targetHost: String, targetPort: Int, name: String, pinHex: String?) {
|
fun doConnect(targetHost: String, targetPort: Int, name: String, pinHex: String?) {
|
||||||
if (identity == null) {
|
if (identity == null) {
|
||||||
status = "Identity not ready yet — try again in a moment"
|
status = "Identity not ready yet — try again in a moment"
|
||||||
@@ -230,7 +231,7 @@ fun ConnectScreen(
|
|||||||
fun liveAdvert(): DiscoveredHost? =
|
fun liveAdvert(): DiscoveredHost? =
|
||||||
if (kh != null) discovered.firstOrNull { kh.matches(it) }
|
if (kh != null) discovered.firstOrNull { kh.matches(it) }
|
||||||
else discovered.firstOrNull { it.host == targetHost && it.port == targetPort }
|
else discovered.firstOrNull { it.host == targetHost && it.port == targetPort }
|
||||||
if (macs.isNotEmpty() && liveAdvert() == null) {
|
if (settings.autoWakeEnabled && macs.isNotEmpty() && liveAdvert() == null) {
|
||||||
waker.start(
|
waker.start(
|
||||||
hostName = name,
|
hostName = name,
|
||||||
connectsAfter = true,
|
connectsAfter = true,
|
||||||
|
|||||||
@@ -51,8 +51,12 @@ class MainActivity : ComponentActivity() {
|
|||||||
* Whether the last console input came from a real gamepad (face buttons / stick) vs. a TV D-pad
|
* Whether the last console input came from a real gamepad (face buttons / stick) vs. a TV D-pad
|
||||||
* remote (which has no A/B/X/Y). The console UI reads this to show glyphs the user recognises — pad
|
* remote (which has no A/B/X/Y). The console UI reads this to show glyphs the user recognises — pad
|
||||||
* face buttons, or a select glyph + arrows for a remote. Compose observes it (a snapshot state).
|
* face buttons, or a select glyph + arrows for a remote. Compose observes it (a snapshot state).
|
||||||
|
* Defaults to the remote glyphs on a TV (its D-pad remote is the typical first input, and often the
|
||||||
|
* only one) and to gamepad glyphs everywhere else (the console UI on a phone/tablet only activates
|
||||||
|
* via a real controller, so a TV-remote glyph would be a wrong first impression there) — set from
|
||||||
|
* [onCreate] once a [Context] is available, then kept live by real input.
|
||||||
*/
|
*/
|
||||||
var lastPadIsGamepad by mutableStateOf(false)
|
var lastPadIsGamepad by mutableStateOf(true)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
/** The panel's highest-refresh display mode (0 = unknown/unsupported), resolved once at startup. */
|
/** The panel's highest-refresh display mode (0 = unknown/unsupported), resolved once at startup. */
|
||||||
@@ -60,6 +64,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
lastPadIsGamepad = !isTvDevice(this)
|
||||||
resolveHighRefreshMode()
|
resolveHighRefreshMode()
|
||||||
setConsoleHighRefreshRate(true) // the console UI wants max refresh; streaming manages its own
|
setConsoleHighRefreshRate(true) // the console UI wants max refresh; streaming manages its own
|
||||||
// Dark, transparent system bars regardless of the system theme — our UI is always dark, so
|
// Dark, transparent system bars regardless of the system theme — our UI is always dark, so
|
||||||
|
|||||||
@@ -62,6 +62,15 @@ data class Settings(
|
|||||||
* lock and HDMI game-mode signalling stay on regardless — they're harmless.
|
* lock and HDMI game-mode signalling stay on regardless — they're harmless.
|
||||||
*/
|
*/
|
||||||
val lowLatencyMode: Boolean = true,
|
val lowLatencyMode: Boolean = true,
|
||||||
|
/**
|
||||||
|
* Wake-on-LAN a saved host before connecting when it isn't currently seen on mDNS. On (default):
|
||||||
|
* a connect to a host with a learned MAC that isn't advertising sends a magic packet and waits
|
||||||
|
* for it to reappear (see [WakeController]) before dialing. Off: always dial straight through,
|
||||||
|
* skipping the mDNS-presence check entirely — for a host that's actually up but not visible on
|
||||||
|
* mDNS (a flaky discovery path, a VLAN/subnet that blocks multicast, etc.), where auto-wake would
|
||||||
|
* otherwise misfire and wait out its timeout despite the host already being reachable.
|
||||||
|
*/
|
||||||
|
val autoWakeEnabled: Boolean = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** [Settings.touchMode] values; persisted by name. */
|
/** [Settings.touchMode] values; persisted by name. */
|
||||||
@@ -91,6 +100,7 @@ class SettingsStore(context: Context) {
|
|||||||
gamepadUiEnabled = prefs.getBoolean(K_GAMEPAD_UI, true),
|
gamepadUiEnabled = prefs.getBoolean(K_GAMEPAD_UI, true),
|
||||||
libraryEnabled = prefs.getBoolean(K_LIBRARY, true),
|
libraryEnabled = prefs.getBoolean(K_LIBRARY, true),
|
||||||
lowLatencyMode = prefs.getBoolean(K_LOW_LATENCY, true),
|
lowLatencyMode = prefs.getBoolean(K_LOW_LATENCY, true),
|
||||||
|
autoWakeEnabled = prefs.getBoolean(K_AUTO_WAKE, true),
|
||||||
)
|
)
|
||||||
|
|
||||||
fun save(s: Settings) {
|
fun save(s: Settings) {
|
||||||
@@ -110,6 +120,7 @@ class SettingsStore(context: Context) {
|
|||||||
.putBoolean(K_GAMEPAD_UI, s.gamepadUiEnabled)
|
.putBoolean(K_GAMEPAD_UI, s.gamepadUiEnabled)
|
||||||
.putBoolean(K_LIBRARY, s.libraryEnabled)
|
.putBoolean(K_LIBRARY, s.libraryEnabled)
|
||||||
.putBoolean(K_LOW_LATENCY, s.lowLatencyMode)
|
.putBoolean(K_LOW_LATENCY, s.lowLatencyMode)
|
||||||
|
.putBoolean(K_AUTO_WAKE, s.autoWakeEnabled)
|
||||||
.apply()
|
.apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +140,7 @@ class SettingsStore(context: Context) {
|
|||||||
const val K_GAMEPAD_UI = "gamepad_ui_enabled"
|
const val K_GAMEPAD_UI = "gamepad_ui_enabled"
|
||||||
const val K_LIBRARY = "library_enabled"
|
const val K_LIBRARY = "library_enabled"
|
||||||
const val K_LOW_LATENCY = "low_latency_mode"
|
const val K_LOW_LATENCY = "low_latency_mode"
|
||||||
|
const val K_AUTO_WAKE = "auto_wake_enabled"
|
||||||
|
|
||||||
/** Legacy Boolean the enum replaced — read once as the migration default, never written. */
|
/** Legacy Boolean the enum replaced — read once as the migration default, never written. */
|
||||||
const val K_TRACKPAD = "trackpad_mode"
|
const val K_TRACKPAD = "trackpad_mode"
|
||||||
|
|||||||
@@ -394,6 +394,14 @@ private fun InterfaceSettings(s: Settings, update: (Settings) -> Unit) {
|
|||||||
checked = s.libraryEnabled,
|
checked = s.libraryEnabled,
|
||||||
onCheckedChange = { on -> update(s.copy(libraryEnabled = on)) },
|
onCheckedChange = { on -> update(s.copy(libraryEnabled = on)) },
|
||||||
)
|
)
|
||||||
|
ToggleRow(
|
||||||
|
title = "Auto-wake on connect",
|
||||||
|
subtitle = "Send Wake-on-LAN and wait for a saved host to reappear on mDNS before " +
|
||||||
|
"connecting. Turn off if a host that's already on isn't seen on mDNS, so connects " +
|
||||||
|
"go straight through instead of waiting out the wake timeout.",
|
||||||
|
checked = s.autoWakeEnabled,
|
||||||
|
onCheckedChange = { on -> update(s.copy(autoWakeEnabled = on)) },
|
||||||
|
)
|
||||||
ToggleRow(
|
ToggleRow(
|
||||||
title = "Stats overlay",
|
title = "Stats overlay",
|
||||||
subtitle = "Show FPS, throughput and latency while streaming (3-finger tap toggles it live)",
|
subtitle = "Show FPS, throughput and latency while streaming (3-finger tap toggles it live)",
|
||||||
|
|||||||
Reference in New Issue
Block a user