Skip to content

Commit 32ac6ce

Browse files
committed
feat(TouchController): Add vibration kind support
1 parent 6d1c136 commit 32ac6ce

9 files changed

Lines changed: 199 additions & 35 deletions

File tree

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/launch/GameLauncher.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,11 @@ class GameLauncher(
220220

221221
private fun tryStartTouchProxy() {
222222
if (version.isTouchProxyEnabled()) {
223-
ControllerProxy.startProxy(activity, version.getTouchVibrateDuration())
223+
ControllerProxy.startProxy(
224+
context = activity,
225+
vibrateDuration = version.getTouchVibrateDuration(),
226+
vibrateKind = version.getTouchVibrateKind(),
227+
)
224228
}
225229
}
226230

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/support/touch_controller/ControllerProxy.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ object ControllerProxy {
4141
/**
4242
* 启动控制代理客户端,目的是与 TouchController 模组进行通信
4343
*/
44-
fun startProxy(context: Context, vibrateDuration: Int?) {
44+
fun startProxy(
45+
context: Context,
46+
vibrateDuration: Int?,
47+
vibrateKind: VibrationHandler.VibrateKind?,
48+
) {
4549
if (proxyClient.value == null) {
4650
try {
4751
val transport = UnixSocketTransport(InfoDistributor.LAUNCHER_NAME)
@@ -51,7 +55,7 @@ object ControllerProxy {
5155
capabilities = setOf(PlatformCapability.TEXT_STATUS),
5256
)
5357
val vibrator = context.getSystemService(Vibrator::class.java)
54-
val handler = VibrationHandler(vibrator, vibrateDuration)
58+
val handler = VibrationHandler(vibrator, vibrateDuration, vibrateKind)
5559
client.vibrationHandler = handler
5660
client.run()
5761
LoggerBridge.append("TouchController: TouchController Proxy Client has been created!")

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/support/touch_controller/VibrationHandler.kt

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
package com.movtery.zalithlauncher.game.support.touch_controller
2020

21+
import android.annotation.SuppressLint
22+
import android.os.Build
2123
import android.os.VibrationEffect
2224
import android.os.Vibrator
2325
import com.movtery.zalithlauncher.utils.logging.Logger.lError
@@ -27,17 +29,49 @@ import top.fifthlight.touchcontroller.proxy.message.VibrateMessage
2729
class VibrationHandler(
2830
private val vibrator: Vibrator,
2931
private val vibrateDuration: Int?,
32+
private val vibrateKind: VibrateKind?,
3033
) : LauncherProxyClient.VibrationHandler {
31-
override fun vibrate(kind: VibrateMessage.Kind) {
32-
runCatching {
33-
val duration = vibrateDuration?.coerceAtMost(500)?.coerceAtLeast(80)?.toLong()
34-
val effect = VibrationEffect.createOneShot(
35-
duration ?: 100L,
36-
VibrationEffect.DEFAULT_AMPLITUDE
37-
)
38-
vibrator.vibrate(effect)
39-
}.onFailure {
40-
lError("Failed to attempt vibrating the device!", it)
34+
enum class VibrateKind {
35+
ONE_SHOT,
36+
CLICK,
37+
DOUBLE_CLICK,
38+
HEAVY_CLICK,
39+
TICK,
40+
}
41+
42+
companion object {
43+
@SuppressLint("NewApi")
44+
fun vibrate(
45+
vibrator: Vibrator,
46+
vibrateDuration: Int?,
47+
vibrateKind: VibrateKind?,
48+
) {
49+
runCatching {
50+
val effectKind = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
51+
VibrateKind.ONE_SHOT
52+
} else {
53+
vibrateKind
54+
}
55+
val effect = when (effectKind) {
56+
VibrateKind.ONE_SHOT, null -> {
57+
val duration = vibrateDuration?.coerceAtMost(500)?.coerceAtLeast(80)?.toLong()
58+
VibrationEffect.createOneShot(
59+
duration ?: 100L,
60+
VibrationEffect.DEFAULT_AMPLITUDE
61+
)
62+
}
63+
64+
VibrateKind.CLICK -> VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK)
65+
VibrateKind.DOUBLE_CLICK -> VibrationEffect.createPredefined(VibrationEffect.EFFECT_DOUBLE_CLICK)
66+
VibrateKind.HEAVY_CLICK -> VibrationEffect.createPredefined(VibrationEffect.EFFECT_HEAVY_CLICK)
67+
VibrateKind.TICK -> VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK)
68+
}
69+
vibrator.vibrate(effect)
70+
}.onFailure {
71+
lError("Failed to attempt vibrating the device!", it)
72+
}
4173
}
4274
}
75+
76+
override fun vibrate(kind: VibrateMessage.Kind) = vibrate(vibrator, vibrateDuration, vibrateKind)
4377
}

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/Version.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.movtery.zalithlauncher.BuildConfig
2727
import com.movtery.zalithlauncher.context.GlobalContext
2828
import com.movtery.zalithlauncher.game.path.getGameHome
2929
import com.movtery.zalithlauncher.game.path.getVersionsHome
30+
import com.movtery.zalithlauncher.game.support.touch_controller.VibrationHandler
3031
import com.movtery.zalithlauncher.path.PathManager
3132
import com.movtery.zalithlauncher.setting.AllSettings
3233
import com.movtery.zalithlauncher.setting.unit.getOrMin
@@ -176,4 +177,6 @@ class Version(
176177
fun isTouchProxyEnabled(): Boolean = versionConfig.enableTouchProxy
177178

178179
fun getTouchVibrateDuration(): Int? = versionConfig.touchVibrateDuration.takeIf { it >= 80 }
180+
181+
fun getTouchVibrateKind(): VibrationHandler.VibrateKind = versionConfig.touchVibrateKind
179182
}

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/VersionConfig.kt

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import android.os.Parcel
2222
import android.os.Parcelable
2323
import com.google.gson.annotations.SerializedName
2424
import com.movtery.zalithlauncher.R
25+
import com.movtery.zalithlauncher.game.support.touch_controller.VibrationHandler
2526
import com.movtery.zalithlauncher.game.version.installed.VersionsManager.getZalithVersionPath
2627
import com.movtery.zalithlauncher.setting.AllSettings
2728
import com.movtery.zalithlauncher.utils.GSON
@@ -97,6 +98,8 @@ class VersionConfig(
9798
var enableTouchProxy: Boolean = false
9899
@SerializedName("touchVibrateDuration")
99100
var touchVibrateDuration: Int = 100
101+
@SerializedName("touchVibrateKind")
102+
var touchVibrateKind: VibrationHandler.VibrateKind = VibrationHandler.VibrateKind.ONE_SHOT
100103

101104
constructor(
102105
filePath: File,
@@ -113,7 +116,8 @@ class VersionConfig(
113116
serverIp: String = "",
114117
ramAllocation: Int = -1,
115118
enableTouchProxy: Boolean = false,
116-
touchVibrateDuration: Int = 100
119+
touchVibrateDuration: Int = 100,
120+
touchVibrateKind: VibrationHandler.VibrateKind = VibrationHandler.VibrateKind.ONE_SHOT,
117121
) : this(filePath) {
118122
this.isolationType = isolationType
119123
this.skipGameIntegrityCheck = skipGameIntegrityCheck
@@ -129,6 +133,7 @@ class VersionConfig(
129133
this.ramAllocation = ramAllocation
130134
this.enableTouchProxy = enableTouchProxy
131135
this.touchVibrateDuration = touchVibrateDuration
136+
this.touchVibrateKind = touchVibrateKind
132137
}
133138

134139
fun copy(): VersionConfig = VersionConfig(
@@ -146,7 +151,8 @@ class VersionConfig(
146151
getStringNotNull(serverIp),
147152
ramAllocation,
148153
enableTouchProxy,
149-
touchVibrateDuration
154+
touchVibrateDuration,
155+
touchVibrateKind,
150156
)
151157

152158
fun save() {
@@ -205,6 +211,7 @@ class VersionConfig(
205211
writeInt(ramAllocation)
206212
writeInt(enableTouchProxy.getInt())
207213
writeInt(touchVibrateDuration)
214+
writeInt(touchVibrateKind.ordinal)
208215
}
209216
}
210217

@@ -225,6 +232,8 @@ class VersionConfig(
225232
val ramAllocation = parcel.readInt()
226233
val enableTouchProxy = parcel.readInt().toBoolean()
227234
val touchVibrateDuration = parcel.readInt()
235+
val touchVibrateKind = VibrationHandler.VibrateKind.entries.getOrNull(parcel.readInt())
236+
?: VibrationHandler.VibrateKind.ONE_SHOT
228237

229238
return VersionConfig(
230239
versionPath,
@@ -241,7 +250,8 @@ class VersionConfig(
241250
serverIp,
242251
ramAllocation,
243252
enableTouchProxy,
244-
touchVibrateDuration
253+
touchVibrateDuration,
254+
touchVibrateKind,
245255
)
246256
}
247257

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/settings/layouts/Enum.kt

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
2929
import androidx.compose.material3.RadioButton
3030
import androidx.compose.material3.Text
3131
import androidx.compose.runtime.Composable
32+
import androidx.compose.runtime.NonRestartableComposable
3233
import androidx.compose.ui.Alignment
3334
import androidx.compose.ui.Modifier
3435
import androidx.compose.ui.draw.alpha
@@ -43,7 +44,7 @@ import kotlin.enums.EnumEntries
4344

4445
@Composable
4546
fun <E: Enum<E>> EnumSettingsCard(
46-
unit: EnumSettingUnit<E>,
47+
value: E,
4748
entries: EnumEntries<E>,
4849
title: String,
4950
position: CardPosition,
@@ -57,7 +58,6 @@ fun <E: Enum<E>> EnumSettingsCard(
5758
titleStyle: TextStyle = MaterialTheme.typography.titleSmall,
5859
summaryStyle: TextStyle = MaterialTheme.typography.labelSmall,
5960
onRadioClick: (E) -> Unit = {},
60-
onValueChange: (E) -> Unit = {},
6161
) {
6262
SettingsCard(
6363
modifier = modifier,
@@ -89,12 +89,9 @@ fun <E: Enum<E>> EnumSettingsCard(
8989
val radioText = getRadioText(enum)
9090
RadioButton(
9191
enabled = getRadioEnable(enum),
92-
selected = unit.state == enum,
92+
selected = value == enum,
9393
onClick = {
9494
onRadioClick(enum)
95-
if (unit.state == enum) return@RadioButton
96-
unit.save(enum)
97-
onValueChange(enum)
9895
}
9996
)
10097
Text(
@@ -109,4 +106,44 @@ fun <E: Enum<E>> EnumSettingsCard(
109106
}
110107
}
111108
}
112-
}
109+
}
110+
111+
@NonRestartableComposable
112+
@Composable
113+
fun <E: Enum<E>> EnumSettingsCard(
114+
unit: EnumSettingUnit<E>,
115+
entries: EnumEntries<E>,
116+
title: String,
117+
position: CardPosition,
118+
modifier: Modifier = Modifier,
119+
outerShape: Dp = 28.dp,
120+
innerShape: Dp = 4.dp,
121+
summary: String? = null,
122+
getRadioText: @Composable (E) -> String,
123+
getRadioEnable: (E) -> Boolean,
124+
maxItemsInEachRow: Int = Int.MAX_VALUE,
125+
titleStyle: TextStyle = MaterialTheme.typography.titleSmall,
126+
summaryStyle: TextStyle = MaterialTheme.typography.labelSmall,
127+
onRadioClick: (E) -> Unit = {},
128+
onValueChange: (E) -> Unit = {},
129+
) = EnumSettingsCard(
130+
value = unit.state,
131+
entries = entries,
132+
title = title,
133+
position = position,
134+
modifier = modifier,
135+
outerShape = outerShape,
136+
innerShape = innerShape,
137+
summary = summary,
138+
getRadioText = getRadioText,
139+
getRadioEnable = getRadioEnable,
140+
maxItemsInEachRow = maxItemsInEachRow,
141+
titleStyle = titleStyle,
142+
summaryStyle = summaryStyle,
143+
onRadioClick = { enum ->
144+
onRadioClick(enum)
145+
if (unit.state == enum) return@EnumSettingsCard
146+
unit.save(enum)
147+
onValueChange(enum)
148+
},
149+
)

ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/versions/VersionConfigScreen.kt

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
package com.movtery.zalithlauncher.ui.screens.content.versions
2020

2121
import android.content.Context
22+
import android.os.Build
23+
import android.os.Vibrator
2224
import androidx.compose.animation.AnimatedVisibility
2325
import androidx.compose.foundation.layout.fillMaxWidth
2426
import androidx.compose.foundation.layout.offset
@@ -38,12 +40,14 @@ import androidx.compose.ui.platform.LocalContext
3840
import androidx.compose.ui.res.stringResource
3941
import androidx.compose.ui.unit.IntOffset
4042
import androidx.compose.ui.unit.dp
43+
import androidx.core.content.getSystemService
4144
import androidx.lifecycle.compose.collectAsStateWithLifecycle
4245
import com.movtery.zalithlauncher.R
4346
import com.movtery.zalithlauncher.game.control.ControlManager
4447
import com.movtery.zalithlauncher.game.multirt.RuntimesManager
4548
import com.movtery.zalithlauncher.game.plugin.driver.DriverPluginManager
4649
import com.movtery.zalithlauncher.game.renderer.Renderers
50+
import com.movtery.zalithlauncher.game.support.touch_controller.VibrationHandler
4751
import com.movtery.zalithlauncher.game.version.installed.Version
4852
import com.movtery.zalithlauncher.game.version.installed.VersionConfig
4953
import com.movtery.zalithlauncher.setting.AllSettings
@@ -61,6 +65,7 @@ import com.movtery.zalithlauncher.ui.screens.content.elements.MicrophoneCheckSta
6165
import com.movtery.zalithlauncher.ui.screens.content.settings.DriverSummaryLayout
6266
import com.movtery.zalithlauncher.ui.screens.content.settings.RendererSummaryLayout
6367
import com.movtery.zalithlauncher.ui.screens.content.settings.layouts.CardPosition
68+
import com.movtery.zalithlauncher.ui.screens.content.settings.layouts.EnumSettingsCard
6469
import com.movtery.zalithlauncher.ui.screens.content.settings.layouts.IntSliderSettingsCard
6570
import com.movtery.zalithlauncher.ui.screens.content.settings.layouts.ListSettingsCard
6671
import com.movtery.zalithlauncher.ui.screens.content.settings.layouts.SettingsCard
@@ -417,25 +422,76 @@ private fun SupportConfigs(
417422
summary = stringResource(R.string.versions_config_enable_touch_proxy_summary)
418423
)
419424

425+
val vibrator = remember(context) { context.getSystemService<Vibrator>() }
426+
var touchVibrateKind by remember { mutableStateOf(config.touchVibrateKind) }
420427
var touchVibrateDuration by remember { mutableIntStateOf(config.touchVibrateDuration) }
421-
IntSliderSettingsCard(
428+
EnumSettingsCard(
422429
modifier = Modifier.fillMaxWidth(),
423430
position = CardPosition.Middle,
424-
value = touchVibrateDuration,
425-
title = stringResource(R.string.versions_config_vibrate_duration_title),
426-
summary = stringResource(R.string.versions_config_vibrate_duration_summary),
427-
valueRange = 80f..500f,
428-
onValueChange = {
429-
touchVibrateDuration = it
430-
config.touchVibrateDuration = touchVibrateDuration
431+
value = touchVibrateKind,
432+
title = stringResource(R.string.versions_config_vibrate_kind_title),
433+
summary = stringResource(R.string.versions_config_vibrate_kind_summary),
434+
entries = VibrationHandler.VibrateKind.entries,
435+
getRadioEnable = { enum ->
436+
if (enum == VibrationHandler.VibrateKind.ONE_SHOT) {
437+
true
438+
} else {
439+
Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
440+
}
431441
},
432-
onValueChangeFinished = {
433-
config.saveOrShowError(context, submitError)
442+
getRadioText = { enum ->
443+
when (enum) {
444+
VibrationHandler.VibrateKind.ONE_SHOT -> stringResource(R.string.vibrate_kind_one_shot)
445+
VibrationHandler.VibrateKind.CLICK -> stringResource(R.string.vibrate_kind_click)
446+
VibrationHandler.VibrateKind.DOUBLE_CLICK -> stringResource(R.string.vibrate_kind_double_click)
447+
VibrationHandler.VibrateKind.HEAVY_CLICK -> stringResource(R.string.vibrate_kind_heavy_click)
448+
VibrationHandler.VibrateKind.TICK -> stringResource(R.string.vibrate_kind_tick)
449+
}
434450
},
435-
suffix = "ms",
436-
fineTuningControl = true
451+
maxItemsInEachRow = 4,
452+
onRadioClick = { enum ->
453+
touchVibrateKind = enum
454+
vibrator?.let { vibrator ->
455+
VibrationHandler.vibrate(
456+
vibrator = vibrator,
457+
vibrateDuration = touchVibrateDuration,
458+
vibrateKind = touchVibrateKind,
459+
)
460+
}
461+
if (config.touchVibrateKind != enum) {
462+
config.touchVibrateKind = enum
463+
config.saveOrShowError(context, submitError)
464+
}
465+
}
437466
)
438467

468+
AnimatedVisibility(touchVibrateKind == VibrationHandler.VibrateKind.ONE_SHOT) {
469+
IntSliderSettingsCard(
470+
modifier = Modifier.fillMaxWidth(),
471+
position = CardPosition.Middle,
472+
value = touchVibrateDuration,
473+
title = stringResource(R.string.versions_config_vibrate_duration_title),
474+
summary = stringResource(R.string.versions_config_vibrate_duration_summary),
475+
valueRange = 80f..500f,
476+
onValueChange = {
477+
touchVibrateDuration = it
478+
config.touchVibrateDuration = touchVibrateDuration
479+
},
480+
onValueChangeFinished = {
481+
vibrator?.let { vibrator ->
482+
VibrationHandler.vibrate(
483+
vibrator = vibrator,
484+
vibrateDuration = touchVibrateDuration,
485+
vibrateKind = touchVibrateKind,
486+
)
487+
}
488+
config.saveOrShowError(context, submitError)
489+
},
490+
suffix = "ms",
491+
fineTuningControl = true
492+
)
493+
}
494+
439495
//检查麦克风
440496
var microphoneState by remember { mutableStateOf<MicrophoneCheckState>(MicrophoneCheckState.None) }
441497
MicrophoneCheckOperation(

0 commit comments

Comments
 (0)