diff --git a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationActivity.kt b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationActivity.kt index ff1ba9d4..bf197166 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationActivity.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationActivity.kt @@ -5,7 +5,6 @@ import android.os.Bundle import android.text.InputFilter.LengthFilter import android.view.KeyEvent import android.view.MotionEvent -import android.view.View import androidx.activity.OnBackPressedCallback import androidx.annotation.Keep import androidx.appcompat.app.AlertDialog @@ -20,11 +19,9 @@ import info.cemu.cemu.databinding.ActivityEmulationBinding import info.cemu.cemu.emulation.EmulationFragment.OnEmulationErrorCallback import info.cemu.cemu.input.InputManager import info.cemu.cemu.nativeinterface.NativeSwkbd.setCurrentInputText -import java.util.Objects import kotlin.system.exitProcess class EmulationActivity : AppCompatActivity() { - private var hasEmulationError = false private val inputManager = InputManager() private var emulationTextInputDialog: AlertDialog? = null @@ -95,10 +92,6 @@ class EmulationActivity : AppCompatActivity() { } private fun onEmulationError(emulationError: String?) { - if (hasEmulationError) { - return - } - hasEmulationError = true val builder = MaterialAlertDialogBuilder(this) builder.setTitle(R.string.error) .setMessage(emulationError) @@ -143,19 +136,13 @@ class EmulationActivity : AppCompatActivity() { val inputEditText = inputEditTextLayout.requireViewById(R.id.emulation_input_text) inputEditText.updateText(initialText) - val dialog = MaterialAlertDialogBuilder( - emulationActivityInstance!! - ) + val dialog = MaterialAlertDialogBuilder(emulationActivityInstance!!) .setView(inputEditTextLayout) .setCancelable(false) - .setPositiveButton( - R.string.done - ) { d: DialogInterface?, w: Int -> }.show() - val doneButton = Objects.requireNonNull( - dialog.getButton(DialogInterface.BUTTON_POSITIVE) - ) + .setPositiveButton(R.string.done) { _, _ -> }.show() + val doneButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)!! doneButton.isEnabled = false - doneButton.setOnClickListener { _: View? -> inputEditText.onFinishedEdit() } + doneButton.setOnClickListener { _ -> inputEditText.onFinishedEdit() } inputEditText.setOnTextChangedListener { doneButton.isEnabled = it.isNotEmpty() } diff --git a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt index a9697005..8b76f404 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/emulation/EmulationFragment.kt @@ -21,8 +21,9 @@ import androidx.fragment.app.Fragment import info.cemu.cemu.R import info.cemu.cemu.databinding.FragmentEmulationBinding import info.cemu.cemu.input.SensorManager -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider +import info.cemu.cemu.inputoverlay.InputOverlaySettingsManager import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView +import info.cemu.cemu.inputoverlay.OverlaySettings import info.cemu.cemu.nativeinterface.NativeEmulation import info.cemu.cemu.nativeinterface.NativeEmulation.clearSurface import info.cemu.cemu.nativeinterface.NativeEmulation.initializeRenderer @@ -35,40 +36,43 @@ import info.cemu.cemu.nativeinterface.NativeInput.onTouchDown import info.cemu.cemu.nativeinterface.NativeInput.onTouchMove import info.cemu.cemu.nativeinterface.NativeInput.onTouchUp -@SuppressLint("ClickableViewAccessibility") -class EmulationFragment(private val launchPath: String) : Fragment(), - PopupMenu.OnMenuItemClickListener { - private class OnSurfaceTouchListener(val isTV: Boolean) : OnTouchListener { - var currentPointerId: Int = -1 - override fun onTouch(v: View, event: MotionEvent): Boolean { - val pointerIndex = event.actionIndex - val pointerId = event.getPointerId(pointerIndex) - if (currentPointerId != -1 && pointerId != currentPointerId) { - return false - } - val x = event.getX(pointerIndex).toInt() - val y = event.getY(pointerIndex).toInt() - when (event.actionMasked) { - MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { - onTouchDown(x, y, isTV) - return true - } +private class OnSurfaceTouchListener(val isTV: Boolean) : OnTouchListener { + var currentPointerId: Int = -1 - MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> { - currentPointerId = -1 - onTouchUp(x, y, isTV) - return true - } - - MotionEvent.ACTION_MOVE -> { - onTouchMove(x, y, isTV) - return true - } - } + @SuppressLint("ClickableViewAccessibility") + override fun onTouch(v: View, event: MotionEvent): Boolean { + val pointerIndex = event.actionIndex + val pointerId = event.getPointerId(pointerIndex) + if (currentPointerId != -1 && pointerId != currentPointerId) { return false } + val x = event.getX(pointerIndex).toInt() + val y = event.getY(pointerIndex).toInt() + when (event.actionMasked) { + MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { + onTouchDown(x, y, isTV) + return true + } + + MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> { + currentPointerId = -1 + onTouchUp(x, y, isTV) + return true + } + + MotionEvent.ACTION_MOVE -> { + onTouchMove(x, y, isTV) + return true + } + } + return false } +} + +class EmulationFragment(private val launchPath: String) : Fragment(), + PopupMenu.OnMenuItemClickListener { + private inner class SurfaceHolderCallback(val isMainCanvas: Boolean) : SurfaceHolder.Callback { var surfaceSet: Boolean = false @@ -107,8 +111,9 @@ class EmulationFragment(private val launchPath: String) : Fragment(), private var isGameRunning = false private var padCanvas: SurfaceView? = null private var toast: Toast? = null - private var binding: FragmentEmulationBinding? = null + private lateinit var binding: FragmentEmulationBinding private var isMotionEnabled = false + private lateinit var overlaySettings: OverlaySettings private var settingsMenu: PopupMenu? = null private var inputOverlaySurfaceView: InputOverlaySurfaceView? = null private var sensorManager: SensorManager? = null @@ -121,45 +126,43 @@ class EmulationFragment(private val launchPath: String) : Fragment(), override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - val inputOverlaySettingsProvider = InputOverlaySettingsProvider(requireContext()) - if (sensorManager == null) { - sensorManager = SensorManager(requireContext()) - } - sensorManager!!.setIsLandscape(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) + val inputOverlaySettingsManager = InputOverlaySettingsManager(requireContext()) + overlaySettings = inputOverlaySettingsManager.overlaySettings + sensorManager = SensorManager(requireContext()) + sensorManager?.setIsLandscape(resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) } override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) - if (sensorManager != null) { - sensorManager!!.setIsLandscape(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) - } + sensorManager?.setIsLandscape(newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) } override fun onPause() { super.onPause() - sensorManager!!.pauseListening() + sensorManager?.pauseListening() } override fun onResume() { super.onResume() if (isMotionEnabled) { - sensorManager!!.startListening() + sensorManager?.startListening() } } override fun onDestroy() { super.onDestroy() if (sensorManager != null) { - sensorManager!!.pauseListening() + sensorManager?.pauseListening() } } + @SuppressLint("ClickableViewAccessibility") private fun createPadCanvas() { if (padCanvas != null) { return } padCanvas = SurfaceView(requireContext()) - binding!!.canvasesLayout.addView( + binding.canvasesLayout.addView( padCanvas, LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1.0f) ) @@ -179,7 +182,7 @@ class EmulationFragment(private val launchPath: String) : Fragment(), if (padCanvas == null) { return } - binding!!.canvasesLayout.removeView(padCanvas) + binding.canvasesLayout.removeView(padCanvas) padCanvas = null } @@ -200,9 +203,9 @@ class EmulationFragment(private val launchPath: String) : Fragment(), return true } if (itemId == R.id.edit_inputs) { - binding!!.editInputsLayout.visibility = View.VISIBLE - binding!!.finishEditInputsButton.visibility = View.VISIBLE - binding!!.moveInputsButton.performClick() + binding.editInputsLayout.visibility = View.VISIBLE + binding.finishEditInputsButton.visibility = View.VISIBLE + binding.moveInputsButton.performClick() return true } if (itemId == R.id.replace_tv_with_pad) { @@ -218,9 +221,9 @@ class EmulationFragment(private val launchPath: String) : Fragment(), if (itemId == R.id.enable_motion) { isMotionEnabled = !item.isChecked if (isMotionEnabled) { - sensorManager!!.startListening() + sensorManager?.startListening() } else { - sensorManager!!.pauseListening() + sensorManager?.pauseListening() } item.setChecked(isMotionEnabled) return true @@ -242,50 +245,51 @@ class EmulationFragment(private val launchPath: String) : Fragment(), return false } + @SuppressLint("ClickableViewAccessibility") override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?, ): View { binding = FragmentEmulationBinding.inflate(inflater, container, false) - inputOverlaySurfaceView = binding!!.inputOverlay + inputOverlaySurfaceView = binding.inputOverlay - binding!!.moveInputsButton.setOnClickListener { v: View? -> + binding.moveInputsButton.setOnClickListener { v: View? -> if (inputOverlaySurfaceView!!.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_POSITION) { return@setOnClickListener } - binding!!.resizeInputsButton.alpha = 0.5f - binding!!.moveInputsButton.alpha = 1.0f + binding.resizeInputsButton.alpha = 0.5f + binding.moveInputsButton.alpha = 1.0f toastMessage(R.string.input_mode_edit_position) inputOverlaySurfaceView!!.setInputMode(InputOverlaySurfaceView.InputMode.EDIT_POSITION) } - binding!!.resizeInputsButton.setOnClickListener { v: View? -> + binding.resizeInputsButton.setOnClickListener { v: View? -> if (inputOverlaySurfaceView!!.getInputMode() == InputOverlaySurfaceView.InputMode.EDIT_SIZE) { return@setOnClickListener } - binding!!.moveInputsButton.alpha = 0.5f - binding!!.resizeInputsButton.alpha = 1.0f + binding.moveInputsButton.alpha = 0.5f + binding.resizeInputsButton.alpha = 1.0f toastMessage(R.string.input_mode_edit_size) inputOverlaySurfaceView!!.setInputMode(InputOverlaySurfaceView.InputMode.EDIT_SIZE) } - binding!!.finishEditInputsButton.setOnClickListener { v: View? -> + binding.finishEditInputsButton.setOnClickListener { v: View? -> inputOverlaySurfaceView!!.setInputMode(InputOverlaySurfaceView.InputMode.DEFAULT) - binding!!.finishEditInputsButton.visibility = View.GONE - binding!!.editInputsLayout.visibility = View.GONE + binding.finishEditInputsButton.visibility = View.GONE + binding.editInputsLayout.visibility = View.GONE toastMessage(R.string.input_mode_default) } - settingsMenu = PopupMenu(requireContext(), binding!!.emulationSettingsButton) + settingsMenu = PopupMenu(requireContext(), binding.emulationSettingsButton)!! settingsMenu!!.menuInflater.inflate(R.menu.emulation, settingsMenu!!.menu) settingsMenu!!.setOnMenuItemClickListener(this@EmulationFragment) - binding!!.emulationSettingsButton.setOnClickListener { v: View? -> settingsMenu!!.show() } + binding.emulationSettingsButton.setOnClickListener { v: View? -> settingsMenu!!.show() } val menu = settingsMenu!!.menu -// menu.findItem(R.id.show_input_overlay).setChecked(overlaySettings!!.isOverlayEnabled) -// if (!overlaySettings!!.isOverlayEnabled) { -// menu.findItem(R.id.reset_inputs).setEnabled(false) -// menu.findItem(R.id.edit_inputs).setEnabled(false) -// inputOverlaySurfaceView!!.visibility = View.GONE -// } - val mainCanvas = binding!!.mainCanvas + menu.findItem(R.id.show_input_overlay).setChecked(overlaySettings!!.isOverlayEnabled) + if (!overlaySettings!!.isOverlayEnabled) { + menu.findItem(R.id.reset_inputs).setEnabled(false) + menu.findItem(R.id.edit_inputs).setEnabled(false) + inputOverlaySurfaceView!!.visibility = View.GONE + } + val mainCanvas = binding.mainCanvas try { val testSurfaceTexture = SurfaceTexture(0) val testSurface = Surface(testSurfaceTexture) @@ -299,7 +303,7 @@ class EmulationFragment(private val launchPath: String) : Fragment(), exception.message ) ) - return binding!!.root + return binding.root } val mainCanvasHolder = mainCanvas.holder @@ -327,7 +331,7 @@ class EmulationFragment(private val launchPath: String) : Fragment(), } }) mainCanvas.setOnTouchListener(OnSurfaceTouchListener(true)) - return binding!!.root + return binding.root } private fun startGame() { diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt index aa9a1cef..578d6bb4 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Button.kt @@ -2,52 +2,23 @@ package info.cemu.cemu.inputoverlay import android.content.res.Resources import android.graphics.Canvas -import android.graphics.drawable.Drawable +import android.graphics.Rect import android.view.MotionEvent import androidx.annotation.DrawableRes -import androidx.core.content.res.ResourcesCompat -import info.cemu.cemu.drawable.getInvertedDrawable -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings -import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView.OverlayButton -import java.util.Objects abstract class Button( resources: Resources, @DrawableRes buttonId: Int, - buttonStateChangeListener: ButtonStateChangeListener, - button: OverlayButton, - settings: InputOverlaySettings -) : - Input(settings) { - @JvmField - protected var iconPressed: Drawable + private val onButtonStateChange: (state: Boolean) -> Unit, + boundingRect: Rect, +) : Input(boundingRect) { - @JvmField - protected var iconNotPressed: Drawable = ResourcesCompat.getDrawable( - resources, - buttonId, - null - )!! - @JvmField - protected var icon: Drawable + protected val inputDrawable = InputDrawable(resources, buttonId) - @JvmField - protected var currentPointerId: Int = -1 - private val buttonStateChangeListener: ButtonStateChangeListener - private val button: OverlayButton - - init { - iconPressed = iconNotPressed.getInvertedDrawable(resources) - iconPressed.alpha = settings.alpha - iconNotPressed.alpha = settings.alpha - icon = iconNotPressed - this.buttonStateChangeListener = buttonStateChangeListener - this.button = button - } + private var currentPointerId: Int = -1 override fun onTouch(event: MotionEvent): Boolean { - var stateUpdated = false when (event.actionMasked) { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { val pointerIndex = event.actionIndex @@ -56,7 +27,7 @@ abstract class Button( if (isInside(x, y)) { currentPointerId = event.getPointerId(pointerIndex) updateState(true) - stateUpdated = true + return true } } @@ -64,27 +35,23 @@ abstract class Button( if (event.getPointerId(event.actionIndex) == currentPointerId) { currentPointerId = -1 updateState(false) - stateUpdated = true + return true } } } - return stateUpdated + return false } override fun drawInput(canvas: Canvas) { - icon.draw(canvas) + inputDrawable.icon.draw(canvas) } override fun resetInput() { updateState(false) } - protected fun updateState(state: Boolean) { - icon = if (state) { - iconPressed - } else { - iconNotPressed - } - buttonStateChangeListener.onButtonStateChange(button, state) + private fun updateState(state: Boolean) { + inputDrawable.setActiveState(state) + onButtonStateChange(state) } } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/ButtonStateChangeListener.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/ButtonStateChangeListener.kt deleted file mode 100644 index 02f7ac58..00000000 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/ButtonStateChangeListener.kt +++ /dev/null @@ -1,7 +0,0 @@ -package info.cemu.cemu.inputoverlay - -import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView.OverlayButton - -fun interface ButtonStateChangeListener { - fun onButtonStateChange(button: OverlayButton?, state: Boolean) -} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt index a818a674..25597c02 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/DPadInput.kt @@ -2,12 +2,10 @@ package info.cemu.cemu.inputoverlay import android.content.res.Resources import android.graphics.Canvas -import android.graphics.drawable.Drawable +import android.graphics.Rect import android.view.MotionEvent import androidx.annotation.DrawableRes import androidx.core.content.res.ResourcesCompat -import info.cemu.cemu.drawable.applyInvertedColorTransform -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings import kotlin.math.atan2 import kotlin.math.min @@ -15,290 +13,110 @@ class DPadInput( resources: Resources, @DrawableRes backgroundId: Int, @DrawableRes buttonId: Int, - private val buttonStateChangeListener: ButtonStateChangeListener, - settings: InputOverlaySettings -) : - Input(settings) { - var iconDpadUp: Drawable - var iconDpadUpPressed: Drawable - var iconDpadUpNotPressed: Drawable + private val onButtonStateChange: (button: OverlayDpad, state: Boolean) -> Unit, + private val alpha: Int, + rect: Rect, +) : Input(rect) { + private val dpadUpDrawable = InputDrawable(resources, buttonId) + private val dpadDownDrawable = InputDrawable(resources, buttonId) + private val dpadLeftDrawable = InputDrawable(resources, buttonId) + private val dpadRightDrawable = InputDrawable(resources, buttonId) + private val background = ResourcesCompat.getDrawable(resources, backgroundId, null)!! - var iconDpadDown: Drawable - var iconDpadDownPressed: Drawable - var iconDpadDownNotPressed: Drawable + private var dpadState: Int = NONE - var iconDpadLeft: Drawable - var iconDpadLeftPressed: Drawable - var iconDpadLeftNotPressed: Drawable - - var iconDpadRight: Drawable - var iconDpadRightPressed: Drawable - var iconDpadRightNotPressed: Drawable - var background: Drawable = ResourcesCompat.getDrawable(resources, backgroundId, null)!! - - enum class DpadState { - NONE, UP, DOWN, LEFT, RIGHT, UP_LEFT, UP_RIGHT, DOWN_LEFT, DOWN_RIGHT, - } - - var dpadState: DpadState = DpadState.NONE - - var centreX: Int = 0 - var centreY: Int = 0 - var radius: Int = 0 - var currentPointerId: Int = -1 + private var centerX: Int = 0 + private var centerY: Int = 0 + private var radius2: Int = 0 + private var currentPointerId: Int = -1 override fun configure() { - val rect = settings.rect - this.centreX = rect.centerX() - this.centreY = rect.centerY() - this.radius = (min(rect.width(), rect.height()) / 2.0f).toInt() - background.alpha = settings.alpha + this.centerX = rect.centerX() + this.centerY = rect.centerY() + val radius = min(rect.width(), rect.height()) / 2 + radius2 = radius * radius + background.alpha = alpha background.setBounds( - centreX - radius, - centreY - radius, - centreX + radius, - centreY + radius + centerX - radius, + centerY - radius, + centerX + radius, + centerY + radius ) - val buttonSize = (radius * 0.5f).toInt() - val configureButton = - { circleXPos: Int, circleYPos: Int, pressedButton: Drawable, notPressedButton: Drawable -> - pressedButton.alpha = - settings.alpha - notPressedButton.alpha = settings.alpha - val left = circleXPos - buttonSize / 2 - val top = circleYPos - buttonSize / 2 - pressedButton.setBounds(left, top, left + buttonSize, top + buttonSize) - notPressedButton.setBounds(left, top, left + buttonSize, top + buttonSize) - } + val buttonSize = radius / 2 + val configureButton = { circleXPos: Int, circleYPos: Int, inputDrawable: InputDrawable -> + inputDrawable.setAlpha(alpha) + val left = circleXPos - buttonSize / 2 + val top = circleYPos - buttonSize / 2 + inputDrawable.setBounds(left, top, left + buttonSize, top + buttonSize) + } configureButton( - centreX, - centreY - 2 * radius / 3, - iconDpadUpPressed, - iconDpadUp + centerX, + centerY - 2 * radius / 3, + dpadUpDrawable ) configureButton( - centreX, - centreY + 2 * radius / 3, - iconDpadDownPressed, - iconDpadDownNotPressed + centerX, + centerY + 2 * radius / 3, + dpadDownDrawable ) configureButton( - centreX - 2 * radius / 3, - centreY, - iconDpadLeftPressed, - iconDpadLeftNotPressed + centerX - 2 * radius / 3, + centerY, + dpadLeftDrawable ) configureButton( - centreX + 2 * radius / 3, - centreY, - iconDpadRightPressed, - iconDpadRightNotPressed + centerX + 2 * radius / 3, + centerY, + dpadRightDrawable ) } init { - val getNotPressedButtonIcon = { - ResourcesCompat.getDrawable(resources, buttonId, null)!! - } - val getPressedButtonIcon = { - ResourcesCompat.getDrawable(resources, buttonId, null)!! - .applyInvertedColorTransform() - } - - iconDpadUpPressed = getPressedButtonIcon() - iconDpadUpNotPressed = getNotPressedButtonIcon() - iconDpadUp = iconDpadUpNotPressed - - iconDpadDownPressed = getPressedButtonIcon() - iconDpadDownNotPressed = getNotPressedButtonIcon() - iconDpadDown = iconDpadDownNotPressed - - iconDpadLeftPressed = getPressedButtonIcon() - iconDpadLeftNotPressed = getNotPressedButtonIcon() - iconDpadLeft = iconDpadLeftNotPressed - - iconDpadRightPressed = getPressedButtonIcon() - iconDpadRightNotPressed = getNotPressedButtonIcon() - iconDpadRight = iconDpadRightNotPressed - configure() } - private fun updateState(nextDpadState: DpadState) { - when (dpadState) { - DpadState.NONE -> {} - - DpadState.UP -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - false - ) - iconDpadUp = iconDpadUpNotPressed - } - - DpadState.DOWN -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - false - ) - iconDpadDown = iconDpadDownNotPressed - } - - DpadState.LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - false - ) - iconDpadLeft = iconDpadLeftNotPressed - } - - DpadState.RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - false - ) - iconDpadRight = iconDpadRightNotPressed - } - - DpadState.UP_LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - false - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - false - ) - iconDpadUp = iconDpadUpNotPressed - iconDpadLeft = iconDpadLeftNotPressed - } - - DpadState.UP_RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - false - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - false - ) - iconDpadUp = iconDpadUpNotPressed - iconDpadRight = iconDpadRightNotPressed - } - - DpadState.DOWN_LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - false - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - false - ) - iconDpadDown = iconDpadDownNotPressed - iconDpadLeft = iconDpadLeftNotPressed - } - - DpadState.DOWN_RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - false - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - false - ) - iconDpadDown = iconDpadDownNotPressed - iconDpadRight = iconDpadRightNotPressed - } - } + private fun updateState(nextDpadState: Int) { + updateState(dpadState and nextDpadState.inv(), false) + updateState(nextDpadState and dpadState.inv(), true) dpadState = nextDpadState - when (nextDpadState) { - DpadState.NONE -> {} - DpadState.UP -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - true - ) - iconDpadUp = iconDpadUpPressed - } + } - DpadState.DOWN -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - true - ) - iconDpadDown = iconDpadDownPressed - } + private fun updateState(dpadState: Int, pressed: Boolean) { + if (dpadState == NONE) return + if ((dpadState and UP) != 0) { + onButtonStateChange(OverlayDpad.DPAD_UP, pressed) + dpadUpDrawable.setActiveState(pressed) + } + if ((dpadState and DOWN) != 0) { + onButtonStateChange(OverlayDpad.DPAD_DOWN, pressed) + dpadDownDrawable.setActiveState(pressed) + } + if ((dpadState and LEFT) != 0) { + onButtonStateChange(OverlayDpad.DPAD_LEFT, pressed) + dpadLeftDrawable.setActiveState(pressed) + } + if ((dpadState and RIGHT) != 0) { + onButtonStateChange(OverlayDpad.DPAD_RIGHT, pressed) + dpadRightDrawable.setActiveState(pressed) + } + } - DpadState.LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - true - ) - iconDpadLeft = iconDpadLeftPressed - } - - DpadState.RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - true - ) - iconDpadRight = iconDpadRightPressed - } - - DpadState.UP_LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - true - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - true - ) - iconDpadUp = iconDpadUpPressed - iconDpadLeft = iconDpadLeftPressed - } - - DpadState.UP_RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_UP, - true - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - true - ) - iconDpadUp = iconDpadUpPressed - iconDpadRight = iconDpadRightPressed - } - - DpadState.DOWN_LEFT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - true - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_LEFT, - true - ) - iconDpadDown = iconDpadDownPressed - iconDpadLeft = iconDpadLeftPressed - } - - DpadState.DOWN_RIGHT -> { - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_DOWN, - true - ) - buttonStateChangeListener.onButtonStateChange( - InputOverlaySurfaceView.OverlayButton.DPAD_RIGHT, - true - ) - iconDpadDown = iconDpadDownPressed - iconDpadRight = iconDpadRightPressed - } + private fun getStateByPosition(x: Int, y: Int): Int { + val norm2 = (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) + if (norm2 <= radius2 * 0.1f) { + return NONE + } + val angle = atan2((y - centerY).toDouble(), (x - centerX).toDouble()) + return when (angle) { + in -0.875 * Math.PI..<-0.625 * Math.PI -> UP or LEFT + in -0.625 * Math.PI..<-0.375 * Math.PI -> UP + in -0.375 * Math.PI..<-0.125 * Math.PI -> UP or RIGHT + in -0.125 * Math.PI..<0.125 * Math.PI -> RIGHT + in 0.125 * Math.PI..<0.375 * Math.PI -> DOWN or RIGHT + in 0.375 * Math.PI..<0.625 * Math.PI -> DOWN + in 0.625 * Math.PI..<0.875 * Math.PI -> DOWN or LEFT + else -> LEFT // in [-pi, -0.875*pi) or [0.875*pi, pi] } } @@ -312,8 +130,7 @@ class DPadInput( val pointerId = event.getPointerId(pointerIndex) if (isInside(x, y)) { currentPointerId = pointerId - val angle = atan2((y - centreY).toDouble(), (x - centreX).toDouble()) - nextState = getStateByAngle(angle) + nextState = getStateByPosition(x, y) } } @@ -321,7 +138,7 @@ class DPadInput( val pointerId = event.getPointerId(event.actionIndex) if (pointerId == currentPointerId) { currentPointerId = -1 - nextState = DpadState.NONE + nextState = NONE } } @@ -335,13 +152,7 @@ class DPadInput( } val x = event.getX(i).toInt() val y = event.getY(i).toInt() - val norm2 = ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) - if (norm2 <= radius * radius * 0.1f) { - nextState = DpadState.NONE - break - } - val angle = atan2((y - centreY).toDouble(), (x - centreX).toDouble()) - nextState = getStateByAngle(angle) + nextState = getStateByPosition(x, y) break } } @@ -354,43 +165,26 @@ class DPadInput( } override fun resetInput() { - updateState(DpadState.NONE) - } - - private fun getStateByAngle(angle: Double): DpadState { - if (-0.125 * Math.PI <= angle && angle < 0.125 * Math.PI) { - return DpadState.RIGHT - } - if (0.125 * Math.PI <= angle && angle < 0.375 * Math.PI) { - return DpadState.DOWN_RIGHT - } - if (0.375 * Math.PI <= angle && angle < 0.625 * Math.PI) { - return DpadState.DOWN - } - if (0.625 * Math.PI <= angle && angle < 0.875 * Math.PI) { - return DpadState.DOWN_LEFT - } - if (-0.875 * Math.PI <= angle && angle < -0.625 * Math.PI) { - return DpadState.UP_LEFT - } - if (-0.625 * Math.PI <= angle && angle < -0.375 * Math.PI) { - return DpadState.UP - } - if (-0.375 * Math.PI <= angle && angle < -0.125 * Math.PI) { - return DpadState.UP_RIGHT - } - return DpadState.LEFT + updateState(NONE) } override fun isInside(x: Int, y: Int): Boolean { - return ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) <= radius * radius + return (x - centerX) * (x - centerX) + (y - centerY) * (y - centerY) <= radius2 } override fun drawInput(canvas: Canvas) { background.draw(canvas) - iconDpadUp.draw(canvas) - iconDpadDown.draw(canvas) - iconDpadLeft.draw(canvas) - iconDpadRight.draw(canvas) + dpadUpDrawable.icon.draw(canvas) + dpadDownDrawable.icon.draw(canvas) + dpadLeftDrawable.icon.draw(canvas) + dpadRightDrawable.icon.draw(canvas) + } + + companion object { + private const val NONE = 0 + private const val UP = 1 shl 0 + private const val DOWN = 1 shl 1 + private const val LEFT = 1 shl 2 + private const val RIGHT = 1 shl 3 } } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt index ec6da119..125b732a 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Input.kt @@ -5,57 +5,55 @@ import android.graphics.Paint import android.graphics.Rect import android.view.MotionEvent import androidx.annotation.ColorInt -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings import kotlin.math.max import kotlin.math.min -abstract class Input protected constructor(@JvmField protected val settings: InputOverlaySettings) { +abstract class Input protected constructor( + protected var rect: Rect, +) { private var drawBoundingRectangle = false - private var boundingRectangle: Rect private val boundingRectanglePaint = Paint() - init { - boundingRectangle = settings.rect - } + fun getBoundingRectangle() = rect abstract fun onTouch(event: MotionEvent): Boolean fun moveInput(x: Int, y: Int, maxWidth: Int, maxHeight: Int) { - val rect = settings.rect val width = rect.width() val height = rect.height() val left = min( - max(x - width / 2.0f, 0.0f), - (maxWidth - width).toFloat() - ).toInt() + max(x - width / 2, 0), + maxWidth - width + ) val top = min( - max(y - height / 2.0f, 0.0f), - (maxHeight - height).toFloat() - ).toInt() - boundingRectangle = Rect( + max(y - height / 2, 0), + maxHeight - height + ) + rect = Rect( left, top, left + width, top + height ) - settings.rect = boundingRectangle configure() } fun resize(diffX: Int, diffY: Int, maxWidth: Int, maxHeight: Int, minWidthHeight: Int) { - val rect = settings.rect val newRight = rect.right + diffX val newBottom = rect.bottom + diffY - if (newRight - rect.left < minWidthHeight || newBottom - rect.top < minWidthHeight || newRight > maxWidth || newBottom > maxHeight) { + if (newRight - rect.left < minWidthHeight + || newBottom - rect.top < minWidthHeight + || newRight > maxWidth + || newBottom > maxHeight + ) { return } - boundingRectangle = Rect( + rect = Rect( rect.left, rect.top, newRight, newBottom ) - settings.rect = boundingRectangle configure() } @@ -65,17 +63,13 @@ abstract class Input protected constructor(@JvmField protected val settings: Inp drawBoundingRectangle = false } - fun saveConfiguration() { - settings.saveSettings() - } - protected abstract fun configure() protected abstract fun drawInput(canvas: Canvas) fun draw(canvas: Canvas) { if (drawBoundingRectangle) { - canvas.drawRect(boundingRectangle, boundingRectanglePaint) + canvas.drawRect(rect, boundingRectanglePaint) } drawInput(canvas) } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt new file mode 100644 index 00000000..1f2060cf --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputDrawable.kt @@ -0,0 +1,41 @@ +package info.cemu.cemu.inputoverlay + +import android.content.res.Resources +import android.graphics.Rect +import android.graphics.drawable.Drawable +import androidx.annotation.DrawableRes +import androidx.core.content.res.ResourcesCompat +import info.cemu.cemu.drawable.getInvertedDrawable + +class InputDrawable( + resources: Resources, + @DrawableRes inputDrawableId: Int, +) { + private val iconInactive: Drawable = ResourcesCompat.getDrawable( + resources, + inputDrawableId, + null + )!! + private val iconActive: Drawable = iconInactive.getInvertedDrawable(resources) + private var _icon = iconInactive + val icon: Drawable + get() = _icon + + fun setActiveState(active: Boolean) { + _icon = if (active) iconActive else iconInactive + } + + fun setAlpha(alpha: Int) { + iconActive.alpha = alpha + iconInactive.alpha = alpha + } + + fun setBounds(bounds: Rect) { + iconActive.bounds = bounds + iconInactive.bounds = bounds + } + + fun setBounds(left: Int, top: Int, right: Int, bottom: Int) { + setBounds(Rect(left, top, right, bottom)) + } +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlayDefaultConfigParser.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlayDefaultConfigParser.kt new file mode 100644 index 00000000..11ccbe23 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlayDefaultConfigParser.kt @@ -0,0 +1,93 @@ +package info.cemu.cemu.inputoverlay + +import org.xmlpull.v1.XmlPullParser + +data class InputConfig( + val width: Int, + val height: Int, + val alignEnd: Boolean, + val alignBottom: Boolean, + val paddingHorizontal: Int, + val paddingVertical: Int, +) + +private fun String.toIntOrZero() = toIntOrNull() ?: 0 + +private const val INPUT_OVERLAY_CONFIG_TAG_NAME = "input-overlay-config" +private const val WIDTH_CONFIG_TAG_NAME = "width" +private const val SIZE_CONFIG_TAG_NAME = "size" +private const val HEIGHT_CONFIG_TAG_NAME = "height" +private const val ALIGN_END_CONFIG_TAG_NAME = "align-end" +private const val ALIGN_BOTTOM_CONFIG_TAG_NAME = "align-bottom" +private const val PADDING_HORIZONTAL_CONFIG_TAG_NAME = "padding-horizontal" +private const val PADDING_VERTICAL_CONFIG_TAG_NAME = "padding-vertical" +private const val NAME_CONFIG_TAG_NAME = "name" + +private fun parseInputConfig(xmlPullParser: XmlPullParser): Pair? { + var name = "" + var width = 0 + var height = 0 + var alignEnd = false + var alignBottom = false + var paddingHorizontal = 0 + var paddingVertical = 0 + var eventType = xmlPullParser.eventType + var currentTag = "" + while (eventType != XmlPullParser.END_DOCUMENT) { + when (eventType) { + XmlPullParser.END_TAG -> { + if (xmlPullParser.name == INPUT_OVERLAY_CONFIG_TAG_NAME) { + return if (name.isBlank()) null + else name to InputConfig( + width = width, + height = height, + alignEnd = alignEnd, + alignBottom = alignBottom, + paddingHorizontal = paddingHorizontal, + paddingVertical = paddingVertical, + ) + } + } + + XmlPullParser.START_TAG -> { + currentTag = xmlPullParser.name + } + + XmlPullParser.TEXT -> { + val text = xmlPullParser.text + when (currentTag) { + SIZE_CONFIG_TAG_NAME -> { + val size = text.toIntOrZero() + width = size + height = size + } + + WIDTH_CONFIG_TAG_NAME -> width = text.toIntOrZero() + HEIGHT_CONFIG_TAG_NAME -> height = text.toIntOrZero() + ALIGN_END_CONFIG_TAG_NAME -> alignEnd = text.toBoolean() + ALIGN_BOTTOM_CONFIG_TAG_NAME -> alignBottom = text.toBoolean() + PADDING_HORIZONTAL_CONFIG_TAG_NAME -> paddingHorizontal = text.toIntOrZero() + PADDING_VERTICAL_CONFIG_TAG_NAME -> paddingVertical = text.toIntOrZero() + NAME_CONFIG_TAG_NAME -> name = text + } + } + } + eventType = xmlPullParser.next() + } + return null +} + +internal fun parseDefaultInputConfigs(xmlPullParser: XmlPullParser): Map { + val inputConfigs = mutableMapOf() + var eventType = xmlPullParser.eventType + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG && xmlPullParser.name == INPUT_OVERLAY_CONFIG_TAG_NAME) { + val inputConfig = parseInputConfig(xmlPullParser) + if (inputConfig != null) { + inputConfigs[inputConfig.first] = inputConfig.second + } + } + eventType = xmlPullParser.next() + } + return inputConfigs +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsManager.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsManager.kt new file mode 100644 index 00000000..4c286fbe --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsManager.kt @@ -0,0 +1,139 @@ +package info.cemu.cemu.inputoverlay + +import android.content.Context +import android.content.SharedPreferences +import android.graphics.Rect +import android.util.DisplayMetrics.DENSITY_DEFAULT +import androidx.annotation.IntRange +import info.cemu.cemu.R +import info.cemu.cemu.nativeinterface.NativeInput +import kotlin.math.max +import kotlin.math.min + +class OverlaySettings( + var isVibrateOnTouchEnabled: Boolean, + var isOverlayEnabled: Boolean, + @IntRange(0, (NativeInput.MAX_CONTROLLERS - 1L)) + var controllerIndex: Int, + @IntRange(0, 255) + var alpha: Int, +) { + companion object { + private const val DEFAULT_ALPHA: Int = 64 + private const val IS_VIBRATE_ON_TOUCH_ENABLED_KEY = "IS_VIBRATE_ON_TOUCH_ENABLED" + private const val IS_OVERLAY_ENABLED_KEY = "IS_OVERLAY_ENABLED" + private const val CONTROLLER_INDEX_KEY = "CONTROLLER_INDEX" + private const val ALPHA_KEY = "ALPHA" + } + + internal constructor(sharedPreferences: SharedPreferences) : this( + isVibrateOnTouchEnabled = sharedPreferences.getBoolean( + IS_VIBRATE_ON_TOUCH_ENABLED_KEY, + false + ), + isOverlayEnabled = sharedPreferences.getBoolean(IS_OVERLAY_ENABLED_KEY, false), + controllerIndex = sharedPreferences.getInt(CONTROLLER_INDEX_KEY, 0), + alpha = sharedPreferences.getInt(ALPHA_KEY, DEFAULT_ALPHA), + ) + + internal fun save(sharedPreferences: SharedPreferences) { + sharedPreferences.edit().apply { + putBoolean(IS_VIBRATE_ON_TOUCH_ENABLED_KEY, isVibrateOnTouchEnabled) + putBoolean(IS_OVERLAY_ENABLED_KEY, isOverlayEnabled) + putInt(CONTROLLER_INDEX_KEY, controllerIndex) + putInt(ALPHA_KEY, alpha) + apply() + } + } +} + +class InputOverlaySettingsManager(context: Context) { + private val defaultInputConfigs = + parseDefaultInputConfigs(context.resources.getXml(R.xml.input_overlay_default_configs)) + private val sharedPreferences: SharedPreferences = + context.getSharedPreferences(INPUT_OVERLAY_SETTINGS_NAME, Context.MODE_PRIVATE) + + var overlaySettings: OverlaySettings + get() = OverlaySettings(sharedPreferences) + set(value) = value.save(sharedPreferences) + + fun getInputOverlayRectangle( + input: OverlayInput, + width: Int, + height: Int, + density: Int, + ): Rect { + return getRectangle(input) ?: getDefaultRectangle(input, width, height, density) + } + + private fun getRectLeftConfigName(input: OverlayInput) = "${input.configName}_LEFT" + private fun getRectTopConfigName(input: OverlayInput) = "${input.configName}_TOP" + private fun getRectRightConfigName(input: OverlayInput) = "${input.configName}_RIGHT" + private fun getRectBottomConfigName(input: OverlayInput) = "${input.configName}_BOTTOM" + + private fun getRectangle(input: OverlayInput): Rect? { + val left = sharedPreferences.getInt(getRectLeftConfigName(input), -1) + val top = sharedPreferences.getInt(getRectTopConfigName(input), -1) + val right = sharedPreferences.getInt(getRectRightConfigName(input), -1) + val bottom = sharedPreferences.getInt(getRectBottomConfigName(input), -1) + if (left == -1 || top == -1 || right == -1 || bottom == -1) { + return null + } + return Rect(left, top, right, bottom) + } + + fun saveRectangle(input: OverlayInput, rect: Rect) { + sharedPreferences.edit().apply { + putInt(getRectLeftConfigName(input), rect.left) + putInt(getRectTopConfigName(input), rect.top) + putInt(getRectRightConfigName(input), rect.right) + putInt(getRectBottomConfigName(input), rect.bottom) + apply() + } + } + + + fun clearSavedRectangle(input: OverlayInput) { + sharedPreferences.edit().apply { + remove(getRectLeftConfigName(input)) + remove(getRectTopConfigName(input)) + remove(getRectRightConfigName(input)) + remove(getRectBottomConfigName(input)) + apply() + } + } + + private fun getDefaultRectangle( + input: OverlayInput, + width: Int, + height: Int, + density: Int, + ): Rect { + fun Int.dpToPx() = (this * density) / DENSITY_DEFAULT + val inputConfig = defaultInputConfigs[input.configName] ?: return Rect() + val inputWidth = inputConfig.width.dpToPx() + val horizontalPadding = inputConfig.paddingHorizontal.dpToPx() + val verticalPadding = inputConfig.paddingVertical.dpToPx() + val inputHeight = inputConfig.height.dpToPx() + val top = min( + max(if (inputConfig.alignBottom) height - inputHeight else 0, verticalPadding), + height - verticalPadding - inputHeight + ) + val left = min( + max(if (inputConfig.alignEnd) width - inputWidth else 0, horizontalPadding), + width - horizontalPadding - inputWidth + ) + val right = left + inputWidth + val bottom = top + inputHeight + return Rect( + left, + top, + right, + bottom, + ) + } + + companion object { + private const val INPUT_OVERLAY_SETTINGS_NAME = "INPUT_OVERLAY_SETTINGS" + } +} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsProvider.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsProvider.kt deleted file mode 100644 index bc5314b4..00000000 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySettingsProvider.kt +++ /dev/null @@ -1,306 +0,0 @@ -package info.cemu.cemu.inputoverlay - -import android.content.Context -import android.content.SharedPreferences -import android.graphics.Rect -import androidx.annotation.IntRange -import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.MutablePreferences -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.booleanPreferencesKey -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.intPreferencesKey -import androidx.datastore.preferences.preferencesDataStore -import info.cemu.cemu.CemuApplication -import info.cemu.cemu.nativeinterface.NativeInput -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import java.util.Optional - -private const val INPUT_DATA_STORE_NAME = "input-settings22" -private val Context.dataStore: DataStore by preferencesDataStore(name = INPUT_DATA_STORE_NAME) -private fun Preferences.get(key: Preferences.Key, default: T) = - this[key] ?: default - -class OverlaySettings( - var isVibrateOnTouchEnabled: Boolean, - var isOverlayEnabled: Boolean, - @IntRange(0, (NativeInput.MAX_CONTROLLERS - 1L)) - var controllerIndex: Int, - @IntRange(0, 255) - var alpha: Int, -) { - companion object { - private const val DEFAULT_ALPHA: Int = 64 - private const val IS_VIBRATE_ON_TOUCH_ENABLED_KEY = "IS_VIBRATE_ON_TOUCH_ENABLED" - private const val IS_OVERLAY_ENABLED_KEY = "IS_OVERLAY_ENABLED" - private const val CONTROLLER_INDEX_KEY = "CONTROLLER_INDEX" - private const val ALPHA_KEY = "ALPHA" - } - - internal constructor(sharedPreferences: SharedPreferences) : this( - isVibrateOnTouchEnabled = sharedPreferences.getBoolean( - IS_VIBRATE_ON_TOUCH_ENABLED_KEY, - false - ), - isOverlayEnabled = sharedPreferences.getBoolean(IS_OVERLAY_ENABLED_KEY, false), - controllerIndex = sharedPreferences.getInt(CONTROLLER_INDEX_KEY, 0), - alpha = sharedPreferences.getInt(ALPHA_KEY, DEFAULT_ALPHA), - ) - - internal fun save(sharedPreferences: SharedPreferences) { - sharedPreferences.edit().apply { - putBoolean(IS_VIBRATE_ON_TOUCH_ENABLED_KEY, isVibrateOnTouchEnabled) - putBoolean(IS_OVERLAY_ENABLED_KEY, isOverlayEnabled) - putInt(CONTROLLER_INDEX_KEY, controllerIndex) - putInt(ALPHA_KEY, alpha) - apply() - } - } -} - -class InputOverlaySettingsProvider(context: Context) { - enum class OverlayInput { - A, - B, - ONE, - TWO, - C, - Z, - HOME, - DPAD, - L, - L_STICK, - MINUS, - PLUS, - R, - R_STICK, - X, - Y, - ZL, - ZR, - NUN_CHUCK_AXIS, - LEFT_AXIS, - RIGHT_AXIS, - } - - val sharedPreferences: SharedPreferences = - context.getSharedPreferences("input-overlay-settings", Context.MODE_PRIVATE); - - var overlaySettings: OverlaySettings - get() = OverlaySettings(sharedPreferences) - set(value) = value.save(sharedPreferences) - - -// private val sharedPreferences: SharedPreferences = -// context.getSharedPreferences("settings", Context.MODE_PRIVATE) - - fun getOverlaySettingsForInput( - input: OverlayInput, - width: Int, - height: Int - ): InputOverlaySettings { - return InputOverlaySettings( - getRectangle(input).orElseGet({ getDefaultRectangle(input, width, height) }), - 100, - { settings: InputOverlaySettings -> -// val editor: SharedPreferences.Editor = sharedPreferences.edit() -// val rect: Rect? = settings.rect -// editor.putInt(input.toString() + "left", rect!!.left) -// editor.putInt(input.toString() + "top", rect.top) -// editor.putInt(input.toString() + "right", rect.right) -// editor.putInt(input.toString() + "bottom", rect.bottom) -// editor.putInt(input.toString() + "alpha", settings.alpha) -// editor.apply() - } - ) - } - - - private fun getRectangle(input: OverlayInput): Optional { -// val left: Int = sharedPreferences.getInt(input.toString() + "left", -1) -// val top: Int = sharedPreferences.getInt(input.toString() + "top", -1) -// val right: Int = sharedPreferences.getInt(input.toString() + "right", -1) -// val bottom: Int = sharedPreferences.getInt(input.toString() + "bottom", -1) -// if (left == -1 || top == -1 || right == -1 || bottom == -1) { -// } - return Optional.empty() -// return Optional.of(Rect(left, top, right, bottom)) - } - - fun getDefaultRectangle(input: OverlayInput, width: Int, height: Int): Rect { - val pmButtonRadius: Int = (height * 0.065f).toInt() - val pmButtonsCentreX: Int = (width * 0.5f).toInt() - val pmButtonsCentreY: Int = (height * 0.875f).toInt() - - val roundButtonRadius: Int = (height * 0.065f).toInt() - val abxyButtonsCentreX: Int = width - roundButtonRadius * 4 - val abxyButtonsCentreY: Int = height - roundButtonRadius * 4 - - val rectangleButtonsHeight: Int = (height * 0.10f).toInt() - val rectangleButtonsWidth: Int = rectangleButtonsHeight * 2 - - val triggersLLeft: Int = (width * 0.05f).toInt() - val triggersRLeft: Int = (width * 0.95f - rectangleButtonsWidth).toInt() - val triggersTop: Int = (height * 0.05f).toInt() - - val dpadRadius: Int = (height * 0.20f).toInt() - val dpadCentreX: Int = dpadRadius - val dpadCentreY: Int = (height * 0.75f).toInt() - - val joystickRadius: Int = (height * 0.10f).toInt() - val rightJoystickCentreX: Int = (width * 0.75f).toInt() - val leftJoystickCentreX: Int = (width * 0.25f).toInt() - val joystickCentreY: Int = (height * 0.55f).toInt() - val joystickClickRadius: Int = (joystickRadius * 0.7f).toInt() - // TODO: move this to res? - return when (input) { - OverlayInput.A -> Rect( - abxyButtonsCentreX + roundButtonRadius, - abxyButtonsCentreY - roundButtonRadius, - abxyButtonsCentreX + roundButtonRadius * 3, - abxyButtonsCentreY + roundButtonRadius - ) - - OverlayInput.B -> Rect( - abxyButtonsCentreX - roundButtonRadius, - abxyButtonsCentreY + roundButtonRadius, - abxyButtonsCentreX + roundButtonRadius, - abxyButtonsCentreY + roundButtonRadius * 3 - ) - - OverlayInput.X, OverlayInput.ONE -> Rect( - abxyButtonsCentreX - roundButtonRadius, - abxyButtonsCentreY - roundButtonRadius * 3, - abxyButtonsCentreX + roundButtonRadius, - abxyButtonsCentreY - roundButtonRadius - ) - - OverlayInput.Y, OverlayInput.TWO -> Rect( - abxyButtonsCentreX - roundButtonRadius * 3, - abxyButtonsCentreY - roundButtonRadius, - abxyButtonsCentreX - roundButtonRadius, - abxyButtonsCentreY + roundButtonRadius - ) - - OverlayInput.MINUS -> Rect( - pmButtonsCentreX - pmButtonRadius * 3, - pmButtonsCentreY - pmButtonRadius, - pmButtonsCentreX - pmButtonRadius, - pmButtonsCentreY + pmButtonRadius - ) - - OverlayInput.PLUS -> Rect( - pmButtonsCentreX + pmButtonRadius, - pmButtonsCentreY - pmButtonRadius, - pmButtonsCentreX + pmButtonRadius * 3, - pmButtonsCentreY + pmButtonRadius - ) - - OverlayInput.L -> Rect( - triggersLLeft, - triggersTop, - triggersLLeft + rectangleButtonsWidth, - triggersTop + rectangleButtonsHeight - ) - - OverlayInput.ZL -> Rect( - triggersLLeft, - (triggersTop + rectangleButtonsHeight * 1.5f).toInt(), - triggersLLeft + rectangleButtonsWidth, - (triggersTop + rectangleButtonsHeight * 2.5f).toInt() - ) - - OverlayInput.R -> Rect( - triggersRLeft, - triggersTop, - triggersRLeft + rectangleButtonsWidth, - triggersTop + rectangleButtonsHeight - ) - - OverlayInput.ZR -> Rect( - triggersRLeft, - (triggersTop + rectangleButtonsHeight * 1.5f).toInt(), - triggersRLeft + rectangleButtonsWidth, - (triggersTop + rectangleButtonsHeight * 2.5f).toInt() - ) - - OverlayInput.C, OverlayInput.HOME, OverlayInput.Z, OverlayInput.NUN_CHUCK_AXIS -> Rect() - OverlayInput.DPAD -> Rect( - dpadCentreX - dpadRadius, - dpadCentreY - dpadRadius, - dpadCentreX + dpadRadius, - dpadCentreY + dpadRadius - ) - - OverlayInput.LEFT_AXIS -> Rect( - leftJoystickCentreX - joystickRadius, - joystickCentreY - joystickRadius, - leftJoystickCentreX + joystickRadius, - joystickCentreY + joystickRadius - ) - - OverlayInput.RIGHT_AXIS -> Rect( - rightJoystickCentreX - joystickRadius, - joystickCentreY - joystickRadius, - rightJoystickCentreX + joystickRadius, - joystickCentreY + joystickRadius - ) - - OverlayInput.L_STICK -> Rect( - (leftJoystickCentreX + joystickRadius * 1.5f - joystickClickRadius).toInt(), - (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius).toInt(), - (leftJoystickCentreX + joystickRadius * 1.5f + joystickClickRadius).toInt(), - (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius).toInt() - ) - - OverlayInput.R_STICK -> Rect( - (rightJoystickCentreX - joystickRadius * 1.5f - joystickClickRadius).toInt(), - (joystickCentreY + joystickRadius * 1.5f - joystickClickRadius).toInt(), - (rightJoystickCentreX - joystickRadius * 1.5f + joystickClickRadius).toInt(), - (joystickCentreY + joystickRadius * 1.5f + joystickClickRadius).toInt() - ) - } - } - - -// class OverlaySettings internal constructor( -// var isVibrateOnTouchEnabled: Boolean, -// var isOverlayEnabled: Boolean, -// controllerIndex: Int, -// alpha: Int, -// private val overlaySettingsConsumer: (OverlaySettings) -> Unit -// ) { -// var controllerIndex: Int = 0 -// set(controllerIndex) { -// require(controllerIndex in 0.. Unit - ) { - fun saveSettings() { - inputOverlaySettingsConsumer(this) - } - } - - -} diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt index b3cd669a..a45ca939 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/InputOverlaySurfaceView.kt @@ -2,6 +2,7 @@ package info.cemu.cemu.inputoverlay import android.content.Context import android.graphics.Canvas +import android.graphics.Rect import android.os.Build import android.os.VibrationEffect import android.os.Vibrator @@ -12,8 +13,8 @@ import android.view.MotionEvent import android.view.SurfaceView import android.view.View import android.view.View.OnTouchListener +import androidx.annotation.DrawableRes import info.cemu.cemu.R -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings import info.cemu.cemu.nativeinterface.NativeInput import info.cemu.cemu.nativeinterface.NativeInput.getControllerType import info.cemu.cemu.nativeinterface.NativeInput.isControllerDisabled @@ -29,18 +30,39 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } private var inputMode = InputMode.DEFAULT + private var pixelDensity = 1 + private var currentAlpha = 255 + private var currentConfiguredInput: Input? = null + private var nativeControllerType = -1 + var controllerIndex: Int = 0 + + private var inputs: MutableList>? = null + private val settingsProvider: InputOverlaySettingsManager + private val vibrator: Vibrator? + private val buttonTouchVibrationEffect = + VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK) + private var vibrateOnTouch: Boolean = false + private var inputsMinWidthHeight: Int = -1 + + init { + pixelDensity = context.resources.displayMetrics.densityDpi + inputsMinWidthHeight = + Math.round(INPUTS_MIN_WIDTH_HEIGHT_DP * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) + vibrator = getVibrator(context) + setOnTouchListener(this) + settingsProvider = InputOverlaySettingsManager(context) + val overlaySettings = settingsProvider.overlaySettings + controllerIndex = overlaySettings.controllerIndex + currentAlpha = overlaySettings.alpha + vibrateOnTouch = vibrator.hasVibrator() && overlaySettings.isVibrateOnTouchEnabled + } fun resetInputs() { if (inputs == null) { return } - val width = width - val height = height - - for (input in InputOverlaySettingsProvider.OverlayInput.entries) { - val inputSettings = settingsProvider.getOverlaySettingsForInput(input, width, height) - inputSettings.rect = settingsProvider.getDefaultRectangle(input, width, height) - inputSettings.saveSettings() + for (input in OverlayInputList) { + settingsProvider.clearSavedRectangle(input) } inputs!!.clear() inputs = null @@ -56,9 +78,9 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : if (this.inputMode != InputMode.DEFAULT) { return } - for (input in inputs!!) { + for ((overlayInput, input) in inputs!!) { input.reset() - input.saveConfiguration() + settingsProvider.saveRectangle(overlayInput, input.getBoundingRectangle()) } } @@ -66,46 +88,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : return inputMode } - enum class OverlayButton { - A, - B, - ONE, - TWO, - C, - Z, - HOME, - DPAD_DOWN, - DPAD_LEFT, - DPAD_RIGHT, - DPAD_UP, - L, - L_STICK, - MINUS, - PLUS, - R, - R_STICK, - X, - Y, - ZL, - ZR, - } - - enum class OverlayJoystick { - LEFT, - RIGHT, - } - - private var nativeControllerType = -1 - var controllerIndex: Int = 0 - - private var inputs: MutableList? = null - private val settingsProvider: InputOverlaySettingsProvider - private val vibrator: Vibrator? - private val buttonTouchVibrationEffect = - VibrationEffect.createPredefined(VibrationEffect.EFFECT_TICK) - private var vibrateOnTouch: Boolean = false - private var inputsMinWidthHeight: Int = 24 - private fun getVibrator(context: Context): Vibrator { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { val vibratorManager = @@ -116,7 +98,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : return context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator } - fun overlayButtonToVPADButton(button: OverlayButton): Int { + private fun overlayButtonToVPADButton(button: OverlayInput): Int { return when (button) { OverlayButton.A -> NativeInput.VPAD_BUTTON_A OverlayButton.B -> NativeInput.VPAD_BUTTON_B @@ -124,12 +106,12 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : OverlayButton.Y -> NativeInput.VPAD_BUTTON_Y OverlayButton.PLUS -> NativeInput.VPAD_BUTTON_PLUS OverlayButton.MINUS -> NativeInput.VPAD_BUTTON_MINUS - OverlayButton.DPAD_UP -> NativeInput.VPAD_BUTTON_UP - OverlayButton.DPAD_DOWN -> NativeInput.VPAD_BUTTON_DOWN - OverlayButton.DPAD_LEFT -> NativeInput.VPAD_BUTTON_LEFT - OverlayButton.DPAD_RIGHT -> NativeInput.VPAD_BUTTON_RIGHT - OverlayButton.L_STICK -> NativeInput.VPAD_BUTTON_STICKL - OverlayButton.R_STICK -> NativeInput.VPAD_BUTTON_STICKR + OverlayDpad.DPAD_UP -> NativeInput.VPAD_BUTTON_UP + OverlayDpad.DPAD_DOWN -> NativeInput.VPAD_BUTTON_DOWN + OverlayDpad.DPAD_LEFT -> NativeInput.VPAD_BUTTON_LEFT + OverlayDpad.DPAD_RIGHT -> NativeInput.VPAD_BUTTON_RIGHT + OverlayButton.L_STICK_CLICK -> NativeInput.VPAD_BUTTON_STICKL + OverlayButton.R_STICK_CLICK -> NativeInput.VPAD_BUTTON_STICKR OverlayButton.L -> NativeInput.VPAD_BUTTON_L OverlayButton.R -> NativeInput.VPAD_BUTTON_R OverlayButton.ZR -> NativeInput.VPAD_BUTTON_ZR @@ -138,7 +120,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun overlayButtonToClassicButton(button: OverlayButton): Int { + private fun overlayButtonToClassicButton(button: OverlayInput): Int { return when (button) { OverlayButton.A -> NativeInput.CLASSIC_BUTTON_A OverlayButton.B -> NativeInput.CLASSIC_BUTTON_B @@ -146,10 +128,10 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : OverlayButton.Y -> NativeInput.CLASSIC_BUTTON_Y OverlayButton.PLUS -> NativeInput.CLASSIC_BUTTON_PLUS OverlayButton.MINUS -> NativeInput.CLASSIC_BUTTON_MINUS - OverlayButton.DPAD_UP -> NativeInput.CLASSIC_BUTTON_UP - OverlayButton.DPAD_DOWN -> NativeInput.CLASSIC_BUTTON_DOWN - OverlayButton.DPAD_LEFT -> NativeInput.CLASSIC_BUTTON_LEFT - OverlayButton.DPAD_RIGHT -> NativeInput.CLASSIC_BUTTON_RIGHT + OverlayDpad.DPAD_UP -> NativeInput.CLASSIC_BUTTON_UP + OverlayDpad.DPAD_DOWN -> NativeInput.CLASSIC_BUTTON_DOWN + OverlayDpad.DPAD_LEFT -> NativeInput.CLASSIC_BUTTON_LEFT + OverlayDpad.DPAD_RIGHT -> NativeInput.CLASSIC_BUTTON_RIGHT OverlayButton.L -> NativeInput.CLASSIC_BUTTON_L OverlayButton.R -> NativeInput.CLASSIC_BUTTON_R OverlayButton.ZR -> NativeInput.CLASSIC_BUTTON_ZR @@ -158,7 +140,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun overlayButtonToProButton(button: OverlayButton): Int { + private fun overlayButtonToProButton(button: OverlayInput): Int { return when (button) { OverlayButton.A -> NativeInput.PRO_BUTTON_A OverlayButton.B -> NativeInput.PRO_BUTTON_B @@ -166,12 +148,12 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : OverlayButton.Y -> NativeInput.PRO_BUTTON_Y OverlayButton.PLUS -> NativeInput.PRO_BUTTON_PLUS OverlayButton.MINUS -> NativeInput.PRO_BUTTON_MINUS - OverlayButton.DPAD_UP -> NativeInput.PRO_BUTTON_UP - OverlayButton.DPAD_DOWN -> NativeInput.PRO_BUTTON_DOWN - OverlayButton.DPAD_LEFT -> NativeInput.PRO_BUTTON_LEFT - OverlayButton.DPAD_RIGHT -> NativeInput.PRO_BUTTON_RIGHT - OverlayButton.L_STICK -> NativeInput.PRO_BUTTON_STICKL - OverlayButton.R_STICK -> NativeInput.PRO_BUTTON_STICKR + OverlayDpad.DPAD_UP -> NativeInput.PRO_BUTTON_UP + OverlayDpad.DPAD_DOWN -> NativeInput.PRO_BUTTON_DOWN + OverlayDpad.DPAD_LEFT -> NativeInput.PRO_BUTTON_LEFT + OverlayDpad.DPAD_RIGHT -> NativeInput.PRO_BUTTON_RIGHT + OverlayButton.L_STICK_CLICK -> NativeInput.PRO_BUTTON_STICKL + OverlayButton.R_STICK_CLICK -> NativeInput.PRO_BUTTON_STICKR OverlayButton.L -> NativeInput.PRO_BUTTON_L OverlayButton.R -> NativeInput.PRO_BUTTON_R OverlayButton.ZR -> NativeInput.PRO_BUTTON_ZR @@ -180,7 +162,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun overlayButtonToWiimoteButton(button: OverlayButton): Int { + private fun overlayButtonToWiimoteButton(button: OverlayInput): Int { return when (button) { OverlayButton.A -> NativeInput.WIIMOTE_BUTTON_A OverlayButton.B -> NativeInput.WIIMOTE_BUTTON_B @@ -189,20 +171,17 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : OverlayButton.PLUS -> NativeInput.WIIMOTE_BUTTON_PLUS OverlayButton.MINUS -> NativeInput.WIIMOTE_BUTTON_MINUS OverlayButton.HOME -> NativeInput.WIIMOTE_BUTTON_HOME - OverlayButton.DPAD_UP -> NativeInput.WIIMOTE_BUTTON_UP - OverlayButton.DPAD_DOWN -> NativeInput.WIIMOTE_BUTTON_DOWN - OverlayButton.DPAD_LEFT -> NativeInput.WIIMOTE_BUTTON_LEFT - OverlayButton.DPAD_RIGHT -> NativeInput.WIIMOTE_BUTTON_RIGHT + OverlayDpad.DPAD_UP -> NativeInput.WIIMOTE_BUTTON_UP + OverlayDpad.DPAD_DOWN -> NativeInput.WIIMOTE_BUTTON_DOWN + OverlayDpad.DPAD_LEFT -> NativeInput.WIIMOTE_BUTTON_LEFT + OverlayDpad.DPAD_RIGHT -> NativeInput.WIIMOTE_BUTTON_RIGHT OverlayButton.C -> NativeInput.WIIMOTE_BUTTON_NUNCHUCK_C OverlayButton.Z -> NativeInput.WIIMOTE_BUTTON_NUNCHUCK_Z else -> -1 } } - fun onButtonStateChange(button: OverlayButton?, state: Boolean) { - if (button == null) { - return - } + private fun onButtonStateChange(button: OverlayInput, state: Boolean) { val nativeButtonId = when (nativeControllerType) { NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> overlayButtonToVPADButton(button) NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC -> overlayButtonToClassicButton(button) @@ -219,23 +198,23 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : onOverlayButton(controllerIndex, nativeButtonId, state) } - fun onOverlayAxis(axis: Int, value: Float) { + private fun onOverlayAxis(axis: Int, value: Float) { onOverlayAxis(controllerIndex, axis, value) } - fun onVPADJoystickStateChange( - joystick: OverlayJoystick, + private fun onVPADJoystickStateChange( + joystick: OverlayInput, up: Float, down: Float, left: Float, - right: Float + right: Float, ) { if (joystick == OverlayJoystick.LEFT) { onOverlayAxis(NativeInput.VPAD_BUTTON_STICKL_UP, up) onOverlayAxis(NativeInput.VPAD_BUTTON_STICKL_DOWN, down) onOverlayAxis(NativeInput.VPAD_BUTTON_STICKL_LEFT, left) onOverlayAxis(NativeInput.VPAD_BUTTON_STICKL_RIGHT, right) - } else { + } else if (joystick == OverlayJoystick.RIGHT) { onOverlayAxis(NativeInput.VPAD_BUTTON_STICKR_UP, up) onOverlayAxis(NativeInput.VPAD_BUTTON_STICKR_DOWN, down) onOverlayAxis(NativeInput.VPAD_BUTTON_STICKR_LEFT, left) @@ -243,19 +222,19 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun onProJoystickStateChange( - joystick: OverlayJoystick, + private fun onProJoystickStateChange( + joystick: OverlayInput, up: Float, down: Float, left: Float, - right: Float + right: Float, ) { if (joystick == OverlayJoystick.LEFT) { onOverlayAxis(NativeInput.PRO_BUTTON_STICKL_UP, up) onOverlayAxis(NativeInput.PRO_BUTTON_STICKL_DOWN, down) onOverlayAxis(NativeInput.PRO_BUTTON_STICKL_LEFT, left) onOverlayAxis(NativeInput.PRO_BUTTON_STICKL_RIGHT, right) - } else { + } else if (joystick == OverlayJoystick.RIGHT) { onOverlayAxis(NativeInput.PRO_BUTTON_STICKR_UP, up) onOverlayAxis(NativeInput.PRO_BUTTON_STICKR_DOWN, down) onOverlayAxis(NativeInput.PRO_BUTTON_STICKR_LEFT, left) @@ -263,19 +242,19 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun onClassicJoystickStateChange( - joystick: OverlayJoystick, + private fun onClassicJoystickStateChange( + joystick: OverlayInput, up: Float, down: Float, left: Float, - right: Float + right: Float, ) { if (joystick == OverlayJoystick.LEFT) { onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKL_UP, up) onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKL_DOWN, down) onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKL_LEFT, left) onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKL_RIGHT, right) - } else { + } else if (joystick == OverlayJoystick.RIGHT) { onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKR_UP, up) onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKR_DOWN, down) onOverlayAxis(NativeInput.CLASSIC_BUTTON_STICKR_LEFT, left) @@ -283,14 +262,14 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun onWiimoteJoystickStateChange( - joystick: OverlayJoystick, + private fun onWiimoteJoystickStateChange( + joystick: OverlayInput, up: Float, down: Float, left: Float, - right: Float + right: Float, ) { - if (joystick == OverlayJoystick.LEFT) { + if (joystick == OverlayJoystick.RIGHT) { onOverlayAxis(NativeInput.WIIMOTE_BUTTON_NUNCHUCK_UP, up) onOverlayAxis(NativeInput.WIIMOTE_BUTTON_NUNCHUCK_DOWN, down) onOverlayAxis(NativeInput.WIIMOTE_BUTTON_NUNCHUCK_LEFT, left) @@ -298,9 +277,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } } - fun onJoystickStateChange(joystick: OverlayJoystick, x: Float, y: Float) { - val (up, down) = if (y < 0) Pair(-y, 0f) else Pair(0f, y) - val (left, right) = if (x < 0) Pair(-x, 0f) else Pair(0f, x) + private fun onJoystickStateChange(joystick: OverlayInput, x: Float, y: Float) { val onJoystickChange = when (nativeControllerType) { NativeInput.EMULATED_CONTROLLER_TYPE_VPAD -> ::onVPADJoystickStateChange NativeInput.EMULATED_CONTROLLER_TYPE_PRO -> ::onProJoystickStateChange @@ -308,15 +285,76 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE -> ::onWiimoteJoystickStateChange else -> return } + val (up, down) = if (y < 0) Pair(-y, 0f) else Pair(0f, y) + val (left, right) = if (x < 0) Pair(-x, 0f) else Pair(0f, x) onJoystickChange(joystick, up, down, left, right) } - private fun getOverlaySettingsForInput(input: InputOverlaySettingsProvider.OverlayInput): InputOverlaySettings { - return settingsProvider.getOverlaySettingsForInput(input, width, height) + private fun getBoundingRectangleForInput(input: OverlayInput): Rect { + return settingsProvider.getInputOverlayRectangle(input, width, height, pixelDensity) + } + + + private fun MutableList>.addRoundButton( + button: OverlayButton, + @DrawableRes buttonDrawable: Int, + ) { + add( + button to RoundButton( + resources, + buttonDrawable, + { onButtonStateChange(button, it) }, + currentAlpha, + getBoundingRectangleForInput(button), + ) + ) + } + + private fun MutableList>.addJoystick( + joystick: OverlayJoystick, + @DrawableRes joystickInnerCircleDrawable: Int = R.drawable.stick_inner, + @DrawableRes joystickBackgroundDrawable: Int = R.drawable.stick_background, + ) { + add( + joystick to Joystick( + resources, + joystickBackgroundDrawable, + joystickInnerCircleDrawable, + { x, y -> onJoystickStateChange(joystick, x, y) }, + currentAlpha, + getBoundingRectangleForInput(joystick) + ) + ) + } + + private fun MutableList>.addDpad() { + add( + OverlayDpad.DPAD_UP to DPadInput( + resources, + R.drawable.dpad_background, + R.drawable.dpad_button, + ::onButtonStateChange, + currentAlpha, + getBoundingRectangleForInput(OverlayDpad.DPAD_UP) + ) + ) + } + + private fun MutableList>.addRectangleButton( + button: OverlayButton, + @DrawableRes buttonDrawable: Int, + ) { + add( + button to RectangleButton( + resources, + buttonDrawable, + { onButtonStateChange(button, it) }, + currentAlpha, + getBoundingRectangleForInput(button) + ) + ) } - private fun MutableList.addAll(vararg inputs: Input): Unit = - inputs.forEach { add(it) } private fun setInputs() { if (inputs != null) { @@ -326,150 +364,86 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : inputs = mutableListOf() return } - val resources = resources + nativeControllerType = getControllerType(controllerIndex) - inputs = mutableListOf( - RoundButton( - resources, - R.drawable.button_minus, - ::onButtonStateChange, + inputs = mutableListOf>().apply { + addRoundButton( OverlayButton.MINUS, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.MINUS) - ), - RoundButton( - resources, - R.drawable.button_plus, - ::onButtonStateChange, - OverlayButton.PLUS, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.PLUS) - ), - DPadInput( - resources, - R.drawable.dpad_background, - R.drawable.dpad_button, - ::onButtonStateChange, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.DPAD) - ), - Joystick( - resources, - R.drawable.stick_background, - R.drawable.stick_inner, - ::onJoystickStateChange, - OverlayJoystick.LEFT, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.LEFT_AXIS) - ), - RoundButton( - resources, - R.drawable.button_a, - ::onButtonStateChange, - OverlayButton.A, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.A) - ), - RoundButton( - resources, - R.drawable.button_b, - ::onButtonStateChange, - OverlayButton.B, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.B) + R.drawable.button_minus, ) - ).apply { + addRoundButton( + OverlayButton.PLUS, + R.drawable.button_plus, + ) + addDpad() + addRoundButton( + OverlayButton.A, + R.drawable.button_a, + ) + addRoundButton( + OverlayButton.B, + R.drawable.button_b, + ) + addJoystick(OverlayJoystick.RIGHT) if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) { - addAll( - RoundButton( - resources, - R.drawable.button_x, - ::onButtonStateChange, - OverlayButton.X, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.X) - ), - RoundButton( - resources, - R.drawable.button_y, - ::onButtonStateChange, - OverlayButton.Y, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.Y) - ), - RectangleButton( - resources, - R.drawable.button_zl, - ::onButtonStateChange, - OverlayButton.ZL, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.ZL) - ), - RectangleButton( - resources, - R.drawable.button_l, - ::onButtonStateChange, - OverlayButton.L, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.L) - ), - RectangleButton( - resources, - R.drawable.button_zr, - ::onButtonStateChange, - OverlayButton.ZR, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.ZR) - ), - RectangleButton( - resources, - R.drawable.button_r, - ::onButtonStateChange, - OverlayButton.R, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.R) - ), - Joystick( - resources, - R.drawable.stick_background, - R.drawable.stick_inner, - ::onJoystickStateChange, - OverlayJoystick.RIGHT, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.RIGHT_AXIS) - ) + addRoundButton( + OverlayButton.X, + R.drawable.button_x, ) + addRoundButton( + OverlayButton.Y, + R.drawable.button_y, + ) + addRectangleButton( + OverlayButton.ZL, + R.drawable.button_zl, + ) + addRectangleButton( + OverlayButton.ZR, + R.drawable.button_zr, + ) + addRectangleButton( + OverlayButton.L, + R.drawable.button_l, + ) + addRectangleButton( + OverlayButton.R, + R.drawable.button_r, + ) + addJoystick(OverlayJoystick.LEFT) } if (nativeControllerType == NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE) { - addAll( - RoundButton( - resources, - R.drawable.button_c, - ::onButtonStateChange, - OverlayButton.C, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.C) - ), - RoundButton( - resources, - R.drawable.button_z, - ::onButtonStateChange, - OverlayButton.Z, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.Z) - ), - RoundButton( - resources, - R.drawable.button_home, - ::onButtonStateChange, - OverlayButton.HOME, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.HOME) - ) + addRoundButton( + OverlayButton.ONE, + R.drawable.button_one, + ) + addRoundButton( + OverlayButton.TWO, + R.drawable.button_two, + ) + addRoundButton( + OverlayButton.C, + R.drawable.button_c, + ) + addRectangleButton( + OverlayButton.Z, + R.drawable.button_z, + ) + addRoundButton( + OverlayButton.HOME, + R.drawable.button_home, ) } if (nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_CLASSIC && nativeControllerType != NativeInput.EMULATED_CONTROLLER_TYPE_WIIMOTE ) { - addAll( - RoundButton( - resources, - R.drawable.button_stick, - ::onButtonStateChange, - OverlayButton.L_STICK, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.L_STICK) - ), - RoundButton( - resources, - R.drawable.button_stick, - ::onButtonStateChange, - OverlayButton.R_STICK, - getOverlaySettingsForInput(InputOverlaySettingsProvider.OverlayInput.R_STICK) - ) + addRoundButton( + OverlayButton.L_STICK_CLICK, + R.drawable.button_stick, + ) + addRoundButton( + OverlayButton.R_STICK_CLICK, + R.drawable.button_stick, ) } } @@ -484,30 +458,18 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : override fun draw(canvas: Canvas) { super.draw(canvas) - for (input in inputs!!) { + for ((_, input) in inputs!!) { input.draw(canvas) } } - var currentConfiguredInput: Input? = null - - init { - inputsMinWidthHeight = - Math.round(INPUTS_MIN_WIDTH_HEIGHT_DP * (context.resources.displayMetrics.densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT)) - vibrator = getVibrator(context) - setOnTouchListener(this) - settingsProvider = InputOverlaySettingsProvider(context) - val overlaySettings = settingsProvider.overlaySettings - controllerIndex = overlaySettings.controllerIndex - vibrateOnTouch = vibrator.hasVibrator() && overlaySettings.isVibrateOnTouchEnabled - } private fun onEditPosition(event: MotionEvent): Boolean { when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { val x = event.x.toInt() val y = event.y.toInt() - for (input in inputs!!) { + for ((_, input) in inputs!!) { if (input.isInside(x, y)) { currentConfiguredInput = input input.enableDrawingBoundingRect( @@ -546,7 +508,7 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : MotionEvent.ACTION_DOWN -> { val x = event.x.toInt() val y = event.y.toInt() - for (input in inputs!!) { + for ((_, input) in inputs!!) { if (input.isInside(x, y)) { currentConfiguredInput = input input.enableDrawingBoundingRect( @@ -593,16 +555,22 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : override fun onTouch(v: View, event: MotionEvent): Boolean { var touchEventProcessed = false - if (inputMode == InputMode.DEFAULT) { - for (input in inputs!!) { - if (input.onTouch(event)) { - touchEventProcessed = true + when (inputMode) { + InputMode.DEFAULT -> { + for ((_, input) in inputs!!) { + if (input.onTouch(event)) { + touchEventProcessed = true + } } } - } else if (inputMode == InputMode.EDIT_POSITION) { - touchEventProcessed = onEditPosition(event) - } else if (inputMode == InputMode.EDIT_SIZE) { - touchEventProcessed = onEditSize(event) + + InputMode.EDIT_POSITION -> { + touchEventProcessed = onEditPosition(event) + } + + InputMode.EDIT_SIZE -> { + touchEventProcessed = onEditSize(event) + } } if (touchEventProcessed) { @@ -613,6 +581,6 @@ class InputOverlaySurfaceView(context: Context, attrs: AttributeSet?) : } companion object { - private const val INPUTS_MIN_WIDTH_HEIGHT_DP = 40 + private const val INPUTS_MIN_WIDTH_HEIGHT_DP = 20 } } diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt index c41e5812..8d673943 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/Joystick.kt @@ -2,13 +2,11 @@ package info.cemu.cemu.inputoverlay import android.content.res.Resources import android.graphics.Canvas +import android.graphics.Rect import android.graphics.drawable.Drawable import android.view.MotionEvent import androidx.annotation.DrawableRes import androidx.core.content.res.ResourcesCompat -import info.cemu.cemu.drawable.getInvertedDrawable -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings -import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView.OverlayJoystick import kotlin.math.min import kotlin.math.sqrt @@ -16,17 +14,13 @@ class Joystick( resources: Resources, @DrawableRes joystickBackgroundId: Int, @DrawableRes innerStickId: Int, - stickStateChangeListener: StickStateChangeListener, - joystick: OverlayJoystick, - settings: InputOverlaySettings -) : - Input(settings) { - private val iconPressed: Drawable - private val iconNotPressed: Drawable = - ResourcesCompat.getDrawable(resources, innerStickId, null)!! + private val onStickStateChange: (x: Float, y: Float) -> Unit, + private val alpha: Int, + boundingRectangle: Rect, +) : Input(boundingRectangle) { private val joystickBackground: Drawable = ResourcesCompat.getDrawable(resources, joystickBackgroundId, null)!! - private var icon: Drawable + private val joystickDrawable = InputDrawable(resources, innerStickId) private var currentPointerId = -1 private var centerX = 0 private var centerY = 0 @@ -35,31 +29,16 @@ class Joystick( private var radius = 0 private var innerRadius = 0 - fun interface StickStateChangeListener { - fun onStickStateChange(joystick: OverlayJoystick, x: Float, y: Float) - } - - private val stickStateChangeListener: StickStateChangeListener - private val joystick: OverlayJoystick - init { - iconPressed = iconNotPressed.getInvertedDrawable(resources) - icon = iconNotPressed - this.stickStateChangeListener = stickStateChangeListener - this.joystick = joystick configure() } private fun updateState(pressed: Boolean, x: Float, y: Float) { - icon = if (pressed) { - iconPressed - } else { - iconNotPressed - } - stickStateChangeListener.onStickStateChange(joystick, x, y) + joystickDrawable.setActiveState(pressed) + onStickStateChange(x, y) val newCentreX = (centerX + radius * x).toInt() val newCentreY = (centerY + radius * y).toInt() - iconPressed.setBounds( + joystickDrawable.setBounds( newCentreX - innerRadius, newCentreY - innerRadius, newCentreX + innerRadius, @@ -68,33 +47,21 @@ class Joystick( } override fun configure() { - val joystickBounds = settings.rect - this.centerX = joystickBounds.centerX() - this.originalCenterX = this.centerX - this.centerY = joystickBounds.centerY() - this.originalCenterY = this.centerY - this.radius = - (min( - joystickBounds.width().toDouble(), - joystickBounds.height().toDouble() - ) / 2).toInt() - this.innerRadius = (radius * 0.65f).toInt() + centerX = rect.centerX() + originalCenterX = centerX + centerY = rect.centerY() + originalCenterY = centerY + radius = min(rect.width(), rect.height()) / 2 + innerRadius = (radius * 0.65f).toInt() joystickBackground.setBounds( centerX - radius, centerY - radius, centerX + radius, centerY + radius ) - joystickBackground.alpha = settings.alpha - iconPressed.alpha = settings.alpha - iconPressed.setBounds( - centerX - innerRadius, - centerY - innerRadius, - centerX + innerRadius, - centerY + innerRadius - ) - iconNotPressed.alpha = settings.alpha - iconNotPressed.setBounds( + joystickBackground.alpha = alpha + joystickDrawable.setAlpha(alpha) + joystickDrawable.setBounds( centerX - innerRadius, centerY - innerRadius, centerX + innerRadius, @@ -103,7 +70,6 @@ class Joystick( } override fun onTouch(event: MotionEvent): Boolean { - var stateUpdated = false when (event.actionMasked) { MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> { val pointerIndex = event.actionIndex @@ -120,7 +86,7 @@ class Joystick( ) currentPointerId = event.getPointerId(pointerIndex) updateState(true, 0.0f, 0.0f) - stateUpdated = true + return true } } @@ -136,7 +102,7 @@ class Joystick( ) currentPointerId = -1 updateState(false, 0.0f, 0.0f) - stateUpdated = true + return true } } @@ -156,12 +122,11 @@ class Joystick( y /= norm } updateState(true, x, y) - stateUpdated = true - break + return true } } } - return stateUpdated + return false } override fun resetInput() { @@ -170,7 +135,7 @@ class Joystick( override fun drawInput(canvas: Canvas) { joystickBackground.draw(canvas) - icon.draw(canvas) + joystickDrawable.icon.draw(canvas) } override fun isInside(x: Int, y: Int): Boolean { diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/OverlayInput.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/OverlayInput.kt new file mode 100644 index 00000000..c684ca11 --- /dev/null +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/OverlayInput.kt @@ -0,0 +1,46 @@ +package info.cemu.cemu.inputoverlay + +sealed interface OverlayInput { + val configName: String +} + +enum class OverlayJoystick : OverlayInput { + LEFT, + RIGHT; + + override val configName = "AXIS_$name" +} + +enum class OverlayButton : OverlayInput { + A, + B, + ONE, + TWO, + C, + Z, + HOME, + L, + L_STICK_CLICK, + MINUS, + PLUS, + R, + R_STICK_CLICK, + X, + Y, + ZL, + ZR; + + override val configName = "BUTTON_$name" +} + +enum class OverlayDpad : OverlayInput { + DPAD_DOWN, + DPAD_LEFT, + DPAD_RIGHT, + DPAD_UP; + + override val configName = "DPAD" +} + +val OverlayInputList: List = + OverlayButton.entries + OverlayJoystick.entries + OverlayDpad.entries \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt index 40e31fd8..39de3c3b 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RectangleButton.kt @@ -1,18 +1,16 @@ package info.cemu.cemu.inputoverlay import android.content.res.Resources +import android.graphics.Rect import androidx.annotation.DrawableRes -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings -import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView.OverlayButton class RectangleButton( resources: Resources, @DrawableRes buttonId: Int, - buttonStateChangeListener: ButtonStateChangeListener, - overlayButton: OverlayButton, - settings: InputOverlaySettings -) : - Button(resources, buttonId, buttonStateChangeListener, overlayButton, settings) { + onButtonStateChange: (state: Boolean) -> Unit, + private val alpha: Int, + rect: Rect, +) : Button(resources, buttonId, onButtonStateChange, rect) { var left: Int = 0 var top: Int = 0 var right: Int = 0 @@ -23,13 +21,12 @@ class RectangleButton( } override fun configure() { - val rect = settings.rect this.left = rect.left this.top = rect.top this.right = rect.right this.bottom = rect.bottom - iconPressed.setBounds(left, top, right, bottom) - iconNotPressed.setBounds(left, top, right, bottom) + inputDrawable.setAlpha(alpha) + inputDrawable.setBounds(left, top, right, bottom) } override fun isInside(x: Int, y: Int): Boolean { diff --git a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RoundButton.kt b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RoundButton.kt index 49937433..05ef1c9d 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RoundButton.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/inputoverlay/RoundButton.kt @@ -3,42 +3,38 @@ package info.cemu.cemu.inputoverlay import android.content.res.Resources import android.graphics.Rect import androidx.annotation.DrawableRes -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider.InputOverlaySettings -import info.cemu.cemu.inputoverlay.InputOverlaySurfaceView.OverlayButton import kotlin.math.min class RoundButton internal constructor( resources: Resources, @DrawableRes buttonId: Int, - buttonStateChangeListener: ButtonStateChangeListener, - overlayButton: OverlayButton, - settings: InputOverlaySettings -) : - Button(resources, buttonId, buttonStateChangeListener, overlayButton, settings) { - var centreX: Int = 0 - var centreY: Int = 0 - var radius: Int = 0 + onButtonStateChange: (state: Boolean) -> Unit, + private val alpha: Int, + rect: Rect, +) : Button(resources, buttonId, onButtonStateChange, rect) { + private var centreX: Int = 0 + private var centreY: Int = 0 + private var radius2: Int = 0 init { configure() } override fun configure() { - val rect = settings.rect centreX = rect.centerX() centreY = rect.centerY() - radius = (min(rect.width().toDouble(), rect.height().toDouble()) / 2).toInt() - val iconRect = Rect( + val radius = min(rect.width(), rect.height()) / 2 + inputDrawable.setBounds( centreX - radius, centreY - radius, centreX + radius, centreY + radius ) - iconPressed.bounds = iconRect - iconNotPressed.bounds = iconRect + inputDrawable.setAlpha(alpha) + radius2 = radius * radius } override fun isInside(x: Int, y: Int): Boolean { - return ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) <= radius * radius + return ((x - centreX) * (x - centreX) + (y - centreY) * (y - centreY)) <= radius2 } } diff --git a/src/android/app/src/main/java/info/cemu/cemu/settings/inputoverlay/InputOverlaySettingsViewModel.kt b/src/android/app/src/main/java/info/cemu/cemu/settings/inputoverlay/InputOverlaySettingsViewModel.kt index fecd1f47..ab296f3f 100644 --- a/src/android/app/src/main/java/info/cemu/cemu/settings/inputoverlay/InputOverlaySettingsViewModel.kt +++ b/src/android/app/src/main/java/info/cemu/cemu/settings/inputoverlay/InputOverlaySettingsViewModel.kt @@ -5,23 +5,23 @@ import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory import info.cemu.cemu.CemuApplication -import info.cemu.cemu.inputoverlay.InputOverlaySettingsProvider +import info.cemu.cemu.inputoverlay.InputOverlaySettingsManager class InputOverlaySettingsViewModel( - private val inputOverlaySettingsProvider: InputOverlaySettingsProvider + private val inputOverlaySettingsManager: InputOverlaySettingsManager ) : ViewModel() { - val overlaySettings = inputOverlaySettingsProvider.overlaySettings + val overlaySettings = inputOverlaySettingsManager.overlaySettings override fun onCleared() { super.onCleared() - inputOverlaySettingsProvider.overlaySettings = overlaySettings + inputOverlaySettingsManager.overlaySettings = overlaySettings } companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { InputOverlaySettingsViewModel( - InputOverlaySettingsProvider(CemuApplication.Application) + InputOverlaySettingsManager(CemuApplication.Application) ) } } diff --git a/src/android/app/src/main/res/drawable/button_z.xml b/src/android/app/src/main/res/drawable/button_z.xml index 003eab95..8367da11 100644 --- a/src/android/app/src/main/res/drawable/button_z.xml +++ b/src/android/app/src/main/res/drawable/button_z.xml @@ -1,16 +1,15 @@ + android:width="94.033dp" + android:height="53.792dp" + android:viewportWidth="94.033" + android:viewportHeight="53.792"> + android:strokeColor="#ffffff"/> + android:pathData="m34.113,42.944l25.806,0l0,-3.33l-22.198,0l0.555,1.156 21.042,-27.517L59.318,10.849L34.761,10.849L34.761,14.178l20.903,0l-0.555,-1.156 -20.996,27.47z" + android:strokeWidth="1.196" + android:fillColor="#ffffff"/> diff --git a/src/android/app/src/main/res/values/refs.xml b/src/android/app/src/main/res/values/refs.xml index a6b3daec..3a090684 100644 --- a/src/android/app/src/main/res/values/refs.xml +++ b/src/android/app/src/main/res/values/refs.xml @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/src/android/app/src/main/res/xml/input_overlay_default_configs.xml b/src/android/app/src/main/res/xml/input_overlay_default_configs.xml new file mode 100644 index 00000000..d01370b5 --- /dev/null +++ b/src/android/app/src/main/res/xml/input_overlay_default_configs.xml @@ -0,0 +1,156 @@ + + + + BUTTON_PLUS + true + true + 272 + 24 + 48 + + + BUTTON_MINUS + true + 272 + 24 + 48 + + + BUTTON_HOME + true + true + 208 + 80 + 48 + + + BUTTON_L + 48 + 8 + 72 + 36 + + + BUTTON_ZL + 48 + 64 + 72 + 36 + + + BUTTON_R + true + 48 + 8 + 72 + 36 + + + BUTTON_ZR + true + 48 + 64 + 72 + 36 + + + BUTTON_C + true + 60 + 8 + 48 + + + BUTTON_Z + true + 48 + 64 + 72 + 36 + + + BUTTON_A + true + true + 32 + 56 + 48 + + + BUTTON_Y + true + true + 128 + 56 + 48 + + + BUTTON_X + true + true + 80 + 104 + 48 + + + BUTTON_B + true + true + 80 + 8 + 48 + + + BUTTON_ONE + true + true + 128 + 56 + 48 + + + BUTTON_TWO + true + true + 80 + 104 + 48 + + + BUTTON_R_STICK_CLICK + true + true + 208 + 80 + 48 + + + BUTTON_L_STICK_CLICK + true + 208 + 80 + 48 + + + AXIS_LEFT + true + 144 + 120 + 72 + + + AXIS_RIGHT + true + true + 144 + 120 + 72 + + + DPAD + true + 144 + 8 + 8 + + \ No newline at end of file