// Copyright 2019-2023 Tauri Programme within The Commons Conservancy // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT package app.tauri.haptics import android.app.Activity import android.content.Context import android.os.Build import android.os.VibrationEffect import android.os.Vibrator import android.os.VibratorManager import app.tauri.Logger import app.tauri.annotation.Command import app.tauri.annotation.InvokeArg import app.tauri.annotation.TauriPlugin import app.tauri.haptics.patterns.ImpactPatternHeavy import app.tauri.haptics.patterns.ImpactPatternLight import app.tauri.haptics.patterns.ImpactPatternMedium import app.tauri.haptics.patterns.ImpactPatternRigid import app.tauri.haptics.patterns.ImpactPatternSoft import app.tauri.haptics.patterns.NotificationPatternError import app.tauri.haptics.patterns.NotificationPatternSuccess import app.tauri.haptics.patterns.NotificationPatternWarning import app.tauri.haptics.patterns.Pattern import app.tauri.haptics.patterns.SelectionPattern import app.tauri.plugin.Invoke import app.tauri.plugin.Plugin import com.fasterxml.jackson.annotation.JsonProperty @InvokeArg class HapticsOptions { var duration: Long = 300 } @InvokeArg class NotificationFeedbackArgs { val type: NotificationFeedbackType = NotificationFeedbackType.Success } @InvokeArg enum class NotificationFeedbackType { @JsonProperty("success") Success, @JsonProperty("warning") Warning, @JsonProperty("error") Error; fun into(): Pattern { return when(this) { Success -> NotificationPatternSuccess Warning -> NotificationPatternWarning Error -> NotificationPatternError } } } @InvokeArg class ImpactFeedbackArgs { val style: ImpactFeedbackStyle = ImpactFeedbackStyle.Medium } @InvokeArg enum class ImpactFeedbackStyle { @JsonProperty("light") Light, @JsonProperty("medium") Medium, @JsonProperty("heavy") Heavy, @JsonProperty("soft") Soft, @JsonProperty("rigid") Rigid; fun into(): Pattern { return when(this) { Light -> ImpactPatternLight Medium -> ImpactPatternMedium Heavy -> ImpactPatternHeavy Soft -> ImpactPatternSoft Rigid -> ImpactPatternRigid } } } @TauriPlugin class HapticsPlugin(private val activity: Activity): Plugin(activity) { private val vibrator: Vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val vibManager = activity.applicationContext.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager vibManager.defaultVibrator } else { @Suppress("DEPRECATION") activity.applicationContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator } // // TAURI COMMANDS // @Command fun vibrate(invoke: Invoke) { val args = invoke.parseArgs(HapticsOptions::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrator.vibrate(VibrationEffect.createOneShot(args.duration, VibrationEffect.DEFAULT_AMPLITUDE)) } else { vibrator.vibrate(args.duration) } invoke.resolve() } @Command fun impactFeedback(invoke: Invoke) { val args = invoke.parseArgs(ImpactFeedbackArgs::class.java) vibratePattern(args.style.into()) invoke.resolve() } @Command fun notificationFeedback(invoke: Invoke) { val args = invoke.parseArgs(NotificationFeedbackArgs::class.java) vibratePattern(args.type.into()) invoke.resolve() } // TODO: Consider breaking this up into Start,Change,End like capacitor @Command fun selectionFeedback(invoke: Invoke) { vibratePattern(SelectionPattern) invoke.resolve() } // INTERNAL FUNCTIONS private fun vibratePattern(pattern: Pattern) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { vibrator.vibrate(VibrationEffect.createWaveform(pattern.timings, pattern.amplitudes, -1)) } else { vibrator.vibrate(pattern.oldSDKPattern, -1) } } }