Skip to content

Commit f1452c9

Browse files
ENOA-ANSUL-EDENWhiredPlanck
authored andcommitted
feat: enhance keyboard haptic feedback with additional vibration options
1 parent 6c3fbe0 commit f1452c9

File tree

9 files changed

+64
-38
lines changed

9 files changed

+64
-38
lines changed

app/src/main/java/com/osfans/trime/data/prefs/AppPrefs.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ class AppPrefs(
141141
const val SOUND_EFFECT_ENABLED = "custom_sound_effect_enabled"
142142
const val CUSTOM_SOUND_EFFECT = "custom_sound_effect_name"
143143

144-
const val VIBRATE_ON_KEYPRESS = "vibrate_on_keypress"
144+
const val VIBRATE_ON_KEY_PRESS = "vibrate_on_key_press"
145+
const val VIBRATE_ON_KEY_RELEASE = "vibrate_on_key_release"
146+
const val VIBRATE_ON_KEY_REPEAT = "vibrate_on_key_repeat"
145147
const val VIBRATION_DURATION = "vibration_duration"
146148
const val VIBRATION_AMPLITUDE = "vibration_amplitude"
147149

@@ -184,7 +186,9 @@ class AppPrefs(
184186
val soundEffectEnabled = bool(SOUND_EFFECT_ENABLED, false)
185187
val customSoundEffect = string(CUSTOM_SOUND_EFFECT, "")
186188
val soundVolume = int(KEY_SOUND_VALUE, 100)
187-
val vibrateOnKeyPress = bool(VIBRATE_ON_KEYPRESS, false)
189+
val vibrateOnKeyPress = bool(VIBRATE_ON_KEY_PRESS, false)
190+
val vibrateOnKeyRelease = bool(VIBRATE_ON_KEY_RELEASE, false)
191+
val vibrateOnKeyRepeat = bool(VIBRATE_ON_KEY_REPEAT, false)
188192
val vibrationDuration = int(VIBRATION_DURATION, 10)
189193
val vibrationAmplitude = int(VIBRATION_AMPLITUDE, -1)
190194
val speakOnKeyPress = bool(SPEAK_ON_KEYPRESS, false)

app/src/main/java/com/osfans/trime/ime/keyboard/CommonKeyboardActionListener.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ class CommonKeyboardActionListener(
128128

129129
val listener by lazy {
130130
object : KeyboardActionListener {
131-
override fun onPress(keyEventCode: Int) {
131+
override fun onPress(keyEventCode: Int, isSound: Boolean) {
132132
InputFeedbackManager.run {
133133
keyPressVibrate(service.window.window!!.decorView)
134-
keyPressSound(keyEventCode)
134+
if (isSound) keyPressSound(keyEventCode)
135135
keyPressSpeak(keyEventCode)
136136
}
137137
}

app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardActionListener.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ interface KeyboardActionListener {
1313
* @param keyEventCode the unicode of the key being pressed. If the touch is not on a valid key,
1414
* the value will be zero.
1515
*/
16-
fun onPress(keyEventCode: Int)
16+
fun onPress(keyEventCode: Int, isSound: Boolean = true)
1717

1818
/**
1919
* Called when the user releases a key. This is sent after the [.onKey] is called. For

app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardGestureFrame.kt

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,9 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
5050
private val activePointers = SparseArray<PointerState>()
5151
private val lastTapTimes = SparseArray<Long>()
5252

53-
var onKeyActionListener: ((keyIndex: Int, behavior: KeyBehavior, repeat: Boolean) -> Boolean)? = null
53+
var onKeyActionListener: ((keyIndex: Int, behavior: KeyBehavior) -> Boolean)? = null
5454
var onKeySlideListener: ((keyIndex: Int, deltaX: Int, x: Float, y: Float) -> Boolean)? = null
55-
var onKeyPreviewListener: ((keyIndex: Int, behavior: KeyBehavior, showing: Boolean) -> Unit)? = null
55+
var onKeyStateListener: ((keyIndex: Int, behavior: KeyBehavior, isVisible: Boolean, isPressed: Boolean, isRepeating: Boolean) -> Unit)? = null
5656
var onKeyReleaseListener: (() -> Unit)? = null
5757

5858
open fun getKeyIndex(x: Float, y: Float): Int = -1
@@ -89,7 +89,7 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
8989
)
9090
activePointers.put(pointerId, state)
9191

92-
showPreview(keyIndex)
92+
onKeyStateListener?.invoke(keyIndex, KeyBehavior.CLICK, true, true, false)
9393
val hasLongClick = hasAction(keyIndex, KeyBehavior.LONG_CLICK)
9494
if (hasLongClick || isKeyRepeatable(keyIndex)) launchLongPressJob(pointerId, state)
9595
}
@@ -169,20 +169,20 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
169169
val hasDouble = hasAction(keyIndex, KeyBehavior.DOUBLE_CLICK)
170170

171171
if (state.shouldPerformSwipe && !state.slideActivated) {
172-
onKeyActionListener?.invoke(keyIndex, state.lastSwipeBehavior!!, false)
172+
onKeyActionListener?.invoke(keyIndex, state.lastSwipeBehavior!!)
173173
hidePreview(keyIndex)
174174
return true
175175
}
176176

177177
if (activePointers.isNotEmpty()) {
178-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.COMBO, false)
178+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.COMBO)
179179
hidePreview(keyIndex)
180180
return true
181181
}
182182

183183
if (!hasLazy && !hasDouble) {
184184
if (!state.slideActivated) {
185-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK, false)
185+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK)
186186
}
187187
hidePreview(keyIndex)
188188
return true
@@ -194,24 +194,24 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
194194
if (hasLazy && !hasDouble) {
195195
if (timeDelta <= doubleTapTimeout) {
196196
lastTapTimes.delete(keyIndex)
197-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.LAZY_DOUBLE_CLICK, false)
197+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.LAZY_DOUBLE_CLICK)
198198
} else {
199199
val scheduledAt = now
200200
lastTapTimes.put(keyIndex, scheduledAt)
201201
lifecycleScope.launch {
202202
delay(doubleTapTimeout.toLong())
203203
if (lastTapTimes.get(keyIndex, -1L) == scheduledAt) {
204204
lastTapTimes.delete(keyIndex)
205-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK, false)
205+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK)
206206
}
207207
}
208208
}
209209
} else if (timeDelta <= doubleTapTimeout) {
210210
lastTapTimes.delete(keyIndex)
211-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.DOUBLE_CLICK, false)
211+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.DOUBLE_CLICK)
212212
} else if (!state.slideActivated) {
213213
lastTapTimes.put(keyIndex, now)
214-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK, false)
214+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK)
215215
}
216216

217217
hidePreview(keyIndex)
@@ -240,10 +240,10 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
240240
if (activePointers.get(pointerId) != state || state.shouldPerformSwipe) return@launch
241241
state.isLongPressed = true
242242
if (isKeyRepeatable(keyIndex)) {
243-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK, false)
243+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK)
244244
launchRepeatClickJob(pointerId, state)
245245
} else {
246-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.LONG_CLICK, false)
246+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.LONG_CLICK)
247247
hidePreview(keyIndex)
248248
}
249249
}
@@ -255,7 +255,8 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
255255
try {
256256
delay(repeatInterval.toLong())
257257
while (activePointers.get(pointerId) == state) {
258-
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK, true)
258+
onKeyActionListener?.invoke(keyIndex, KeyBehavior.CLICK)
259+
onKeyStateListener?.invoke(keyIndex, KeyBehavior.CLICK, false, false, true)
259260
delay(repeatInterval.toLong())
260261
}
261262
} finally {
@@ -291,11 +292,11 @@ open class KeyboardGestureFrame(context: Context) : FrameLayout(context) {
291292
fun getNStep(start: Float, end: Float, step: Float): Int = (if (start < end) 1 else -1) * floor(abs(end - start) / step).toInt()
292293

293294
private fun showPreview(keyIndex: Int, behavior: KeyBehavior = KeyBehavior.CLICK) {
294-
onKeyPreviewListener?.invoke(keyIndex, behavior, true)
295+
onKeyStateListener?.invoke(keyIndex, behavior, true, false, false)
295296
}
296297

297298
private fun hidePreview(keyIndex: Int) {
298-
onKeyPreviewListener?.invoke(keyIndex, KeyBehavior.CLICK, false)
299+
onKeyStateListener?.invoke(keyIndex, KeyBehavior.CLICK, false, false, false)
299300
}
300301

301302
private val touchPaint by lazy {

app/src/main/java/com/osfans/trime/ime/keyboard/KeyboardView.kt

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class KeyboardView(
7070
* the depressed key. By default the preview is enabled.
7171
*/
7272
private val showPreview by AppPrefs.defaultInstance().keyboard.popupKeyPressEnabled
73+
private val vibrateOnKeyRelease by AppPrefs.defaultInstance().keyboard.vibrateOnKeyRelease
74+
private val vibrateOnKeyRepeat by AppPrefs.defaultInstance().keyboard.vibrateOnKeyRepeat
7375

7476
private val deletedTextBuffer = ArrayDeque<String>()
7577

@@ -110,8 +112,7 @@ class KeyboardView(
110112
computeProximityThreshold(keyboard)
111113
invalidateAllKeys()
112114

113-
onKeyActionListener = { keyIndex, behavior, repeat ->
114-
if (!repeat) keyboardActionListener?.onPress(keyIndex)
115+
onKeyActionListener = { keyIndex, behavior ->
115116
detectAndSendKey(keyIndex, behavior)
116117
true
117118
}
@@ -150,16 +151,20 @@ class KeyboardView(
150151
false
151152
}
152153

153-
onKeyPreviewListener = { keyIndex, behavior, showing ->
154+
onKeyStateListener = { keyIndex, behavior, isVisible, isPressed, isRepeating ->
154155
val key = mKeys[keyIndex]
155-
if (showing) {
156-
key.onPressed()
157-
invalidateKey(key)
158-
if (showPreview) showKeyPreview(key, behavior)
159-
} else {
160-
key.onReleased()
161-
invalidateKey(key)
162-
if (showPreview) dismissKeyPreview(key)
156+
if (isPressed || (isRepeating && vibrateOnKeyRepeat)) keyboardActionListener?.onPress(keyIndex, !isRepeating)
157+
if (!isRepeating) {
158+
if (isVisible) {
159+
key.onPressed()
160+
invalidateKey(key)
161+
if (showPreview) showKeyPreview(key, behavior)
162+
} else {
163+
key.onReleased()
164+
invalidateKey(key)
165+
if (showPreview) dismissKeyPreview(key)
166+
if (vibrateOnKeyRelease) keyboardActionListener?.onPress(keyIndex, false)
167+
}
163168
}
164169
}
165170

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
3434
<string name="normal_mode_color_summary">选取配色</string>
3535
<string name="sound_on_keypress">按键声音</string>
3636
<string name="sound_volume">按键音量</string>
37-
<string name="vibrate_on_keypress">按键时振动</string>
37+
<string name="vibrate_on_key_press">按键时振动</string>
38+
<string name="vibrate_on_key_release">按键松开时振动</string>
39+
<string name="vibrate_on_key_repeat">重复按键时振动</string>
3840
<string name="vibration_duration">按键振动时长</string>
3941
<string name="vibration_amplitude">按键振动强度</string>
4042
<string name="speak_on_keypress">按键时朗读字符</string>

app/src/main/res/values-zh-rTW/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
3737
<string name="profile_reset_summary">使用預設的內建設置覆蓋共享資料夾下相同檔案</string>
3838
<string name="sound_on_keypress">按鍵聲音</string>
3939
<string name="sound_volume">按鍵音量</string>
40-
<string name="vibrate_on_keypress">按鍵時振動</string>
40+
<string name="vibrate_on_key_press">按鍵時振動</string>
41+
<string name="vibrate_on_key_release">按鍵鬆開時振動</string>
42+
<string name="vibrate_on_key_repeat">重複按鍵時振動</string>
4143
<string name="vibration_duration">按鍵振動時長</string>
4244
<string name="vibration_amplitude">按鍵振動強度</string>
4345
<string name="speak_on_keypress">按鍵時朗讀字符</string>

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ SPDX-License-Identifier: GPL-3.0-or-later
3737
<string name="profile_reset_summary">Overwrite the same files in shared directory with default built-in assets</string>
3838
<string name="sound_on_keypress">Sound on key press</string>
3939
<string name="sound_volume">Sound volume on key press</string>
40-
<string name="vibrate_on_keypress">Vibrate on key press</string>
40+
<string name="vibrate_on_key_press">Vibrate on key press</string>
41+
<string name="vibrate_on_key_release">Vibrate on key release</string>
42+
<string name="vibrate_on_key_repeat">Vibrate on key repeat</string>
4143
<string name="vibration_duration">Vibration duration on key press</string>
4244
<string name="vibration_amplitude">Vibration amplitude on key press</string>
4345
<string name="speak_on_keypress">Voice on key press</string>

app/src/main/res/xml/keyboard_preference.xml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,22 @@ SPDX-License-Identifier: GPL-3.0-or-later
155155
app:unit="%"
156156
app:useSimpleSummaryProvider="true" />
157157
<SwitchPreferenceCompat
158-
android:key="vibrate_on_keypress"
159-
android:title="@string/vibrate_on_keypress"
158+
android:key="vibrate_on_key_press"
159+
android:title="@string/vibrate_on_key_press"
160+
app:iconSpaceReserved="false" />
161+
<SwitchPreferenceCompat
162+
android:key="vibrate_on_key_release"
163+
android:title="@string/vibrate_on_key_release"
164+
android:dependency="vibrate_on_key_press"
165+
app:iconSpaceReserved="false" />
166+
<SwitchPreferenceCompat
167+
android:key="vibrate_on_key_repeat"
168+
android:title="@string/vibrate_on_key_repeat"
169+
android:dependency="vibrate_on_key_press"
160170
app:iconSpaceReserved="false" />
161171
<com.osfans.trime.ui.components.DialogSeekBarPreference
162172
android:defaultValue="0"
163-
android:dependency="vibrate_on_keypress"
173+
android:dependency="vibrate_on_key_press"
164174
android:key="vibration_duration"
165175
android:title="@string/vibration_duration"
166176
app:allowDividerAbove="false"
@@ -172,7 +182,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
172182
app:useSimpleSummaryProvider="true" />
173183
<com.osfans.trime.ui.components.DialogSeekBarPreference
174184
android:defaultValue="0"
175-
android:dependency="vibrate_on_keypress"
185+
android:dependency="vibrate_on_key_press"
176186
android:key="vibration_amplitude"
177187
android:title="@string/vibration_amplitude"
178188
app:allowDividerAbove="false"

0 commit comments

Comments
 (0)