Skip to content

Commit

Permalink
Android: Implement haptic feedback support
Browse files Browse the repository at this point in the history
  • Loading branch information
gperrio authored and Gamer64ytb committed Jul 25, 2024
1 parent ecbb87b commit e54c117
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
popupMenu.menu.apply {
findItem(R.id.menu_show_overlay).isChecked = EmulationMenuSettings.showOverlay
findItem(R.id.menu_show_fps).isChecked = EmulationMenuSettings.showFps
findItem(R.id.menu_haptic_feedback).isChecked = EmulationMenuSettings.hapticFeedback
findItem(R.id.menu_emulation_joystick_rel_center).isChecked =
EmulationMenuSettings.joystickRelCenter
findItem(R.id.menu_emulation_dpad_slide_enable).isChecked =
Expand All @@ -623,6 +624,13 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback, Choreographer.Fram
true
}

R.id.menu_haptic_feedback -> {
EmulationMenuSettings.hapticFeedback = !EmulationMenuSettings.hapticFeedback
// wtf
updateShowFpsOverlay()
true
}

R.id.menu_emulation_edit_layout -> {
editControlsPlacement()
binding.drawerLayout.close()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,20 +76,25 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
overlayJoysticks.forEach { it.draw(canvas) }
}

fun hapticFeedback(type:Int) {
if (EmulationMenuSettings.hapticFeedback)
performHapticFeedback(type)
}

