From cb5870f30d536dde05b9959791c89eccb90557fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 15 Jan 2021 18:43:09 +0100 Subject: [PATCH] Add Touch Button Haptics to Android --- .../com/metallic/chiaki/common/Preferences.kt | 5 ++++ .../chiaki/settings/SettingsFragment.kt | 2 ++ .../chiaki/touchcontrols/ButtonHaptics.kt | 29 +++++++++++++++++++ .../chiaki/touchcontrols/ButtonView.kt | 4 +++ .../metallic/chiaki/touchcontrols/DPadView.kt | 4 +++ .../chiaki/touchcontrols/TouchpadView.kt | 5 ++++ .../main/res/drawable/ic_button_haptic.xml | 9 ++++++ android/app/src/main/res/values/strings.xml | 3 ++ android/app/src/main/res/xml/preferences.xml | 6 ++++ 9 files changed, 67 insertions(+) create mode 100644 android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonHaptics.kt create mode 100644 android/app/src/main/res/drawable/ic_button_haptic.xml diff --git a/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt b/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt index 2456fcf..3c4f2f9 100644 --- a/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt +++ b/android/app/src/main/java/com/metallic/chiaki/common/Preferences.kt @@ -83,6 +83,11 @@ class Preferences(context: Context) get() = sharedPreferences.getBoolean(motionEnabledKey, true) set(value) { sharedPreferences.edit().putBoolean(motionEnabledKey, value).apply() } + val buttonHapticEnabledKey get() = resources.getString(R.string.preferences_button_haptic_enabled_key) + var buttonHapticEnabled + get() = sharedPreferences.getBoolean(buttonHapticEnabledKey, true) + set(value) { sharedPreferences.edit().putBoolean(buttonHapticEnabledKey, value).apply() } + val logVerboseKey get() = resources.getString(R.string.preferences_log_verbose_key) var logVerbose get() = sharedPreferences.getBoolean(logVerboseKey, false) diff --git a/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt b/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt index 222d130..f574d09 100644 --- a/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt +++ b/android/app/src/main/java/com/metallic/chiaki/settings/SettingsFragment.kt @@ -27,6 +27,7 @@ class DataStore(val preferences: Preferences): PreferenceDataStore() preferences.swapCrossMoonKey -> preferences.swapCrossMoon preferences.rumbleEnabledKey -> preferences.rumbleEnabled preferences.motionEnabledKey -> preferences.motionEnabled + preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled else -> defValue } @@ -38,6 +39,7 @@ class DataStore(val preferences: Preferences): PreferenceDataStore() preferences.swapCrossMoonKey -> preferences.swapCrossMoon = value preferences.rumbleEnabledKey -> preferences.rumbleEnabled = value preferences.motionEnabledKey -> preferences.motionEnabled = value + preferences.buttonHapticEnabledKey -> preferences.buttonHapticEnabled = value } } diff --git a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonHaptics.kt b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonHaptics.kt new file mode 100644 index 0000000..909eab4 --- /dev/null +++ b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonHaptics.kt @@ -0,0 +1,29 @@ +package com.metallic.chiaki.touchcontrols + +import android.content.Context +import android.os.Build +import android.os.VibrationEffect +import android.os.Vibrator +import androidx.appcompat.app.AppCompatActivity +import com.metallic.chiaki.common.Preferences + +class ButtonHaptics(val context: Context) +{ + private val enabled = Preferences(context).buttonHapticEnabled + + fun trigger(harder: Boolean = false) + { + if(!enabled) + return + val vibrator = context.getSystemService(AppCompatActivity.VIBRATOR_SERVICE) as Vibrator + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + vibrator.vibrate( + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) + VibrationEffect.createPredefined(if(harder) VibrationEffect.EFFECT_CLICK else VibrationEffect.EFFECT_TICK) + else + VibrationEffect.createOneShot(10, if(harder) 200 else 100) + ) + else + vibrator.vibrate(10) + } +} \ No newline at end of file diff --git a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonView.kt b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonView.kt index e82ffac..078f751 100644 --- a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonView.kt +++ b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/ButtonView.kt @@ -16,6 +16,8 @@ class ButtonView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { + private val haptics = ButtonHaptics(context) + var buttonPressed = false private set(value) { @@ -23,6 +25,8 @@ class ButtonView @JvmOverloads constructor( field = value if(diff) { + if(value) + haptics.trigger() invalidate() buttonPressedCallback?.let { it(field) } } diff --git a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/DPadView.kt b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/DPadView.kt index 0006546..9c359d8 100644 --- a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/DPadView.kt +++ b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/DPadView.kt @@ -16,6 +16,8 @@ class DPadView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { + private val haptics = ButtonHaptics(context) + enum class Direction { LEFT, RIGHT, @@ -113,6 +115,8 @@ class DPadView @JvmOverloads constructor( if(state != newState) { + if(newState != null) + haptics.trigger() state = newState invalidate() stateChangeCallback?.let { it(state) } diff --git a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/TouchpadView.kt b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/TouchpadView.kt index 9dba03d..a7e7b85 100644 --- a/android/app/src/main/java/com/metallic/chiaki/touchcontrols/TouchpadView.kt +++ b/android/app/src/main/java/com/metallic/chiaki/touchcontrols/TouchpadView.kt @@ -26,6 +26,8 @@ class TouchpadView @JvmOverloads constructor( private const val BUTTON_HOLD_DELAY_MS = 500L } + private val haptics = ButtonHaptics(context) + private val drawableIdle: Drawable? private val drawablePressed: Drawable? @@ -49,8 +51,10 @@ class TouchpadView @JvmOverloads constructor( val startButtonHoldRunnable = Runnable { if(!moveInsignificant || buttonHeld) return@Runnable + haptics.trigger(true) state.buttons = state.buttons or ControllerState.BUTTON_TOUCHPAD buttonHeld = true + triggerStateChanged() } } private val pointerTouches = mutableMapOf() @@ -105,6 +109,7 @@ class TouchpadView @JvmOverloads constructor( { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { state.startTouch(touchX(event, event.actionIndex), touchY(event, event.actionIndex))?.let { + haptics.trigger() val touch = Touch(it, event.getX(event.actionIndex), event.getY(event.actionIndex)) pointerTouches[event.getPointerId(event.actionIndex)] = touch if(!buttonHeld) diff --git a/android/app/src/main/res/drawable/ic_button_haptic.xml b/android/app/src/main/res/drawable/ic_button_haptic.xml new file mode 100644 index 0000000..fea4c99 --- /dev/null +++ b/android/app/src/main/res/drawable/ic_button_haptic.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 84a1a59..254042b 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -92,6 +92,8 @@ Use phone vibration motor for rumble Motion Use device\'s motion sensors for controller motion + Touch Haptics + Use phone vibration motor for short haptic feedback on button touches Are you sure you want to delete the registered console %s with ID %s? Are you sure you want to delete the console entry for %s? Keep @@ -108,6 +110,7 @@ touchpad_only_enabled rumble_enabled motion_enabled + button_haptic_enabled log_verbose import_settings export_settings diff --git a/android/app/src/main/res/xml/preferences.xml b/android/app/src/main/res/xml/preferences.xml index 0ec2c0d..49989db 100644 --- a/android/app/src/main/res/xml/preferences.xml +++ b/android/app/src/main/res/xml/preferences.xml @@ -31,6 +31,12 @@ app:summary="@string/preferences_motion_enabled_summary" app:icon="@drawable/ic_motion" /> + +