From 7b7722bd25fb2d4e32c336124b3afbc068c46552 Mon Sep 17 00:00:00 2001 From: Delete228 Date: Fri, 6 Nov 2026 19:25:41 +0300 Subject: [PATCH] Preserve replacement session Deck input routing --- .../com/droiddeck/launcher/SessionActivity.kt | 2 +- .../droiddeck/launcher/input/DeckControls.kt | 5 +++ .../input/InputSourceOwnershipTest.kt | 35 +++++++++++++++++++ docs/development/unified-input-validation.md | 7 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt index 8fcf4afba..4be3d31cd 100644 --- a/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt +++ b/app/src/main/java/com/droiddeck/launcher/SessionActivity.kt @@ -5115,7 +5115,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback { padBridge?.setInputEnabled(false) physicalInputBindings?.close() padBridge?.stop() - DeckControls.setRouter(null) + DeckControls.clearRouter(inputRouter) inputRouter = null heldKeys.clear() heldButtons.clear() diff --git a/app/src/main/java/com/droiddeck/launcher/input/DeckControls.kt b/app/src/main/java/com/droiddeck/launcher/input/DeckControls.kt index 10cdd434c..41b724036 100644 --- a/app/src/main/java/com/droiddeck/launcher/input/DeckControls.kt +++ b/app/src/main/java/com/droiddeck/launcher/input/DeckControls.kt @@ -23,6 +23,11 @@ object DeckControls { publish() } + @Synchronized + fun clearRouter(owner: InputRouter?) { + if (owner != null && router === owner) setRouter(null) + } + @Synchronized @JvmOverloads fun source(role: InputRole, routed: Boolean = false) = Source(role, routed) diff --git a/app/src/test/java/com/droiddeck/launcher/input/InputSourceOwnershipTest.kt b/app/src/test/java/com/droiddeck/launcher/input/InputSourceOwnershipTest.kt index 3465b7210..b200a195d 100644 --- a/app/src/test/java/com/droiddeck/launcher/input/InputSourceOwnershipTest.kt +++ b/app/src/test/java/com/droiddeck/launcher/input/InputSourceOwnershipTest.kt @@ -136,6 +136,41 @@ class InputSourceOwnershipTest { } finally { surface.close() } } + @Test fun clearingOldDeckOwnerPreservesReplacementBindingsAndHeldKeys() { + val previous = InputRouter(RecordingOutput()) + val output = RecordingOutput() + val replacement = InputRouter(output).also { + it.setProfile(InputProfile.defaults(InputProfile.GAME) + .withBinding(InputRole.PHYSICAL, InputControl.L4, InputAction.Keys(listOf(57)))) + } + DeckControls.setRouter(previous) + DeckControls.setRouter(replacement) + DeckControls.setGrip(DeckControls.L4, true) + DeckControls.clearRouter(previous) + DeckControls.clearRouter(null) + assertEquals(listOf("key 57 true"), output.events) + DeckControls.setGrip(DeckControls.L4, false) + DeckControls.setGrip(DeckControls.L4, true) + assertEquals(listOf("key 57 true", "key 57 false", "key 57 true"), output.events) + assertEquals(0, JoyConPadTest.RecordingWriter.grips) + } + + @Test fun clearingCurrentDeckOwnerReleasesItsKeysAndRemovesBindings() { + val output = RecordingOutput() + val router = InputRouter(output).also { + it.setProfile(InputProfile.defaults(InputProfile.GAME) + .withBinding(InputRole.PHYSICAL, InputControl.L4, InputAction.Keys(listOf(57)))) + } + DeckControls.setRouter(router) + DeckControls.setGrip(DeckControls.L4, true) + DeckControls.clearRouter(router) + DeckControls.clearRouter(router) + DeckControls.setGrip(DeckControls.L4, false) + DeckControls.setGrip(DeckControls.L4, true) + assertEquals(listOf("key 57 true", "key 57 false"), output.events) + assertEquals(DeckControls.L4, JoyConPadTest.RecordingWriter.grips) + } + @Test fun releasingAllKeepsPhysicalGripsBlockedUntilNeutral() { val router = InputRouter(RecordingOutput()) bridge.setInputRouter(router) diff --git a/docs/development/unified-input-validation.md b/docs/development/unified-input-validation.md index 0f8d31ac3..33a52649d 100644 --- a/docs/development/unified-input-validation.md +++ b/docs/development/unified-input-validation.md @@ -42,6 +42,13 @@ Actual display captures and test reports are retained locally under `app/build/reports/input-publish/`. Screenshot publication is subject to the user's review. No new latency measurement was made for this UI revision. +The follow-up router-ownership fix passed **47 tests** in `InputSourceOwnershipTest`, +`InputSurfaceSourceTest`, and `InputRouterTest`, including two new regressions. +An obsolete activity (or one without a router) cannot detach the replacement +activity's Deck routes; the current owner still releases its held keys on cleanup. +This follow-up changes teardown only and was not reinstalled or retested on Thor, +which was no longer available over ADB after the screenshot pass. + ## Input settings polish, 2026-11-06 The final input/UI host run passed **941 tests in 124 suites, zero failures**.