mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-11 19:11:08 +00:00
Keep display and runtime driver controls visible in mix mode
(cherry picked from commit 0422eb44193cdcebf7a9f22dfff367b1441dc40e)
This commit is contained in:
1 parent
77e23cb397
commit
c334ef309b
2 files changed
+90
-6
No files matched your search
@@ -3,6 +3,8 @@ package com.droiddeck.launcher.ui
|
||||
import com.droiddeck.launcher.R
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.focusGroup
|
||||
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.layout.LayoutCoordinates
|
||||
import androidx.compose.ui.layout.boundsInRoot
|
||||
@@ -19,6 +21,7 @@ import androidx.compose.material.icons.outlined.SportsEsports
|
||||
import androidx.compose.material.icons.outlined.ExpandLess
|
||||
import androidx.compose.material.icons.outlined.ExpandMore
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.withFrameNanos
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
@@ -209,7 +212,7 @@ private fun DeviceInfoSection(section: DeviceInfo.Section) {
|
||||
|
||||
private const val STALE_MS = 6 * 3_600_000L
|
||||
|
||||
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class)
|
||||
@OptIn(androidx.compose.ui.ExperimentalComposeUiApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun GpuDriversBody(
|
||||
gpu: GpuDriversState,
|
||||
@@ -224,6 +227,13 @@ internal fun GpuDriversBody(
|
||||
}
|
||||
val scroll = rememberScrollState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val driverControls = remember { BringIntoViewRequester() }
|
||||
LaunchedEffect(gpu.mode) {
|
||||
if (gpu.mode == GPU_MODE_MIX) {
|
||||
withFrameNanos { }
|
||||
driverControls.bringIntoView()
|
||||
}
|
||||
}
|
||||
val placed = remember { arrayOfNulls<LayoutCoordinates>(2) }
|
||||
// Focusing the top mode shows as much of the in-use card above it as fits, without hiding the mode itself.
|
||||
val revealCard = {
|
||||
@@ -251,7 +261,10 @@ internal fun GpuDriversBody(
|
||||
stringResource(title), stringResource(detail), gpu.mode == id,
|
||||
modifier = mod.then(if (gpu.mode == id) Modifier.focusRequester(first) else Modifier)
|
||||
.then(if (i == 0) Modifier.onGloballyPositioned { placed[1] = it }.onFocusChanged { if (it.isFocused) revealCard() } else Modifier),
|
||||
onClick = { if (gpu.mode != id) actions.onMode(id) },
|
||||
onClick = {
|
||||
if (gpu.mode != id) actions.onMode(id)
|
||||
else if (id == GPU_MODE_MIX) scope.launch { driverControls.bringIntoView() }
|
||||
},
|
||||
)
|
||||
}
|
||||
if (single) Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { modes.indices.forEach { choice(it, Modifier) } }
|
||||
@@ -261,7 +274,7 @@ internal fun GpuDriversBody(
|
||||
when (gpu.mode) {
|
||||
GPU_MODE_AUTO -> AutoBody(gpu)
|
||||
GPU_MODE_PAIR -> PairBody(gpu, actions)
|
||||
else -> MixBody(single, actions, slotFocus, onOpenSlot)
|
||||
else -> MixBody(single, actions, slotFocus, onOpenSlot, Modifier.bringIntoViewRequester(driverControls))
|
||||
}
|
||||
gpu.tpUbwcMode?.let { selected ->
|
||||
var advanced by rememberSaveable { mutableStateOf(false) }
|
||||
@@ -395,7 +408,7 @@ private fun PairBody(gpu: GpuDriversState, actions: GpuDriversActions) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MixBody(single: Boolean, actions: GpuDriversActions, slotFocus: Map<String, FocusRequester>, onOpenSlot: (String, Rect) -> Unit) {
|
||||
private fun MixBody(single: Boolean, actions: GpuDriversActions, slotFocus: Map<String, FocusRequester>, onOpenSlot: (String, Rect) -> Unit, controlsModifier: Modifier) {
|
||||
val pal = LocalPalette.current
|
||||
Column(verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
PanelTitle(stringResource(R.string.gpu_mix_title), stringResource(R.string.gpu_mix_hint))
|
||||
@@ -410,8 +423,8 @@ private fun MixBody(single: Boolean, actions: GpuDriversActions, slotFocus: Map<
|
||||
slotButton(SLOT_DISPLAY, R.string.gpu_change_display, mod)
|
||||
slotButton(SLOT_RUNTIME, R.string.gpu_change_runtime, mod)
|
||||
}
|
||||
if (single) Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { buttons(Modifier.fillMaxWidth()) }
|
||||
else Row(horizontalArrangement = Arrangement.spacedBy(10.dp)) { buttons(Modifier.weight(1f)) }
|
||||
if (single) Column(controlsModifier.fillMaxWidth(), verticalArrangement = Arrangement.spacedBy(8.dp)) { buttons(Modifier.fillMaxWidth()) }
|
||||
else Row(controlsModifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(10.dp)) { buttons(Modifier.weight(1f)) }
|
||||
FocusText(stringResource(R.string.comp_import_zip), pal.signal, onClick = actions.onImportZip)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performScrollTo
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.droiddeck.launcher.R
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.RuntimeEnvironment
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [28], qualifiers = "w960dp-h400dp-land")
|
||||
class GpuDriverControlsVisibilityTest {
|
||||
@get:Rule val compose = createRobolectricComposeRule()
|
||||
private var openedSlot: String? = null
|
||||
|
||||
@Test fun openingMixedModeRevealsBothDriverButtonsOnAShortScreen() {
|
||||
show(GPU_MODE_MIX)
|
||||
assertControlsVisible()
|
||||
compose.onNodeWithText(text(R.string.gpu_change_display)).performClick()
|
||||
assertEquals(SLOT_DISPLAY, openedSlot)
|
||||
}
|
||||
|
||||
@Test fun selectingMixedModeRevealsItsControlsBelowTheModeChoices() {
|
||||
show(GPU_MODE_AUTO)
|
||||
compose.onNodeWithText(text(R.string.gpu_mode_mix)).performScrollTo().performClick()
|
||||
assertControlsVisible()
|
||||
compose.onNodeWithText(text(R.string.gpu_change_runtime)).performClick()
|
||||
assertEquals(SLOT_RUNTIME, openedSlot)
|
||||
}
|
||||
|
||||
private fun assertControlsVisible() {
|
||||
compose.onNodeWithText(text(R.string.gpu_change_display)).assertIsDisplayed()
|
||||
compose.onNodeWithText(text(R.string.gpu_change_runtime)).assertIsDisplayed()
|
||||
}
|
||||
|
||||
private fun text(id: Int) = RuntimeEnvironment.getApplication().getString(id)
|
||||
|
||||
private fun show(initialMode: String) {
|
||||
val mode = mutableStateOf(initialMode)
|
||||
val first = FocusRequester()
|
||||
val slots = mapOf(SLOT_DISPLAY to FocusRequester(), SLOT_RUNTIME to FocusRequester())
|
||||
compose.setContent {
|
||||
DroidDeckTheme {
|
||||
Box(Modifier.requiredSize(480.dp, 280.dp)) {
|
||||
GpuDriversBody(
|
||||
gpu = GpuDriversState(
|
||||
gpuName = "Adreno 830", gpuFamily = "Adreno 8xx", mode = mode.value,
|
||||
display = DriverSlot(name = "Automatic", status = "Applies after restarting DroidDeck"),
|
||||
runtime = DriverSlot(name = "System Qualcomm driver", status = "Applies at the next session start"),
|
||||
),
|
||||
actions = GpuDriversActions(onMode = { mode.value = it }),
|
||||
single = true, first = first, slotFocus = slots,
|
||||
onOpenSlot = { slot, _ -> openedSlot = slot },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user