override fun onTouch(v: View, event: MotionEvent): Boolean {
if (isInEditMode) {
return onTouchWhileEditing(event)
}
var shouldUpdateView = false
for (button in overlayButtons) {
if (!button.updateStatus(event)) {
if (!button.updateStatus(event, this)) {
continue
}
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, button.id, button.status)
shouldUpdateView = true
}
for (dpad in overlayDpads) {
if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide)) {
if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlide, this)) {
continue
}
NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.upId, dpad.upStatus)
Expand All @@ -111,7 +116,7 @@ class InputOverlay(context: Context?, attrs: AttributeSet?) : SurfaceView(contex
shouldUpdateView = true
}
for (joystick in overlayJoysticks) {
if (!joystick.updateStatus(event)) {
if (!joystick.updateStatus(event, this)) {
continue
}
val axisID = joystick.joystickId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import org.citra.citra_emu.NativeLibrary

Expand Down Expand Up @@ -53,7 +54,7 @@ class InputOverlayDrawableButton(
*
* @return true if value was changed
*/
fun updateStatus(event: MotionEvent): Boolean {
fun updateStatus(event: MotionEvent, overlay: InputOverlay): Boolean {
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
Expand All @@ -69,6 +70,7 @@ class InputOverlayDrawableButton(
}
pressedState = true
trackId = pointerId
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
return true
}
if (isActionUp) {
Expand All @@ -77,6 +79,7 @@ class InputOverlayDrawableButton(
}
pressedState = false
trackId = -1
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
return true
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import org.citra.citra_emu.NativeLibrary

Expand Down Expand Up @@ -61,7 +62,8 @@ class InputOverlayDrawableDpad(
trackId = -1
}

fun updateStatus(event: MotionEvent, dpadSlide: Boolean): Boolean {
fun updateStatus(event: MotionEvent, dpadSlide: Boolean, overlay: InputOverlay): Boolean {
var isDown = false
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
Expand All @@ -75,6 +77,7 @@ class InputOverlayDrawableDpad(
if (!bounds.contains(xPosition, yPosition)) {
return false
}
isDown = true
trackId = pointerId
}
if (isActionUp) {
Expand All @@ -86,6 +89,7 @@ class InputOverlayDrawableDpad(
downButtonState = false
leftButtonState = false
rightButtonState = false
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
return true
}
if (trackId == -1) {
Expand Down Expand Up @@ -116,7 +120,15 @@ class InputOverlayDrawableDpad(
downButtonState = yAxis > VIRT_AXIS_DEADZONE
leftButtonState = xAxis < -VIRT_AXIS_DEADZONE
rightButtonState = xAxis > VIRT_AXIS_DEADZONE
return upState != upButtonState || downState != downButtonState || leftState != leftButtonState || rightState != rightButtonState

val stateChanged = upState != upButtonState || downState != downButtonState || leftState != leftButtonState || rightState != rightButtonState

if (stateChanged)
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
else if (isDown)
overlay.hapticFeedback(HapticFeedbackConstants.CLOCK_TICK)

return stateChanged
}
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import org.citra.citra_emu.NativeLibrary
import org.citra.citra_emu.utils.EmulationMenuSettings
Expand Down Expand Up @@ -43,6 +44,8 @@ class InputOverlayDrawableJoystick(
var trackId = -1
var xAxis = 0f
var yAxis = 0f
var angle = 0f
var radius = 0f
private var controlPositionX = 0
private var controlPositionY = 0
private var previousTouchX = 0
Expand Down Expand Up @@ -90,7 +93,7 @@ class InputOverlayDrawableJoystick(
boundsBoxBitmap.draw(canvas)
}

fun updateStatus(event: MotionEvent): Boolean {
fun updateStatus(event: MotionEvent, overlay: InputOverlay): Boolean {
val pointerIndex = event.actionIndex
val xPosition = event.getX(pointerIndex).toInt()
val yPosition = event.getY(pointerIndex).toInt()
Expand All @@ -115,6 +118,7 @@ class InputOverlayDrawableJoystick(
}
boundsBoxBitmap.bounds = virtBounds
trackId = pointerId
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY)
}
if (isActionUp) {
if (trackId != pointerId) {
Expand All @@ -123,12 +127,15 @@ class InputOverlayDrawableJoystick(
pressedState = false
xAxis = 0.0f
yAxis = 0.0f
angle = 0.0f
radius = 0.0f
outerBitmap.alpha = opacity
boundsBoxBitmap.alpha = 0
virtBounds = Rect(origBounds.left, origBounds.top, origBounds.right, origBounds.bottom)
bounds = Rect(origBounds.left, origBounds.top, origBounds.right, origBounds.bottom)
setInnerBounds()
trackId = -1
overlay.hapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY_RELEASE)
return true
}
if (trackId == -1) return false
Expand All @@ -148,6 +155,8 @@ class InputOverlayDrawableJoystick(
val yAxis = touchY / maxY
val oldXAxis = this.xAxis
val oldYAxis = this.yAxis
val oldAngle = this.angle
val oldRadius = this.radius

// Clamp the circle pad input to a circle
val angle = atan2(yAxis.toDouble(), xAxis.toDouble()).toFloat()
Expand All @@ -158,6 +167,15 @@ class InputOverlayDrawableJoystick(
this.xAxis = cos(angle.toDouble()).toFloat() * radius
this.yAxis = sin(angle.toDouble()).toFloat() * radius
setInnerBounds()

if (kotlin.math.abs(oldRadius - radius) > .34f
|| radius > .5f && kotlin.math.abs(oldAngle - angle) > kotlin.math.PI / 8) {
this.radius = radius
this.angle = angle

overlay.hapticFeedback(HapticFeedbackConstants.CLOCK_TICK)
}

return oldXAxis != this.xAxis && oldYAxis != this.yAxis
}
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@ object EmulationMenuSettings {
get() = preferences.getBoolean("EmulationMenuSettings_ShowFps", false)
set(value) {
preferences.edit()
.putBoolean("EmulationMenuSettings_ShowFps", value)
.apply()
.putBoolean("EmulationMenuSettings_ShowFps", value)
.apply()
}
var hapticFeedback: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_HapticFeedback", true)
set(value) {
preferences.edit()
.putBoolean("EmulationMenuSettings_HapticFeedback", value)
.apply()
}
var swapScreens: Boolean
get() = preferences.getBoolean("EmulationMenuSettings_SwapScreens", false)
Expand Down
5 changes: 5 additions & 0 deletions src/android/app/src/main/res/menu/menu_overlay_options.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@
android:title="@string/emulation_show_fps"
android:checkable="true" />

<item
android:id="@+id/menu_haptic_feedback"
android:title="@string/emulation_haptic_feedback"
android:checkable="true" />

<item
android:id="@+id/menu_emulation_edit_layout"
android:title="@string/emulation_edit_layout" />
Expand Down
1 change: 1 addition & 0 deletions src/android/app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ Se esperan fallos gráficos temporales cuando ésta esté activado.</string>
<string name="emulation_empty_state_slot">Estado %1$d</string>
<string name="emulation_occupied_state_slot">Estado %1$d - %2$tF %2$tR</string>
<string name="emulation_show_fps">Mostrar FPS</string>
<string name="emulation_haptic_feedback">Respuesta Háptica</string>
<string name="emulation_overlay_options">Opciones de estilo</string>
<string name="emulation_configure_controls">Configurar Controles</string>
<string name="emulation_edit_layout">Editar Estilo</string>
Expand Down
1 change: 1 addition & 0 deletions src/android/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,7 @@
<string name="emulation_empty_state_slot">Slot %1$d</string>
<string name="emulation_occupied_state_slot">Slot %1$d - %2$tF %2$tR</string>
<string name="emulation_show_fps">Show FPS</string>
<string name="emulation_haptic_feedback">Haptic Feedback</string>
<string name="emulation_overlay_options">Overlay Options</string>
<string name="emulation_configure_controls">Configure Controls</string>
<string name="emulation_edit_layout">Edit Layout</string>
Expand Down

0 comments on commit e54c117

Please sign in to comment.