From 42b3e72e9608584d873b78defc63bc61218efd5b Mon Sep 17 00:00:00 2001
From: Charles Lombardo <clombardo169@gmail.com>
Date: Sat, 11 Mar 2023 00:34:09 -0500
Subject: [PATCH] android: Convert InputOverlayDrawableJoystick to Kotlin

---
 .../overlay/InputOverlayDrawableJoystick.java | 243 ------------------
 .../overlay/InputOverlayDrawableJoystick.kt   | 205 +++++++++++++++
 2 files changed, 205 insertions(+), 243 deletions(-)
 delete mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.java
 create mode 100644 src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt

diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.java
deleted file mode 100644
index f7919e483..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.java
+++ /dev/null
@@ -1,243 +0,0 @@
-/**
- * Copyright 2013 Dolphin Emulator Project
- * Licensed under GPLv2+
- * Refer to the license.txt file included.
- */
-
-package org.yuzu.yuzu_emu.overlay;
-
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.view.MotionEvent;
-
-import org.yuzu.yuzu_emu.NativeLibrary;
-import org.yuzu.yuzu_emu.NativeLibrary.ButtonType;
-import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
-
-/**
- * Custom {@link BitmapDrawable} that is capable
- * of storing it's own ID.
- */
-public final class InputOverlayDrawableJoystick {
-    // The ID value what type of joystick this Drawable represents.
-    private int mJoystickId;
-    // The ID value what type of button this Drawable represents.
-    private int mButtonId;
-    // The ID value what motion event is tracking
-    private int mTrackId = -1;
-    private float mXAxis;
-    private float mYAxis;
-    private int mControlPositionX, mControlPositionY;
-    private int mWidth;
-    private int mHeight;
-    private Rect mVirtBounds;
-    private Rect mOrigBounds;
-    private BitmapDrawable mOuterBitmap;
-    private BitmapDrawable mDefaultStateInnerBitmap;
-    private BitmapDrawable mPressedStateInnerBitmap;
-    private BitmapDrawable mBoundsBoxBitmap;
-    private boolean mPressedState = false;
-
-    /**
-     * Constructor
-     *
-     * @param res                {@link Resources} instance.
-     * @param bitmapOuter        {@link Bitmap} which represents the outer non-movable part of the joystick.
-     * @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick.
-     * @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick.
-     * @param rectOuter          {@link Rect} which represents the outer joystick bounds.
-     * @param rectInner          {@link Rect} which represents the inner joystick bounds.
-     * @param joystick           Identifier for which joystick this is.
-     */
-    public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter,
-                                        Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed,
-                                        Rect rectOuter, Rect rectInner, int joystick, int button) {
-        mJoystickId = joystick;
-        mButtonId = button;
-
-        mOuterBitmap = new BitmapDrawable(res, bitmapOuter);
-        mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault);
-        mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed);
-        mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter);
-        mWidth = bitmapOuter.getWidth();
-        mHeight = bitmapOuter.getHeight();
-
-        setBounds(rectOuter);
-        mDefaultStateInnerBitmap.setBounds(rectInner);
-        mPressedStateInnerBitmap.setBounds(rectInner);
-        mVirtBounds = getBounds();
-        mOrigBounds = mOuterBitmap.copyBounds();
-        mBoundsBoxBitmap.setAlpha(0);
-        mBoundsBoxBitmap.setBounds(getVirtBounds());
-        SetInnerBounds();
-    }
-
-    public void draw(Canvas canvas) {
-        mOuterBitmap.draw(canvas);
-        getCurrentStateBitmapDrawable().draw(canvas);
-        mBoundsBoxBitmap.draw(canvas);
-    }
-
-    public boolean updateStatus(MotionEvent event) {
-        int pointerIndex = event.getActionIndex();
-        int xPosition = (int) event.getX(pointerIndex);
-        int yPosition = (int) event.getY(pointerIndex);
-        int pointerId = event.getPointerId(pointerIndex);
-        int motion_event = event.getAction() & MotionEvent.ACTION_MASK;
-        boolean isActionDown = motion_event == MotionEvent.ACTION_DOWN || motion_event == MotionEvent.ACTION_POINTER_DOWN;
-        boolean isActionUp = motion_event == MotionEvent.ACTION_UP || motion_event == MotionEvent.ACTION_POINTER_UP;
-
-        if (isActionDown) {
-            if (!getBounds().contains(xPosition, yPosition)) {
-                return false;
-            }
-            mPressedState = true;
-            mOuterBitmap.setAlpha(0);
-            mBoundsBoxBitmap.setAlpha(255);
-            if (EmulationMenuSettings.getJoystickRelCenter()) {
-                getVirtBounds().offset(xPosition - getVirtBounds().centerX(),
-                        yPosition - getVirtBounds().centerY());
-            }
-            mBoundsBoxBitmap.setBounds(getVirtBounds());
-            mTrackId = pointerId;
-        }
-
-        if (isActionUp) {
-            if (mTrackId != pointerId) {
-                return false;
-            }
-            mPressedState = false;
-            mXAxis = 0.0f;
-            mYAxis = 0.0f;
-            mOuterBitmap.setAlpha(255);
-            mBoundsBoxBitmap.setAlpha(0);
-            setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
-                    mOrigBounds.bottom));
-            setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right,
-                    mOrigBounds.bottom));
-            SetInnerBounds();
-            mTrackId = -1;
-            return true;
-        }
-
-        if (mTrackId == -1)
-            return false;
-
-        for (int i = 0; i < event.getPointerCount(); i++) {
-            if (mTrackId != event.getPointerId(i)) {
-                continue;
-            }
-            float touchX = event.getX(i);
-            float touchY = event.getY(i);
-            float maxY = getVirtBounds().bottom;
-            float maxX = getVirtBounds().right;
-            touchX -= getVirtBounds().centerX();
-            maxX -= getVirtBounds().centerX();
-            touchY -= getVirtBounds().centerY();
-            maxY -= getVirtBounds().centerY();
-            final float AxisX = touchX / maxX;
-            final float AxisY = touchY / maxY;
-            final float oldXAxis = mXAxis;
-            final float oldYAxis = mYAxis;
-
-            // Clamp the circle pad input to a circle
-            final float angle = (float) Math.atan2(AxisY, AxisX);
-            float radius = (float) Math.sqrt(AxisX * AxisX + AxisY * AxisY);
-            if (radius > 1.0f) {
-                radius = 1.0f;
-            }
-            mXAxis = ((float) Math.cos(angle) * radius);
-            mYAxis = ((float) Math.sin(angle) * radius);
-            SetInnerBounds();
-            return oldXAxis != mXAxis && oldYAxis != mYAxis;
-        }
-
-        return false;
-    }
-
-    private void SetInnerBounds() {
-        int X = getVirtBounds().centerX() + (int) ((mXAxis) * (getVirtBounds().width() / 2));
-        int Y = getVirtBounds().centerY() + (int) ((mYAxis) * (getVirtBounds().height() / 2));
-
-        if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2))
-            X = getVirtBounds().centerX() + (getVirtBounds().width() / 2);
-        if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2))
-            X = getVirtBounds().centerX() - (getVirtBounds().width() / 2);
-        if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2))
-            Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2);
-        if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2))
-            Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2);
-
-        int width = mPressedStateInnerBitmap.getBounds().width() / 2;
-        int height = mPressedStateInnerBitmap.getBounds().height() / 2;
-        mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height);
-        mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds());
-    }
-
-    public void setPosition(int x, int y) {
-        mControlPositionX = x;
-        mControlPositionY = y;
-    }
-
-    private BitmapDrawable getCurrentStateBitmapDrawable() {
-        return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap;
-    }
-
-    /**
-     * Gets this InputOverlayDrawableJoystick's button ID.
-     *
-     * @return this InputOverlayDrawableJoystick's button ID.
-     */
-    public int getJoystickId() {
-        return mJoystickId;
-    }
-
-    public float getXAxis() {
-        return mXAxis;
-    }
-
-    public float getYAxis() {
-        // Nintendo joysticks have y axis inverted
-        return -mYAxis;
-    }
-
-    public int getButtonId() {
-        return mButtonId;
-    }
-
-    public int getTrackId() {
-        return mTrackId;
-    }
-
-    public int getButtonStatus() {
-        // TODO: Add button support
-        return NativeLibrary.ButtonState.RELEASED;
-    }
-
-    public Rect getBounds() {
-        return mOuterBitmap.getBounds();
-    }
-
-    public void setBounds(Rect bounds) {
-        mOuterBitmap.setBounds(bounds);
-    }
-
-    private Rect getVirtBounds() {
-        return mVirtBounds;
-    }
-
-    private void setVirtBounds(Rect bounds) {
-        mVirtBounds = bounds;
-    }
-
-    public int getWidth() {
-        return mWidth;
-    }
-
-    public int getHeight() {
-        return mHeight;
-    }
-}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
new file mode 100644
index 000000000..84a3ea40b
--- /dev/null
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt
@@ -0,0 +1,205 @@
+package org.yuzu.yuzu_emu.overlay
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.BitmapDrawable
+import android.view.MotionEvent
+import org.yuzu.yuzu_emu.NativeLibrary
+import org.yuzu.yuzu_emu.utils.EmulationMenuSettings
+import kotlin.math.atan2
+import kotlin.math.cos
+import kotlin.math.sin
+import kotlin.math.sqrt
+
+/**
+ * Custom [BitmapDrawable] that is capable
+ * of storing it's own ID.
+ *
+ * @param res                [Resources] instance.
+ * @param bitmapOuter        [Bitmap] which represents the outer non-movable part of the joystick.
+ * @param bitmapInnerDefault [Bitmap] which represents the default inner movable part of the joystick.
+ * @param bitmapInnerPressed [Bitmap] which represents the pressed inner movable part of the joystick.
+ * @param rectOuter          [Rect] which represents the outer joystick bounds.
+ * @param rectInner          [Rect] which represents the inner joystick bounds.
+ * @param joystickId         The ID value what type of joystick this Drawable represents.
+ * @param buttonId           The ID value what type of button this Drawable represents.
+ */
+class InputOverlayDrawableJoystick(
+    res: Resources,
+    bitmapOuter: Bitmap,
+    bitmapInnerDefault: Bitmap,
+    bitmapInnerPressed: Bitmap,
+    rectOuter: Rect,
+    rectInner: Rect,
+    val joystickId: Int,
+    val buttonId: Int
+) {
+
+    // The ID value what motion event is tracking
+    var trackId = -1
+    var xAxis = 0f
+    private var yAxis = 0f
+    private var controlPositionX = 0
+    private var controlPositionY = 0
+    val width: Int
+    val height: Int
+    private var virtBounds: Rect
+    private val origBounds: Rect
+    private val outerBitmap: BitmapDrawable
+    private val defaultStateInnerBitmap: BitmapDrawable
+    private val pressedStateInnerBitmap: BitmapDrawable
+    private val boundsBoxBitmap: BitmapDrawable
+    private var pressedState = false
+
+    // TODO: Add button support
+    val buttonStatus: Int
+        get() =
+            NativeLibrary.ButtonState.RELEASED
+    var bounds: Rect?
+        get() = outerBitmap.bounds
+        set(bounds) {
+            outerBitmap.bounds = bounds!!
+        }
+
+    // Nintendo joysticks have y axis inverted
+    val realYAxis: Float
+        get() = -yAxis
+
+    private val currentStateBitmapDrawable: BitmapDrawable
+        get() = if (pressedState) pressedStateInnerBitmap else defaultStateInnerBitmap
+
+    init {
+        outerBitmap = BitmapDrawable(res, bitmapOuter)
+        defaultStateInnerBitmap = BitmapDrawable(res, bitmapInnerDefault)
+        pressedStateInnerBitmap = BitmapDrawable(res, bitmapInnerPressed)
+        boundsBoxBitmap = BitmapDrawable(res, bitmapOuter)
+        width = bitmapOuter.width
+        height = bitmapOuter.height
+        bounds = rectOuter
+        defaultStateInnerBitmap.bounds = rectInner
+        pressedStateInnerBitmap.bounds = rectInner
+        virtBounds = bounds!!
+        origBounds = outerBitmap.copyBounds()
+        boundsBoxBitmap.alpha = 0
+        boundsBoxBitmap.bounds = virtBounds
+        setInnerBounds()
+    }
+
+    fun draw(canvas: Canvas?) {
+        outerBitmap.draw(canvas!!)
+        currentStateBitmapDrawable.draw(canvas)
+        boundsBoxBitmap.draw(canvas)
+    }
+
+    fun updateStatus(event: MotionEvent): Boolean {
+        val pointerIndex = event.actionIndex
+        val xPosition = event.getX(pointerIndex).toInt()
+        val yPosition = event.getY(pointerIndex).toInt()
+        val pointerId = event.getPointerId(pointerIndex)
+        val motionEvent = event.action and MotionEvent.ACTION_MASK
+        val isActionDown =
+            motionEvent == MotionEvent.ACTION_DOWN || motionEvent == MotionEvent.ACTION_POINTER_DOWN
+        val isActionUp =
+            motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP
+        if (isActionDown) {
+            if (!bounds!!.contains(xPosition, yPosition)) {
+                return false
+            }
+            pressedState = true
+            outerBitmap.alpha = 0
+            boundsBoxBitmap.alpha = 255
+            if (EmulationMenuSettings.joystickRelCenter) {
+                virtBounds.offset(
+                    xPosition - virtBounds.centerX(),
+                    yPosition - virtBounds.centerY()
+                )
+            }
+            boundsBoxBitmap.bounds = virtBounds
+            trackId = pointerId
+        }
+        if (isActionUp) {
+            if (trackId != pointerId) {
+                return false
+            }
+            pressedState = false
+            xAxis = 0.0f
+            yAxis = 0.0f
+            outerBitmap.alpha = 255
+            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
+            return true
+        }
+        if (trackId == -1) return false
+        for (i in 0 until event.pointerCount) {
+            if (trackId != event.getPointerId(i)) {
+                continue
+            }
+            var touchX = event.getX(i)
+            var touchY = event.getY(i)
+            var maxY = virtBounds.bottom.toFloat()
+            var maxX = virtBounds.right.toFloat()
+            touchX -= virtBounds.centerX().toFloat()
+            maxX -= virtBounds.centerX().toFloat()
+            touchY -= virtBounds.centerY().toFloat()
+            maxY -= virtBounds.centerY().toFloat()
+            val axisX = touchX / maxX
+            val axisY = touchY / maxY
+            val oldXAxis = xAxis
+            val oldYAxis = yAxis
+
+            // Clamp the circle pad input to a circle
+            val angle = atan2(axisY.toDouble(), axisX.toDouble()).toFloat()
+            var radius = sqrt((axisX * axisX + axisY * axisY).toDouble()).toFloat()
+            if (radius > 1.0f) {
+                radius = 1.0f
+            }
+            xAxis = cos(angle.toDouble()).toFloat() * radius
+            yAxis = sin(angle.toDouble()).toFloat() * radius
+            setInnerBounds()
+            return oldXAxis != xAxis && oldYAxis != yAxis
+        }
+        return false
+    }
+
+    private fun setInnerBounds() {
+        var x = virtBounds.centerX() + (xAxis * (virtBounds.width() / 2)).toInt()
+        var y = virtBounds.centerY() + (yAxis * (virtBounds.height() / 2)).toInt()
+        if (x > virtBounds.centerX() + virtBounds.width() / 2) x =
+            virtBounds.centerX() + virtBounds.width() / 2
+        if (x < virtBounds.centerX() - virtBounds.width() / 2) x =
+            virtBounds.centerX() - virtBounds.width() / 2
+        if (y > virtBounds.centerY() + virtBounds.height() / 2) y =
+            virtBounds.centerY() + virtBounds.height() / 2
+        if (y < virtBounds.centerY() - virtBounds.height() / 2) y =
+            virtBounds.centerY() - virtBounds.height() / 2
+        val width = pressedStateInnerBitmap.bounds.width() / 2
+        val height = pressedStateInnerBitmap.bounds.height() / 2
+        defaultStateInnerBitmap.setBounds(
+            x - width,
+            y - height,
+            x + width,
+            y + height
+        )
+        pressedStateInnerBitmap.bounds = defaultStateInnerBitmap.bounds
+    }
+
+    fun setPosition(x: Int, y: Int) {
+        controlPositionX = x
+        controlPositionY = y
+    }
+}