From bc03c24868a3985d8dc54313f42469cbb4da35ac Mon Sep 17 00:00:00 2001 From: rosuH Date: Sun, 17 May 2026 09:49:37 +0800 Subject: [PATCH] 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***. --- app/src/main/AndroidManifest.xml | 3 ++- .../easywatermark/utils/VibrateHelper.kt | 19 +++++++++++++--- .../easywatermark/utils/VibrateHelperTest.kt | 22 +++++++++++++++++++ 3 files changed, 40 insertions(+), 4 deletions(-) create mode 100644 app/src/test/java/me/rosuh/easywatermark/utils/VibrateHelperTest.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7ac0c4ff..9d40be7e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,7 @@ + @@ -76,4 +77,4 @@ - \ No newline at end of file + diff --git a/app/src/main/java/me/rosuh/easywatermark/utils/VibrateHelper.kt b/app/src/main/java/me/rosuh/easywatermark/utils/VibrateHelper.kt index 8016cbc8..c9516823 100644 --- a/app/src/main/java/me/rosuh/easywatermark/utils/VibrateHelper.kt +++ b/app/src/main/java/me/rosuh/easywatermark/utils/VibrateHelper.kt @@ -1,5 +1,6 @@ package me.rosuh.easywatermark.utils +import android.os.Build import android.view.HapticFeedbackConstants import android.view.View @@ -9,18 +10,30 @@ class VibrateHelper private constructor() { private var cd: Long = 20L fun doVibrate(view: View) { - if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.M) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) { return } - if (System.currentTimeMillis() - latestVibration <= cd) { + val now = System.currentTimeMillis() + if (now - latestVibration <= cd) { return } - view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + latestVibration = now + performHapticFeedbackSafely { + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP) + } } companion object { fun get(): VibrateHelper { return VibrateHelper() } + + internal fun performHapticFeedbackSafely(performFeedback: () -> Boolean): Boolean { + return try { + performFeedback() + } catch (_: SecurityException) { + false + } + } } } diff --git a/app/src/test/java/me/rosuh/easywatermark/utils/VibrateHelperTest.kt b/app/src/test/java/me/rosuh/easywatermark/utils/VibrateHelperTest.kt new file mode 100644 index 00000000..7098eefd --- /dev/null +++ b/app/src/test/java/me/rosuh/easywatermark/utils/VibrateHelperTest.kt @@ -0,0 +1,22 @@ +package me.rosuh.easywatermark.utils + +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class VibrateHelperTest { + @Test + fun performHapticFeedbackSafelyReturnsFalseWhenPermissionIsDenied() { + val result = VibrateHelper.performHapticFeedbackSafely { + throw SecurityException("missing android.permission.VIBRATE") + } + + assertFalse(result) + } + + @Test + fun performHapticFeedbackSafelyReturnsDelegateResult() { + assertTrue(VibrateHelper.performHapticFeedbackSafely { true }) + assertFalse(VibrateHelper.performHapticFeedbackSafely { false }) + } +}