Skip to content

Commit bc03c24

Browse files
committed
Fix Xiaomi haptic feedback crash
Add the VIBRATE permission and guard View.performHapticFeedback so Xiaomi/Android 16 devices do not crash when haptic feedback reaches the vibrator service without permission. Reported by A**** K***.
1 parent f7efacc commit bc03c24

3 files changed

Lines changed: 40 additions & 4 deletions

File tree

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<uses-permission
99
android:name="android.permission.READ_EXTERNAL_STORAGE"
1010
android:maxSdkVersion="32" />
11+
<uses-permission android:name="android.permission.VIBRATE" />
1112

1213
<!-- Devices running Android 12L (API level 32) or lower -->
1314
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
@@ -76,4 +77,4 @@
7677
</provider>
7778
</application>
7879

79-
</manifest>
80+
</manifest>
Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package me.rosuh.easywatermark.utils
22

3+
import android.os.Build
34
import android.view.HapticFeedbackConstants
45
import android.view.View
56

@@ -9,18 +10,30 @@ class VibrateHelper private constructor() {
910
private var cd: Long = 20L
1011

1112
fun doVibrate(view: View) {
12-
if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) {
13+
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
1314
return
1415
}
15-
if (System.currentTimeMillis() - latestVibration <= cd) {
16+
val now = System.currentTimeMillis()
17+
if (now - latestVibration <= cd) {
1618
return
1719
}
18-
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
20+
latestVibration = now
21+
performHapticFeedbackSafely {
22+
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
23+
}
1924
}
2025

2126
companion object {
2227
fun get(): VibrateHelper {
2328
return VibrateHelper()
2429
}
30+
31+
internal fun performHapticFeedbackSafely(performFeedback: () -> Boolean): Boolean {
32+
return try {
33+
performFeedback()
34+
} catch (_: SecurityException) {
35+
false
36+
}
37+
}
2538
}
2639
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package me.rosuh.easywatermark.utils
2+
3+
import org.junit.Assert.assertFalse
4+
import org.junit.Assert.assertTrue
5+
import org.junit.Test
6+
7+
class VibrateHelperTest {
8+
@Test
9+
fun performHapticFeedbackSafelyReturnsFalseWhenPermissionIsDenied() {
10+
val result = VibrateHelper.performHapticFeedbackSafely {
11+
throw SecurityException("missing android.permission.VIBRATE")
12+
}
13+
14+
assertFalse(result)
15+
}
16+
17+
@Test
18+
fun performHapticFeedbackSafelyReturnsDelegateResult() {
19+
assertTrue(VibrateHelper.performHapticFeedbackSafely { true })
20+
assertFalse(VibrateHelper.performHapticFeedbackSafely { false })
21+
}
22+
}

0 commit comments

Comments
 (0)