Skip to content

Commit d85600a

Browse files
authored
Replace PC movement size with pointer speed
Merge Android pointer speed parsing, picker UI, and command support.
1 parent 20fb228 commit d85600a

15 files changed

Lines changed: 647 additions & 216 deletions

File tree

app/src/main/java/com/enaboapps/switchify/backend/preferences/PreferenceManager.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ class PreferenceManager(context: Context) {
2525
const val PREFERENCE_KEY_PAUSE_TIMEOUT = "pause_timeout"
2626
const val PREFERENCE_KEY_MOVE_REPEAT = "move_repeat"
2727
const val PREFERENCE_KEY_MOVE_REPEAT_DELAY = "move_repeat_delay"
28-
const val PREFERENCE_KEY_PC_MOUSE_MOVEMENT_SIZE = "pc_mouse_movement_size"
2928
const val PREFERENCE_KEY_PC_CONTROL_SURFACE = "pc_control_surface"
3029
const val PREFERENCE_KEY_PC_MOUSE_REPEAT = "pc_mouse_repeat"
3130
const val PREFERENCE_KEY_PC_MOUSE_REPEAT_INTERVAL = "pc_mouse_repeat_interval"

app/src/main/java/com/enaboapps/switchify/components/Picker.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import androidx.compose.runtime.remember
3030
import androidx.compose.runtime.setValue
3131
import androidx.compose.ui.Alignment
3232
import androidx.compose.ui.Modifier
33+
import androidx.compose.ui.draw.alpha
3334
import androidx.compose.ui.res.stringResource
3435
import androidx.compose.ui.text.font.FontWeight
3536
import androidx.compose.ui.unit.dp
@@ -44,7 +45,8 @@ fun <T> Picker(
4445
items: List<T>,
4546
onItemSelected: (T) -> Unit,
4647
itemToString: (T) -> String,
47-
itemDescription: (T) -> String
48+
itemDescription: (T) -> String,
49+
enabled: Boolean = true
4850
) {
4951
var expanded by remember { mutableStateOf(false) }
5052

@@ -69,10 +71,12 @@ fun <T> Picker(
6971
.heightIn(min = 56.dp)
7072
.background(triggerColor)
7173
.clickable(
74+
enabled = enabled,
7275
interactionSource = interactionSource,
7376
indication = null,
7477
onClick = { expanded = true }
7578
)
79+
.alpha(if (enabled) 1f else 0.55f)
7680
.padding(Dimens.spaceM),
7781
horizontalArrangement = Arrangement.spacedBy(Dimens.spaceM),
7882
verticalAlignment = Alignment.CenterVertically

app/src/main/java/com/enaboapps/switchify/pc/PcConnector.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ sealed class PcControlCommand {
3131
data class ModifierDown(val key: PcKeyboardModifierKey) : PcControlCommand()
3232
data class ModifierUp(val key: PcKeyboardModifierKey) : PcControlCommand()
3333
data class WindowControl(val action: PcWindowControlAction) : PcControlCommand()
34+
data class SetPointerSpeed(val scalePercent: Double) : PcControlCommand()
3435
data object LeftClick : PcControlCommand()
3536
data object DoubleClick : PcControlCommand()
3637
data object RightClick : PcControlCommand()

app/src/main/java/com/enaboapps/switchify/pc/PcPointerProfile.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,19 @@ data class PcPointerCapabilities(
1313
val noAckMouseMove: Boolean = false,
1414
val noAckCommands: Set<String> = emptySet(),
1515
val supportedCommands: Set<String> = emptySet(),
16-
val mouseRepeat: PcMouseRepeatCapabilities = PcMouseRepeatCapabilities()
16+
val mouseRepeat: PcMouseRepeatCapabilities = PcMouseRepeatCapabilities(),
17+
val pointerSpeed: PcPointerSpeedCapabilities = PcPointerSpeedCapabilities()
18+
)
19+
20+
data class PcPointerSpeedCapabilities(
21+
val supported: Boolean = false,
22+
val setSupported: Boolean = false,
23+
val scalePercent: Double = 100.0,
24+
val minScalePercent: Double = 5.0,
25+
val maxScalePercent: Double = 225.0,
26+
val stepPercent: Double = 5.0,
27+
val baseMoveDelta: Int = 128,
28+
val effectiveMoveDelta: Int = 128
1729
)
1830

1931
data class PcMouseRepeatCapabilities(
@@ -56,3 +68,9 @@ fun PcPointerMovementProfile.supportsModifierToggle(): Boolean {
5668
)
5769
)
5870
}
71+
72+
fun PcPointerMovementProfile.pointerMoveStep(): Int {
73+
val speed = capabilities.pointerSpeed
74+
val candidate = if (speed.supported) speed.baseMoveDelta else recommendedDeltas.medium
75+
return candidate.coerceIn(1, maxDelta)
76+
}

app/src/main/java/com/enaboapps/switchify/pc/PcProtocol.kt

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,23 @@ object PcProtocol {
9191
)
9292
}
9393

94+
fun pointerSpeedSet(
95+
id: String,
96+
deviceId: String,
97+
token: String,
98+
timestamp: Long,
99+
scalePercent: Double
100+
): String {
101+
return authenticatedCommand(
102+
id = id,
103+
deviceId = deviceId,
104+
token = token,
105+
timestamp = timestamp,
106+
type = "pointer.speed.set",
107+
payload = JSONObject().put("scalePercent", jsonNumber(scalePercent))
108+
)
109+
}
110+
94111
fun authenticatedCommand(
95112
id: String,
96113
deviceId: String,
@@ -114,6 +131,10 @@ object PcProtocol {
114131
return message.toString()
115132
}
116133

134+
private fun jsonNumber(value: Double): Number {
135+
return if (value.isFinite() && value % 1.0 == 0.0) value.toInt() else value
136+
}
137+
117138
fun mouseMove(
118139
id: String,
119140
deviceId: String,
@@ -568,7 +589,8 @@ object PcProtocol {
568589
noAckMouseMove = capabilitiesJson?.optBoolean("noAckMouseMove", false) ?: false,
569590
noAckCommands = noAckCommands,
570591
supportedCommands = supportedCommands,
571-
mouseRepeat = parseMouseRepeatCapabilities(capabilitiesJson) ?: return PcProtocolResponse.Invalid
592+
mouseRepeat = parseMouseRepeatCapabilities(capabilitiesJson) ?: return PcProtocolResponse.Invalid,
593+
pointerSpeed = parsePointerSpeedCapabilities(capabilitiesJson) ?: return PcProtocolResponse.Invalid
572594
)
573595
val bounds = PcPointerBounds(
574596
x = boundsJson.optInt("x"),
@@ -626,6 +648,52 @@ object PcProtocol {
626648
)
627649
}
628650

651+
private fun parsePointerSpeedCapabilities(capabilitiesJson: JSONObject?): PcPointerSpeedCapabilities? {
652+
val speedJson = capabilitiesJson?.opt("pointerSpeed") ?: return PcPointerSpeedCapabilities()
653+
if (speedJson !is JSONObject) return null
654+
if (speedJson.has("supported") && speedJson.opt("supported") !is Boolean) return null
655+
if (speedJson.has("setSupported") && speedJson.opt("setSupported") !is Boolean) return null
656+
657+
val scalePercent = speedJson.optDouble("scalePercent", 100.0)
658+
val minScalePercent = speedJson.optDouble("minScalePercent", 5.0)
659+
val maxScalePercent = speedJson.optDouble("maxScalePercent", 225.0)
660+
val stepPercent = speedJson.optDouble("stepPercent", 5.0)
661+
val baseMoveDeltaValue = speedJson.opt("baseMoveDelta")
662+
val effectiveMoveDeltaValue = speedJson.opt("effectiveMoveDelta")
663+
if (baseMoveDeltaValue != null && baseMoveDeltaValue !is Number) return null
664+
if (effectiveMoveDeltaValue != null && effectiveMoveDeltaValue !is Number) return null
665+
val baseMoveDelta = baseMoveDeltaValue?.toInt() ?: 128
666+
val effectiveMoveDelta = effectiveMoveDeltaValue?.toInt() ?: baseMoveDelta
667+
668+
if (
669+
!scalePercent.isFinite() ||
670+
!minScalePercent.isFinite() ||
671+
!maxScalePercent.isFinite() ||
672+
!stepPercent.isFinite() ||
673+
scalePercent <= 0.0 ||
674+
minScalePercent <= 0.0 ||
675+
maxScalePercent < minScalePercent ||
676+
scalePercent < minScalePercent ||
677+
scalePercent > maxScalePercent ||
678+
stepPercent <= 0.0 ||
679+
baseMoveDelta <= 0 ||
680+
effectiveMoveDelta <= 0
681+
) {
682+
return null
683+
}
684+
685+
return PcPointerSpeedCapabilities(
686+
supported = speedJson.optBoolean("supported", false),
687+
setSupported = speedJson.optBoolean("setSupported", false),
688+
scalePercent = scalePercent,
689+
minScalePercent = minScalePercent,
690+
maxScalePercent = maxScalePercent,
691+
stepPercent = stepPercent,
692+
baseMoveDelta = baseMoveDelta,
693+
effectiveMoveDelta = effectiveMoveDelta
694+
)
695+
}
696+
629697
private fun parseNoAckCommands(capabilitiesJson: JSONObject?): Set<String>? {
630698
if (capabilitiesJson == null || !capabilitiesJson.has("noAckCommands")) return emptySet()
631699
val commandsJson = capabilitiesJson.opt("noAckCommands")
@@ -681,6 +749,7 @@ object PcProtocol {
681749
"connection.ping",
682750
"connection.disconnecting",
683751
"pointer.profile",
752+
"pointer.speed.set",
684753
"mouse.repeat.start",
685754
"mouse.repeat.stop",
686755
"keyboard.textStream.open",

app/src/main/java/com/enaboapps/switchify/pc/bluetooth/SwitchifyPcBleClient.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ class SwitchifyPcBleClient(
315315
is PcControlCommand.ModifierDown -> PcProtocol.keyboardModifierDown(id, deviceId, token, timestamp, key, responseMode)
316316
is PcControlCommand.ModifierUp -> PcProtocol.keyboardModifierUp(id, deviceId, token, timestamp, key, responseMode)
317317
is PcControlCommand.WindowControl -> PcProtocol.windowControl(id, deviceId, token, timestamp, action, responseMode)
318+
is PcControlCommand.SetPointerSpeed -> PcProtocol.pointerSpeedSet(id, deviceId, token, timestamp, scalePercent)
318319
}
319320
}
320321

@@ -424,6 +425,7 @@ private fun PcControlCommand.protocolType(): String {
424425
is PcControlCommand.ModifierDown -> "keyboard.modifierDown"
425426
is PcControlCommand.ModifierUp -> "keyboard.modifierUp"
426427
is PcControlCommand.WindowControl -> "window.control"
428+
is PcControlCommand.SetPointerSpeed -> "pointer.speed.set"
427429
}
428430
}
429431

0 commit comments

Comments
 (0)