mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-09 17:11:32 -10:00
Preserve second-screen state when refreshing input profiles
This commit is contained in:
1 parent
f44826ae3a
commit
08526f8283
3 files changed
+102
-3
No files matched your search
@@ -59,6 +59,7 @@ class SecondScreenPresentation(
|
||||
private var imeSeen = false
|
||||
private var fieldKeyboard = false
|
||||
private var terminalPanel: SessionTerminalPanel? = null
|
||||
private var controlsView: InputSurfaceView? = null
|
||||
private var created = false
|
||||
|
||||
init {
|
||||
@@ -212,6 +213,7 @@ class SecondScreenPresentation(
|
||||
dropChat()
|
||||
keyboardHost?.hide()
|
||||
keyboardHost = null
|
||||
controlsView = null
|
||||
dismiss()
|
||||
}
|
||||
|
||||
@@ -234,7 +236,14 @@ class SecondScreenPresentation(
|
||||
return super.dispatchGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
fun refreshControls() { if (created) renderMode() }
|
||||
fun refreshControls() {
|
||||
if (!created || currentMode != SecondScreenMode.CUSTOM_CONTROLS) return
|
||||
val controls = controlsView ?: return
|
||||
val layout = com.droiddeck.launcher.input.InputLibraries.read(context)
|
||||
.activeLayout(SessionState.mode == SessionService.MODE_DESKTOP, com.droiddeck.launcher.input.InputRole.SECONDARY)
|
||||
controls.updateCustomLayout(layout?.elements.orEmpty())
|
||||
controls.updateStyle(layout?.style ?: com.droiddeck.launcher.input.InputSurfaceStyle())
|
||||
}
|
||||
|
||||
fun showKeyboard(): Boolean {
|
||||
val host = keyboardHost ?: return false
|
||||
@@ -263,6 +272,7 @@ class SecondScreenPresentation(
|
||||
closeTerminal()
|
||||
closeDashboard()
|
||||
root.removeAllViews()
|
||||
controlsView = null
|
||||
ownComposeViews()
|
||||
val library = com.droiddeck.launcher.input.InputLibraries.read(context)
|
||||
val profileLayout = library.activeLayout(SessionState.mode == SessionService.MODE_DESKTOP, com.droiddeck.launcher.input.InputRole.SECONDARY)
|
||||
@@ -277,8 +287,10 @@ class SecondScreenPresentation(
|
||||
when (mode) {
|
||||
SecondScreenMode.TERMINAL -> renderTerminal()
|
||||
SecondScreenMode.CUSTOM_CONTROLS -> {
|
||||
root.addView(InputSurfaceView(context, pad, profileLayout?.elements.orEmpty(),
|
||||
surfaceStyle = profileLayout?.style ?: com.droiddeck.launcher.input.InputSurfaceStyle()), weightParams())
|
||||
val controls = InputSurfaceView(context, pad, profileLayout?.elements.orEmpty(),
|
||||
surfaceStyle = profileLayout?.style ?: com.droiddeck.launcher.input.InputSurfaceStyle())
|
||||
controlsView = controls
|
||||
root.addView(controls, weightParams())
|
||||
attachKeyboard()
|
||||
}
|
||||
SecondScreenMode.KEYBOARD_TRACKPAD, SecondScreenMode.DECK_CONTROLS -> Unit
|
||||
|
||||
@@ -23,6 +23,13 @@ import com.droiddeck.launcher.input.PadBridgeRoutingTest
|
||||
import com.droiddeck.launcher.session.SessionService
|
||||
import com.droiddeck.launcher.session.SessionState
|
||||
import org.junit.After
|
||||
import com.droiddeck.launcher.input.InputLibraries
|
||||
import com.droiddeck.launcher.input.InputRole
|
||||
import com.droiddeck.launcher.input.InputSurfaceElement
|
||||
import com.droiddeck.launcher.input.InputSurfaceStyle
|
||||
import com.droiddeck.launcher.input.InputControl
|
||||
import org.junit.Assert.assertSame
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
@@ -141,6 +148,74 @@ class SessionSecondScreenTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun profileRefreshLeavesTerminalAndDashboardIntact() {
|
||||
val activity = Robolectric.buildActivity(SessionActivity::class.java).get()
|
||||
val display = activity.getSystemService(DisplayManager::class.java).getDisplay(Display.DEFAULT_DISPLAY)
|
||||
val panel = SecondScreenPresentation(activity, display, {}, {}, {}, dashboard = { androidx.compose.material3.Text("Dashboard") })
|
||||
try {
|
||||
panel.show()
|
||||
for (mode in listOf(SecondScreenMode.TERMINAL, SecondScreenMode.DASHBOARD)) {
|
||||
panel.showMode(mode)
|
||||
val root = panel.window!!.decorView
|
||||
val before = descendants(root).toList()
|
||||
val terminal: Any? = ReflectionHelpers.getField(panel, "terminalPanel")
|
||||
panel.refreshControls()
|
||||
assertEquals(before, descendants(root).toList())
|
||||
assertSame(terminal, ReflectionHelpers.getField<Any?>(panel, "terminalPanel"))
|
||||
}
|
||||
} finally { panel.closeControls() }
|
||||
}
|
||||
|
||||
@Test fun profileRefreshUpdatesControlsWithoutClosingTheFieldKeyboard() {
|
||||
SessionState.mode = SessionService.MODE_STEAM
|
||||
val activity = Robolectric.buildActivity(SessionActivity::class.java).get()
|
||||
val display = activity.getSystemService(DisplayManager::class.java).getDisplay(Display.DEFAULT_DISPLAY)
|
||||
var closed = 0
|
||||
val panel = SecondScreenPresentation(activity, display, {}, {}, {}, onKeyboardClosed = { closed++ })
|
||||
try {
|
||||
panel.show()
|
||||
panel.showMode(SecondScreenMode.CUSTOM_CONTROLS)
|
||||
panel.requestKeyboard(forField = true)
|
||||
val host: Any = ReflectionHelpers.getField(panel, "keyboardHost")
|
||||
val shown: MutableState<Boolean> = ReflectionHelpers.getField(panel, "keyboardShown")
|
||||
shown.value = true
|
||||
ReflectionHelpers.setField(panel, "imeSeen", true)
|
||||
val controls = descendants(panel.window!!.decorView).filterIsInstance<InputSurfaceView>().single()
|
||||
val element = InputSurfaceElement(InputControl.A, .3f, .2f, .2f, .2f, "updated")
|
||||
InputLibraries.update(activity) { library ->
|
||||
val layout = library.activeLayout(false, InputRole.SECONDARY)!!
|
||||
library.withLayout(layout.copy(elements = listOf(element), style = InputSurfaceStyle(opacity = 35)))
|
||||
}
|
||||
panel.refreshControls()
|
||||
assertSame(controls, descendants(panel.window!!.decorView).filterIsInstance<InputSurfaceView>().single())
|
||||
val child = descendants(controls).single { it.tag == element }
|
||||
assertEquals(.35f, child.alpha, .001f)
|
||||
assertSame(host, ReflectionHelpers.getField<Any>(panel, "keyboardHost"))
|
||||
assertTrue(panel.fieldKeyboardOpen)
|
||||
assertTrue(shown.value)
|
||||
assertTrue(ReflectionHelpers.getField<Boolean>(panel, "imeSeen"))
|
||||
assertEquals(0, closed)
|
||||
} finally { panel.closeControls() }
|
||||
}
|
||||
|
||||
@Test fun profileRefreshPreservesTheLowerScreenChatPanel() {
|
||||
val activity = Robolectric.buildActivity(SessionActivity::class.java).get()
|
||||
val display = activity.getSystemService(DisplayManager::class.java).getDisplay(Display.DEFAULT_DISPLAY)
|
||||
var lost = 0
|
||||
val panel = SecondScreenPresentation(activity, display, {}, {}, {})
|
||||
try {
|
||||
panel.show()
|
||||
panel.showMode(SecondScreenMode.CUSTOM_CONTROLS)
|
||||
panel.showChat(onLost = { lost++ }) { androidx.compose.material3.Text("Chat draft") }
|
||||
val chat: View? = ReflectionHelpers.getField(panel, "chatView")
|
||||
assertNotNull(chat)
|
||||
panel.refreshControls()
|
||||
assertSame(chat, ReflectionHelpers.getField<View?>(panel, "chatView"))
|
||||
assertTrue(descendants(panel.window!!.decorView).any { it === chat })
|
||||
assertEquals(0, lost)
|
||||
} finally { panel.closeControls() }
|
||||
}
|
||||
|
||||
@Test
|
||||
@Config(sdk = [34], shadows = [PadBridgeRoutingTest.RegisteredKeyEvent::class])
|
||||
fun controllerBumpersReachTheSecondScreenInBothSessionModes() {
|
||||
|
||||
@@ -113,6 +113,18 @@ runtime auto-update and Proton menu auto-selection. The gamescope p22 archive
|
||||
was downloaded from the newly pinned component repository and verified against
|
||||
upstream's SHA-256 before refreshing the local runtime inputs.
|
||||
|
||||
### Second-screen refresh follow-up, 2026-11-07
|
||||
|
||||
Refreshing a profile now updates the existing custom control surface in place.
|
||||
Terminal and dashboard modes are untouched, and the custom surface keeps its
|
||||
keyboard host, field-keyboard state and WoW chat panel. Actual mode changes
|
||||
continue to use the existing full teardown/rebuild path.
|
||||
|
||||
All **35 tests in 5 suites passed** across `SessionSecondScreenTest`, the
|
||||
surface UI suites and `InputSurfaceSourceTest`. Three new regressions cover
|
||||
terminal/dashboard identity, changed elements/style with an open field keyboard,
|
||||
and preservation of the lower-screen chat view and its dismissal callback.
|
||||
|
||||
## Input settings polish, 2026-11-06
|
||||
|
||||
The final input/UI host run passed **941 tests in 124 suites, zero failures**.
|
||||
|
||||
Reference in new issue
Block a user