package com.peachpasswords.haptics import android.content.Context import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager import android.view.HapticFeedbackConstants import android.view.View object PeachHaptics { fun onUnlock(view: View) = view.performHapticFeedback(sinceR(HapticFeedbackConstants.CONFIRM, HapticFeedbackConstants.VIRTUAL_KEY)) fun onEntryTap(view: View) = view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) fun onCopy(view: View) = view.performHapticFeedback(HapticFeedbackConstants.CLOCK_TICK) fun onSheetOpen(view: View) = view.performHapticFeedback(HapticFeedbackConstants.CONTEXT_CLICK) fun onSheetClose(view: View) = view.performHapticFeedback(sinceR(HapticFeedbackConstants.GESTURE_END, HapticFeedbackConstants.CONTEXT_CLICK)) fun onError(view: View) = view.performHapticFeedback(sinceR(HapticFeedbackConstants.REJECT, HapticFeedbackConstants.LONG_PRESS)) fun onButton(view: View) = view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) fun onCardFocus(view: View) = view.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY) /** * CONFIRM / REJECT / GESTURE_END only exist from API 30. The constants are * `int`s inlined at compile time, so on API 26-29 they don't fail loudly — * `performHapticFeedback` just returns false and the device stays silent. * Each falls back to the nearest pre-30 effect with the same intent: a short * tick for confirm, a heavier buzz for reject, a soft click to close. */ private fun sinceR(api30: Int, fallback: Int): Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) api30 else fallback fun onLongPress(context: Context) = vibrate(context, 20L, 180) fun onSwipeCopy(context: Context) = vibrate(context, 15L, 150) fun onPairingComplete(context: Context) { val vibrator = getVibrator(context) ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrator.vibrate( VibrationEffect.createWaveform( longArrayOf(0, 30, 60, 50), intArrayOf(0, 180, 0, 120), -1, ) ) } } private fun vibrate(context: Context, duration: Long, amplitude: Int) { val vibrator = getVibrator(context) ?: return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude)) } else { @Suppress("DEPRECATION") vibrator.vibrate(duration) } } private fun getVibrator(context: Context): Vibrator? { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val manager = context.getSystemService(VibratorManager::class.java) manager?.defaultVibrator } else { @Suppress("DEPRECATION") context.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator } } }