android: Convert InputOverlay to Kotlin
This commit is contained in:
		| @@ -1,656 +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.app.Activity; |  | ||||||
| import android.content.Context; |  | ||||||
| import android.content.SharedPreferences; |  | ||||||
| import android.content.res.Configuration; |  | ||||||
| import android.content.res.Resources; |  | ||||||
| import android.graphics.Bitmap; |  | ||||||
| import android.graphics.BitmapFactory; |  | ||||||
| import android.graphics.Canvas; |  | ||||||
| import android.graphics.Rect; |  | ||||||
| import android.graphics.drawable.BitmapDrawable; |  | ||||||
| import android.graphics.drawable.Drawable; |  | ||||||
| import android.graphics.drawable.VectorDrawable; |  | ||||||
| import android.hardware.Sensor; |  | ||||||
| import android.hardware.SensorEvent; |  | ||||||
| import android.hardware.SensorEventListener; |  | ||||||
| import android.hardware.SensorManager; |  | ||||||
| import android.preference.PreferenceManager; |  | ||||||
| import android.util.AttributeSet; |  | ||||||
| import android.util.DisplayMetrics; |  | ||||||
| import android.view.Display; |  | ||||||
| import android.view.MotionEvent; |  | ||||||
| import android.view.SurfaceView; |  | ||||||
| import android.view.View; |  | ||||||
| import android.view.View.OnTouchListener; |  | ||||||
|  |  | ||||||
| import androidx.core.content.ContextCompat; |  | ||||||
|  |  | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary; |  | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary.ButtonType; |  | ||||||
| import org.yuzu.yuzu_emu.NativeLibrary.StickType; |  | ||||||
| import org.yuzu.yuzu_emu.R; |  | ||||||
| import org.yuzu.yuzu_emu.utils.EmulationMenuSettings; |  | ||||||
|  |  | ||||||
| import java.util.HashSet; |  | ||||||
| import java.util.Set; |  | ||||||
|  |  | ||||||
| /** |  | ||||||
|  * Draws the interactive input overlay on top of the |  | ||||||
|  * {@link SurfaceView} that is rendering emulation. |  | ||||||
|  */ |  | ||||||
| public final class InputOverlay extends SurfaceView implements OnTouchListener, SensorEventListener { |  | ||||||
|     private final Set<InputOverlayDrawableButton> overlayButtons = new HashSet<>(); |  | ||||||
|     private final Set<InputOverlayDrawableDpad> overlayDpads = new HashSet<>(); |  | ||||||
|     private final Set<InputOverlayDrawableJoystick> overlayJoysticks = new HashSet<>(); |  | ||||||
|  |  | ||||||
|     private boolean mIsInEditMode = false; |  | ||||||
|  |  | ||||||
|     private SharedPreferences mPreferences; |  | ||||||
|  |  | ||||||
|     private float[] gyro = new float[3]; |  | ||||||
|     private float[] accel = new float[3]; |  | ||||||
|  |  | ||||||
|     private long motionTimestamp; |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Constructor |  | ||||||
|      * |  | ||||||
|      * @param context The current {@link Context}. |  | ||||||
|      * @param attrs   {@link AttributeSet} for parsing XML attributes. |  | ||||||
|      */ |  | ||||||
|     public InputOverlay(Context context, AttributeSet attrs) { |  | ||||||
|         super(context, attrs); |  | ||||||
|  |  | ||||||
|         mPreferences = PreferenceManager.getDefaultSharedPreferences(getContext()); |  | ||||||
|         if (!mPreferences.getBoolean("OverlayInit", false)) { |  | ||||||
|             defaultOverlay(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Load the controls. |  | ||||||
|         refreshControls(); |  | ||||||
|  |  | ||||||
|         // Set the on motion sensor listener. |  | ||||||
|         setMotionSensorListener(context); |  | ||||||
|  |  | ||||||
|         // Set the on touch listener. |  | ||||||
|         setOnTouchListener(this); |  | ||||||
|  |  | ||||||
|         // Force draw |  | ||||||
|         setWillNotDraw(false); |  | ||||||
|  |  | ||||||
|         // Request focus for the overlay so it has priority on presses. |  | ||||||
|         requestFocus(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void setMotionSensorListener(Context context) { |  | ||||||
|         SensorManager sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); |  | ||||||
|         Sensor gyro_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE); |  | ||||||
|         Sensor accel_sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); |  | ||||||
|  |  | ||||||
|         if (gyro_sensor != null) { |  | ||||||
|             sensorManager.registerListener(this, gyro_sensor, SensorManager.SENSOR_DELAY_GAME); |  | ||||||
|         } |  | ||||||
|         if (accel_sensor != null) { |  | ||||||
|             sensorManager.registerListener(this, accel_sensor, SensorManager.SENSOR_DELAY_GAME); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Resizes a {@link Bitmap} by a given scale factor |  | ||||||
|      * |  | ||||||
|      * @param vectorDrawable The {@link Bitmap} to scale. |  | ||||||
|      * @param scale          The scale factor for the bitmap. |  | ||||||
|      * @return The scaled {@link Bitmap} |  | ||||||
|      */ |  | ||||||
|     private static Bitmap getBitmap(VectorDrawable vectorDrawable, float scale) { |  | ||||||
|         Bitmap bitmap = Bitmap.createBitmap((int) (vectorDrawable.getIntrinsicWidth() * scale), |  | ||||||
|                 (int) (vectorDrawable.getIntrinsicHeight() * scale), Bitmap.Config.ARGB_8888); |  | ||||||
|         Canvas canvas = new Canvas(bitmap); |  | ||||||
|         vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); |  | ||||||
|         vectorDrawable.draw(canvas); |  | ||||||
|         return bitmap; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private static Bitmap getBitmap(Context context, int drawableId, float scale) { |  | ||||||
|         Drawable drawable = ContextCompat.getDrawable(context, drawableId); |  | ||||||
|         if (drawable instanceof BitmapDrawable) { |  | ||||||
|             return BitmapFactory.decodeResource(context.getResources(), drawableId); |  | ||||||
|         } else if (drawable instanceof VectorDrawable) { |  | ||||||
|             return getBitmap((VectorDrawable) drawable, scale); |  | ||||||
|         } else { |  | ||||||
|             throw new IllegalArgumentException("unsupported drawable type"); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Initializes an InputOverlayDrawableButton, given by resId, with all of the |  | ||||||
|      * parameters set for it to be properly shown on the InputOverlay. |  | ||||||
|      * <p> |  | ||||||
|      * This works due to the way the X and Y coordinates are stored within |  | ||||||
|      * the {@link SharedPreferences}. |  | ||||||
|      * <p> |  | ||||||
|      * In the input overlay configuration menu, |  | ||||||
|      * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). |  | ||||||
|      * the X and Y coordinates of the button at the END of its touch event |  | ||||||
|      * (when you remove your finger/stylus from the touchscreen) are then stored |  | ||||||
|      * within a SharedPreferences instance so that those values can be retrieved here. |  | ||||||
|      * <p> |  | ||||||
|      * This has a few benefits over the conventional way of storing the values |  | ||||||
|      * (ie. within the yuzu ini file). |  | ||||||
|      * <ul> |  | ||||||
|      * <li>No native calls</li> |  | ||||||
|      * <li>Keeps Android-only values inside the Android environment</li> |  | ||||||
|      * </ul> |  | ||||||
|      * <p> |  | ||||||
|      * Technically no modifications should need to be performed on the returned |  | ||||||
|      * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait |  | ||||||
|      * for Android to call the onDraw method. |  | ||||||
|      * |  | ||||||
|      * @param context      The current {@link Context}. |  | ||||||
|      * @param defaultResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Default State). |  | ||||||
|      * @param pressedResId The resource ID of the {@link Drawable} to get the {@link Bitmap} of (Pressed State). |  | ||||||
|      * @param buttonId     Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. |  | ||||||
|      * @return An {@link InputOverlayDrawableButton} with the correct drawing bounds set. |  | ||||||
|      */ |  | ||||||
|     private static InputOverlayDrawableButton initializeOverlayButton(Context context, |  | ||||||
|                                                                       int defaultResId, int pressedResId, int buttonId, String orientation) { |  | ||||||
|         // Resources handle for fetching the initial Drawable resource. |  | ||||||
|         final Resources res = context.getResources(); |  | ||||||
|  |  | ||||||
|         // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. |  | ||||||
|         final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); |  | ||||||
|  |  | ||||||
|         // Decide scale based on button ID and user preference |  | ||||||
|         float scale; |  | ||||||
|  |  | ||||||
|         switch (buttonId) { |  | ||||||
|             case ButtonType.BUTTON_HOME: |  | ||||||
|             case ButtonType.BUTTON_CAPTURE: |  | ||||||
|             case ButtonType.BUTTON_PLUS: |  | ||||||
|             case ButtonType.BUTTON_MINUS: |  | ||||||
|                 scale = 0.35f; |  | ||||||
|                 break; |  | ||||||
|             case ButtonType.TRIGGER_L: |  | ||||||
|             case ButtonType.TRIGGER_R: |  | ||||||
|             case ButtonType.TRIGGER_ZL: |  | ||||||
|             case ButtonType.TRIGGER_ZR: |  | ||||||
|                 scale = 0.38f; |  | ||||||
|                 break; |  | ||||||
|             default: |  | ||||||
|                 scale = 0.43f; |  | ||||||
|                 break; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         scale *= (sPrefs.getInt("controlScale", 50) + 50); |  | ||||||
|         scale /= 100; |  | ||||||
|  |  | ||||||
|         // Initialize the InputOverlayDrawableButton. |  | ||||||
|         final Bitmap defaultStateBitmap = getBitmap(context, defaultResId, scale); |  | ||||||
|         final Bitmap pressedStateBitmap = getBitmap(context, pressedResId, scale); |  | ||||||
|         final InputOverlayDrawableButton overlayDrawable = |  | ||||||
|                 new InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId); |  | ||||||
|  |  | ||||||
|         // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. |  | ||||||
|         // These were set in the input overlay configuration menu. |  | ||||||
|         String xKey; |  | ||||||
|         String yKey; |  | ||||||
|  |  | ||||||
|         xKey = buttonId + orientation + "-X"; |  | ||||||
|         yKey = buttonId + orientation + "-Y"; |  | ||||||
|  |  | ||||||
|         int drawableX = (int) sPrefs.getFloat(xKey, 0f); |  | ||||||
|         int drawableY = (int) sPrefs.getFloat(yKey, 0f); |  | ||||||
|  |  | ||||||
|         int width = overlayDrawable.getWidth(); |  | ||||||
|         int height = overlayDrawable.getHeight(); |  | ||||||
|  |  | ||||||
|         // Now set the bounds for the InputOverlayDrawableButton. |  | ||||||
|         // This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be. |  | ||||||
|         overlayDrawable.setBounds(drawableX - (width / 2), drawableY - (height / 2), drawableX + (width / 2), drawableY + (height / 2)); |  | ||||||
|  |  | ||||||
|         // Need to set the image's position |  | ||||||
|         overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2)); |  | ||||||
|  |  | ||||||
|         return overlayDrawable; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Initializes an {@link InputOverlayDrawableDpad} |  | ||||||
|      * |  | ||||||
|      * @param context                   The current {@link Context}. |  | ||||||
|      * @param defaultResId              The {@link Bitmap} resource ID of the default sate. |  | ||||||
|      * @param pressedOneDirectionResId  The {@link Bitmap} resource ID of the pressed sate in one direction. |  | ||||||
|      * @param pressedTwoDirectionsResId The {@link Bitmap} resource ID of the pressed sate in two directions. |  | ||||||
|      * @param buttonUp                  Identifier for the up button. |  | ||||||
|      * @param buttonDown                Identifier for the down button. |  | ||||||
|      * @param buttonLeft                Identifier for the left button. |  | ||||||
|      * @param buttonRight               Identifier for the right button. |  | ||||||
|      * @return the initialized {@link InputOverlayDrawableDpad} |  | ||||||
|      */ |  | ||||||
|     private static InputOverlayDrawableDpad initializeOverlayDpad(Context context, |  | ||||||
|                                                                   int defaultResId, |  | ||||||
|                                                                   int pressedOneDirectionResId, |  | ||||||
|                                                                   int pressedTwoDirectionsResId, |  | ||||||
|                                                                   int buttonUp, |  | ||||||
|                                                                   int buttonDown, |  | ||||||
|                                                                   int buttonLeft, |  | ||||||
|                                                                   int buttonRight, |  | ||||||
|                                                                   String orientation) { |  | ||||||
|         // Resources handle for fetching the initial Drawable resource. |  | ||||||
|         final Resources res = context.getResources(); |  | ||||||
|  |  | ||||||
|         // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad. |  | ||||||
|         final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); |  | ||||||
|  |  | ||||||
|         // Decide scale based on button ID and user preference |  | ||||||
|         float scale = 0.40f; |  | ||||||
|  |  | ||||||
|         scale *= (sPrefs.getInt("controlScale", 50) + 50); |  | ||||||
|         scale /= 100; |  | ||||||
|  |  | ||||||
|         // Initialize the InputOverlayDrawableDpad. |  | ||||||
|         final Bitmap defaultStateBitmap = getBitmap(context, defaultResId, scale); |  | ||||||
|         final Bitmap pressedOneDirectionStateBitmap = getBitmap(context, pressedOneDirectionResId, |  | ||||||
|                 scale); |  | ||||||
|         final Bitmap pressedTwoDirectionsStateBitmap = getBitmap(context, pressedTwoDirectionsResId, |  | ||||||
|                 scale); |  | ||||||
|         final InputOverlayDrawableDpad overlayDrawable = |  | ||||||
|                 new InputOverlayDrawableDpad(res, defaultStateBitmap, |  | ||||||
|                         pressedOneDirectionStateBitmap, pressedTwoDirectionsStateBitmap, |  | ||||||
|                         buttonUp, buttonDown, buttonLeft, buttonRight); |  | ||||||
|  |  | ||||||
|         // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. |  | ||||||
|         // These were set in the input overlay configuration menu. |  | ||||||
|         int drawableX = (int) sPrefs.getFloat(buttonUp + orientation + "-X", 0f); |  | ||||||
|         int drawableY = (int) sPrefs.getFloat(buttonUp + orientation + "-Y", 0f); |  | ||||||
|  |  | ||||||
|         int width = overlayDrawable.getWidth(); |  | ||||||
|         int height = overlayDrawable.getHeight(); |  | ||||||
|  |  | ||||||
|         // Now set the bounds for the InputOverlayDrawableDpad. |  | ||||||
|         // This will dictate where on the screen (and the what the size) the InputOverlayDrawableDpad will be. |  | ||||||
|         overlayDrawable.setBounds(drawableX - (width / 2), drawableY - (height / 2), drawableX + (width / 2), drawableY + (height / 2)); |  | ||||||
|  |  | ||||||
|         // Need to set the image's position |  | ||||||
|         overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2)); |  | ||||||
|  |  | ||||||
|         return overlayDrawable; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Initializes an {@link InputOverlayDrawableJoystick} |  | ||||||
|      * |  | ||||||
|      * @param context         The current {@link Context} |  | ||||||
|      * @param resOuter        Resource ID for the outer image of the joystick (the static image that shows the circular bounds). |  | ||||||
|      * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). |  | ||||||
|      * @param pressedResInner Resource ID for the pressed inner image of the joystick. |  | ||||||
|      * @param joystick        Identifier for which joystick this is. |  | ||||||
|      * @param button          Identifier for which joystick button this is. |  | ||||||
|      * @return the initialized {@link InputOverlayDrawableJoystick}. |  | ||||||
|      */ |  | ||||||
|     private static InputOverlayDrawableJoystick initializeOverlayJoystick(Context context, |  | ||||||
|                                                                           int resOuter, int defaultResInner, int pressedResInner, int joystick, int button, String orientation) { |  | ||||||
|         // Resources handle for fetching the initial Drawable resource. |  | ||||||
|         final Resources res = context.getResources(); |  | ||||||
|  |  | ||||||
|         // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick. |  | ||||||
|         final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(context); |  | ||||||
|  |  | ||||||
|         // Decide scale based on user preference |  | ||||||
|         float scale = 0.40f; |  | ||||||
|         scale *= (sPrefs.getInt("controlScale", 50) + 50); |  | ||||||
|         scale /= 100; |  | ||||||
|  |  | ||||||
|         // Initialize the InputOverlayDrawableJoystick. |  | ||||||
|         final Bitmap bitmapOuter = getBitmap(context, resOuter, scale); |  | ||||||
|         final Bitmap bitmapInnerDefault = getBitmap(context, defaultResInner, 1.0f); |  | ||||||
|         final Bitmap bitmapInnerPressed = getBitmap(context, pressedResInner, 1.0f); |  | ||||||
|  |  | ||||||
|         // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. |  | ||||||
|         // These were set in the input overlay configuration menu. |  | ||||||
|         int drawableX = (int) sPrefs.getFloat(button + orientation + "-X", 0f); |  | ||||||
|         int drawableY = (int) sPrefs.getFloat(button + orientation + "-Y", 0f); |  | ||||||
|  |  | ||||||
|         float outerScale = 1.66f; |  | ||||||
|  |  | ||||||
|         // Now set the bounds for the InputOverlayDrawableJoystick. |  | ||||||
|         // This will dictate where on the screen (and the what the size) the InputOverlayDrawableJoystick will be. |  | ||||||
|         int outerSize = bitmapOuter.getWidth(); |  | ||||||
|         Rect outerRect = new Rect(drawableX - (outerSize / 2), drawableY - (outerSize / 2), drawableX + (outerSize / 2), drawableY + (outerSize / 2)); |  | ||||||
|         Rect innerRect = new Rect(0, 0, (int) (outerSize / outerScale), (int) (outerSize / outerScale)); |  | ||||||
|  |  | ||||||
|         // Send the drawableId to the joystick so it can be referenced when saving control position. |  | ||||||
|         final InputOverlayDrawableJoystick overlayDrawable |  | ||||||
|                 = new InputOverlayDrawableJoystick(res, bitmapOuter, |  | ||||||
|                 bitmapInnerDefault, bitmapInnerPressed, |  | ||||||
|                 outerRect, innerRect, joystick, button); |  | ||||||
|  |  | ||||||
|         // Need to set the image's position |  | ||||||
|         overlayDrawable.setPosition(drawableX, drawableY); |  | ||||||
|  |  | ||||||
|         return overlayDrawable; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void draw(Canvas canvas) { |  | ||||||
|         super.draw(canvas); |  | ||||||
|  |  | ||||||
|         for (InputOverlayDrawableButton button : overlayButtons) { |  | ||||||
|             button.draw(canvas); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (InputOverlayDrawableDpad dpad : overlayDpads) { |  | ||||||
|             dpad.draw(canvas); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { |  | ||||||
|             joystick.draw(canvas); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public boolean onTouch(View v, MotionEvent event) { |  | ||||||
|         if (isInEditMode()) { |  | ||||||
|             return onTouchWhileEditing(event); |  | ||||||
|         } |  | ||||||
|         boolean should_update_view = false; |  | ||||||
|         for (InputOverlayDrawableButton button : overlayButtons) { |  | ||||||
|             if (!button.updateStatus(event)) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, button.getId(), button.getStatus()); |  | ||||||
|             should_update_view = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (InputOverlayDrawableDpad dpad : overlayDpads) { |  | ||||||
|             if (!dpad.updateStatus(event, EmulationMenuSettings.getDpadSlideEnable())) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, dpad.getUpId(), dpad.getUpStatus()); |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, dpad.getDownId(), dpad.getDownStatus()); |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, dpad.getLeftId(), dpad.getLeftStatus()); |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, dpad.getRightId(), dpad.getRightStatus()); |  | ||||||
|             should_update_view = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { |  | ||||||
|             if (!joystick.updateStatus(event)) { |  | ||||||
|                 continue; |  | ||||||
|             } |  | ||||||
|             int axisID = joystick.getJoystickId(); |  | ||||||
|             NativeLibrary.onGamePadJoystickEvent(NativeLibrary.Player1Device, axisID, joystick.getXAxis(), joystick.getYAxis()); |  | ||||||
|             NativeLibrary.onGamePadButtonEvent(NativeLibrary.Player1Device, joystick.getButtonId(), joystick.getButtonStatus()); |  | ||||||
|             should_update_view = true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (should_update_view) { |  | ||||||
|             invalidate(); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (!mPreferences.getBoolean("isTouchEnabled", true)) { |  | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         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 isActionMove = motion_event == MotionEvent.ACTION_MOVE; |  | ||||||
|         boolean isActionUp = motion_event == MotionEvent.ACTION_UP || motion_event == MotionEvent.ACTION_POINTER_UP; |  | ||||||
|  |  | ||||||
|         if (isActionDown && !isTouchInputConsumed(pointerId)) { |  | ||||||
|             NativeLibrary.onTouchPressed(pointerId, xPosition, yPosition); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (isActionMove) { |  | ||||||
|             for (int i = 0; i < event.getPointerCount(); i++) { |  | ||||||
|                 int fingerId = event.getPointerId(i); |  | ||||||
|                 if (isTouchInputConsumed(fingerId)) { |  | ||||||
|                     continue; |  | ||||||
|                 } |  | ||||||
|                 NativeLibrary.onTouchMoved(fingerId, event.getX(i), event.getY(i)); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (isActionUp && !isTouchInputConsumed(pointerId)) { |  | ||||||
|             NativeLibrary.onTouchReleased(pointerId); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private boolean isTouchInputConsumed(int track_id) { |  | ||||||
|         for (InputOverlayDrawableButton button : overlayButtons) { |  | ||||||
|             if (button.getTrackId() == track_id) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         for (InputOverlayDrawableDpad dpad : overlayDpads) { |  | ||||||
|             if (dpad.getTrackId() == track_id) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { |  | ||||||
|             if (joystick.getTrackId() == track_id) { |  | ||||||
|                 return true; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public boolean onTouchWhileEditing(MotionEvent event) { |  | ||||||
|         // TODO: Reimplement this |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onSensorChanged(SensorEvent event) { |  | ||||||
|         if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { |  | ||||||
|             accel[0] = -event.values[1] / SensorManager.GRAVITY_EARTH; |  | ||||||
|             accel[1] = event.values[0] / SensorManager.GRAVITY_EARTH; |  | ||||||
|             accel[2] = -event.values[2] / SensorManager.GRAVITY_EARTH; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { |  | ||||||
|             // Investigate why sensor value is off by 12x |  | ||||||
|             gyro[0] = event.values[1] / 12.0f; |  | ||||||
|             gyro[1] = -event.values[0] / 12.0f; |  | ||||||
|             gyro[2] = event.values[2] / 12.0f; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         // Only update state on accelerometer data |  | ||||||
|         if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) { |  | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         long delta_timestamp = (event.timestamp - motionTimestamp) / 1000; |  | ||||||
|         motionTimestamp = event.timestamp; |  | ||||||
|         NativeLibrary.onGamePadMotionEvent(NativeLibrary.Player1Device, delta_timestamp, gyro[0], gyro[1], gyro[2], accel[0], accel[1], accel[2]); |  | ||||||
|         NativeLibrary.onGamePadMotionEvent(NativeLibrary.ConsoleDevice, delta_timestamp, gyro[0], gyro[1], gyro[2], accel[0], accel[1], accel[2]); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Override |  | ||||||
|     public void onAccuracyChanged(Sensor sensor, int i) { |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void addOverlayControls(String orientation) { |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle0", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_a, |  | ||||||
|                     R.drawable.facebutton_a_depressed, ButtonType.BUTTON_A, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle1", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_b, |  | ||||||
|                     R.drawable.facebutton_b_depressed, ButtonType.BUTTON_B, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle2", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_x, |  | ||||||
|                     R.drawable.facebutton_x_depressed, ButtonType.BUTTON_X, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle3", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_y, |  | ||||||
|                     R.drawable.facebutton_y_depressed, ButtonType.BUTTON_Y, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle4", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.l_shoulder, |  | ||||||
|                     R.drawable.l_shoulder_depressed, ButtonType.TRIGGER_L, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle5", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.r_shoulder, |  | ||||||
|                     R.drawable.r_shoulder_depressed, ButtonType.TRIGGER_R, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle6", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.zl_trigger, |  | ||||||
|                     R.drawable.zl_trigger_depressed, ButtonType.TRIGGER_ZL, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle7", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.zr_trigger, |  | ||||||
|                     R.drawable.zr_trigger_depressed, ButtonType.TRIGGER_ZR, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle8", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_plus, |  | ||||||
|                     R.drawable.facebutton_plus_depressed, ButtonType.BUTTON_PLUS, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle9", true)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_minus, |  | ||||||
|                     R.drawable.facebutton_minus_depressed, ButtonType.BUTTON_MINUS, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle10", true)) { |  | ||||||
|             overlayDpads.add(initializeOverlayDpad(getContext(), R.drawable.dpad_standard, |  | ||||||
|                     R.drawable.dpad_standard_cardinal_depressed, |  | ||||||
|                     R.drawable.dpad_standard_diagonal_depressed, |  | ||||||
|                     ButtonType.DPAD_UP, ButtonType.DPAD_DOWN, |  | ||||||
|                     ButtonType.DPAD_LEFT, ButtonType.DPAD_RIGHT, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle11", true)) { |  | ||||||
|             overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.joystick_range, |  | ||||||
|                     R.drawable.joystick, R.drawable.joystick_depressed, |  | ||||||
|                     StickType.STICK_L, ButtonType.STICK_L, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle12", true)) { |  | ||||||
|             overlayJoysticks.add(initializeOverlayJoystick(getContext(), R.drawable.joystick_range, |  | ||||||
|                     R.drawable.joystick, R.drawable.joystick_depressed, StickType.STICK_R, ButtonType.STICK_R, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle13", false)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_home, |  | ||||||
|                     R.drawable.facebutton_home_depressed, ButtonType.BUTTON_HOME, orientation)); |  | ||||||
|         } |  | ||||||
|         if (mPreferences.getBoolean("buttonToggle14", false)) { |  | ||||||
|             overlayButtons.add(initializeOverlayButton(getContext(), R.drawable.facebutton_screenshot, |  | ||||||
|                     R.drawable.facebutton_screenshot_depressed, ButtonType.BUTTON_CAPTURE, orientation)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void refreshControls() { |  | ||||||
|         // Remove all the overlay buttons from the HashSet. |  | ||||||
|         overlayButtons.clear(); |  | ||||||
|         overlayDpads.clear(); |  | ||||||
|         overlayJoysticks.clear(); |  | ||||||
|  |  | ||||||
|         String orientation = |  | ||||||
|                 getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ? |  | ||||||
|                         "-Portrait" : ""; |  | ||||||
|  |  | ||||||
|         // Add all the enabled overlay items back to the HashSet. |  | ||||||
|         if (EmulationMenuSettings.getShowOverlay()) { |  | ||||||
|             addOverlayControls(orientation); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         invalidate(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void saveControlPosition(int sharedPrefsId, int x, int y, String orientation) { |  | ||||||
|         final SharedPreferences sPrefs = PreferenceManager.getDefaultSharedPreferences(getContext()); |  | ||||||
|         SharedPreferences.Editor sPrefsEditor = sPrefs.edit(); |  | ||||||
|         sPrefsEditor.putFloat(sharedPrefsId + orientation + "-X", x); |  | ||||||
|         sPrefsEditor.putFloat(sharedPrefsId + orientation + "-Y", y); |  | ||||||
|         sPrefsEditor.apply(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void setIsInEditMode(boolean isInEditMode) { |  | ||||||
|         mIsInEditMode = isInEditMode; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void defaultOverlay() { |  | ||||||
|         if (!mPreferences.getBoolean("OverlayInit", false)) { |  | ||||||
|             defaultOverlayLandscape(); |  | ||||||
|         } |  | ||||||
|         resetButtonPlacement(); |  | ||||||
|         SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); |  | ||||||
|         sPrefsEditor.putBoolean("OverlayInit", true); |  | ||||||
|         sPrefsEditor.apply(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public void resetButtonPlacement() { |  | ||||||
|         defaultOverlayLandscape(); |  | ||||||
|         refreshControls(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     private void defaultOverlayLandscape() { |  | ||||||
|         SharedPreferences.Editor sPrefsEditor = mPreferences.edit(); |  | ||||||
|         // Get screen size |  | ||||||
|         Display display = ((Activity) getContext()).getWindowManager().getDefaultDisplay(); |  | ||||||
|         DisplayMetrics outMetrics = new DisplayMetrics(); |  | ||||||
|         display.getRealMetrics(outMetrics); |  | ||||||
|         float maxX = outMetrics.heightPixels; |  | ||||||
|         float maxY = outMetrics.widthPixels; |  | ||||||
|         // Height and width changes depending on orientation. Use the larger value for height. |  | ||||||
|         if (maxY > maxX) { |  | ||||||
|             float tmp = maxX; |  | ||||||
|             maxX = maxY; |  | ||||||
|             maxY = tmp; |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Resources res = getResources(); |  | ||||||
|  |  | ||||||
|         // Each value is a percent from max X/Y stored as an int. Have to bring that value down |  | ||||||
|         // to a decimal before multiplying by MAX X/Y. |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_A_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_A + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_A_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_B_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_B + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_B_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_X_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_X + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_X_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_Y_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_Y + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_Y_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_ZL + "-X", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_ZL_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_ZL + "-Y", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_ZR + "-X", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_ZR_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_ZR + "-Y", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_DPAD_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.DPAD_UP + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-X", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_L_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_L + "-Y", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_L_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-X", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_R_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.TRIGGER_R + "-Y", (((float) res.getInteger(R.integer.SWITCH_TRIGGER_R_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_PLUS + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_PLUS_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_PLUS + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_MINUS + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_MINUS_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_MINUS + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_HOME_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_HOME + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_HOME_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_CAPTURE + "-X", (((float) res.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.BUTTON_CAPTURE + "-Y", (((float) res.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.STICK_R + "-X", (((float) res.getInteger(R.integer.SWITCH_STICK_R_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.STICK_R + "-Y", (((float) res.getInteger(R.integer.SWITCH_STICK_R_Y) / 1000) * maxY)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.STICK_L + "-X", (((float) res.getInteger(R.integer.SWITCH_STICK_L_X) / 1000) * maxX)); |  | ||||||
|         sPrefsEditor.putFloat(ButtonType.STICK_L + "-Y", (((float) res.getInteger(R.integer.SWITCH_STICK_L_Y) / 1000) * maxY)); |  | ||||||
|  |  | ||||||
|         // We want to commit right away, otherwise the overlay could load before this is saved. |  | ||||||
|         sPrefsEditor.commit(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     public boolean isInEditMode() { |  | ||||||
|         return mIsInEditMode; |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| @@ -0,0 +1,886 @@ | |||||||
|  | package org.yuzu.yuzu_emu.overlay | ||||||
|  |  | ||||||
|  | import android.app.Activity | ||||||
|  | import android.content.Context | ||||||
|  | import android.content.SharedPreferences | ||||||
|  | import android.content.res.Configuration | ||||||
|  | import android.graphics.Bitmap | ||||||
|  | import android.graphics.BitmapFactory | ||||||
|  | import android.graphics.Canvas | ||||||
|  | import android.graphics.Rect | ||||||
|  | import android.graphics.drawable.BitmapDrawable | ||||||
|  | import android.graphics.drawable.Drawable | ||||||
|  | import android.graphics.drawable.VectorDrawable | ||||||
|  | import android.hardware.Sensor | ||||||
|  | import android.hardware.SensorEvent | ||||||
|  | import android.hardware.SensorEventListener | ||||||
|  | import android.hardware.SensorManager | ||||||
|  | import android.util.AttributeSet | ||||||
|  | import android.util.DisplayMetrics | ||||||
|  | import android.view.MotionEvent | ||||||
|  | import android.view.SurfaceView | ||||||
|  | import android.view.View | ||||||
|  | import android.view.View.OnTouchListener | ||||||
|  | import androidx.core.content.ContextCompat | ||||||
|  | import androidx.preference.PreferenceManager | ||||||
|  | import org.yuzu.yuzu_emu.NativeLibrary | ||||||
|  | import org.yuzu.yuzu_emu.NativeLibrary.ButtonType | ||||||
|  | import org.yuzu.yuzu_emu.NativeLibrary.StickType | ||||||
|  | import org.yuzu.yuzu_emu.R | ||||||
|  | import org.yuzu.yuzu_emu.YuzuApplication | ||||||
|  | import org.yuzu.yuzu_emu.features.settings.model.Settings | ||||||
|  | import org.yuzu.yuzu_emu.utils.EmulationMenuSettings | ||||||
|  |  | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Draws the interactive input overlay on top of the | ||||||
|  |  * [SurfaceView] that is rendering emulation. | ||||||
|  |  */ | ||||||
|  | class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context, attrs), | ||||||
|  |     OnTouchListener, SensorEventListener { | ||||||
|  |     private val overlayButtons: MutableSet<InputOverlayDrawableButton> = HashSet() | ||||||
|  |     private val overlayDpads: MutableSet<InputOverlayDrawableDpad> = HashSet() | ||||||
|  |     private val overlayJoysticks: MutableSet<InputOverlayDrawableJoystick> = HashSet() | ||||||
|  |     private var inEditMode = false | ||||||
|  |     private val preferences: SharedPreferences = | ||||||
|  |         PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||||
|  |     private val gyro = FloatArray(3) | ||||||
|  |     private val accel = FloatArray(3) | ||||||
|  |     private var motionTimestamp: Long = 0 | ||||||
|  |  | ||||||
|  |     init { | ||||||
|  |         if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { | ||||||
|  |             defaultOverlay() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Load the controls. | ||||||
|  |         refreshControls() | ||||||
|  |  | ||||||
|  |         // Set the on motion sensor listener. | ||||||
|  |         setMotionSensorListener(context) | ||||||
|  |  | ||||||
|  |         // Set the on touch listener. | ||||||
|  |         setOnTouchListener(this) | ||||||
|  |  | ||||||
|  |         // Force draw | ||||||
|  |         setWillNotDraw(false) | ||||||
|  |  | ||||||
|  |         // Request focus for the overlay so it has priority on presses. | ||||||
|  |         requestFocus() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun setMotionSensorListener(context: Context) { | ||||||
|  |         val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager | ||||||
|  |         val gyroSensor = sensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE) | ||||||
|  |         val accelSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) | ||||||
|  |         if (gyroSensor != null) { | ||||||
|  |             sensorManager.registerListener(this, gyroSensor, SensorManager.SENSOR_DELAY_GAME) | ||||||
|  |         } | ||||||
|  |         if (accelSensor != null) { | ||||||
|  |             sensorManager.registerListener(this, accelSensor, SensorManager.SENSOR_DELAY_GAME) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun draw(canvas: Canvas) { | ||||||
|  |         super.draw(canvas) | ||||||
|  |         for (button in overlayButtons) { | ||||||
|  |             button.draw(canvas) | ||||||
|  |         } | ||||||
|  |         for (dpad in overlayDpads) { | ||||||
|  |             dpad.draw(canvas) | ||||||
|  |         } | ||||||
|  |         for (joystick in overlayJoysticks) { | ||||||
|  |             joystick.draw(canvas) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun onTouch(v: View, event: MotionEvent): Boolean { | ||||||
|  |         if (inEditMode) { | ||||||
|  |             return onTouchWhileEditing(event) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         var shouldUpdateView = false | ||||||
|  |  | ||||||
|  |         for (button in overlayButtons) { | ||||||
|  |             if (!button.updateStatus(event)) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 button.id, | ||||||
|  |                 button.status | ||||||
|  |             ) | ||||||
|  |             shouldUpdateView = true | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (dpad in overlayDpads) { | ||||||
|  |             if (!dpad.updateStatus(event, EmulationMenuSettings.dpadSlideEnable)) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 dpad.upId, | ||||||
|  |                 dpad.upStatus | ||||||
|  |             ) | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 dpad.downId, | ||||||
|  |                 dpad.downStatus | ||||||
|  |             ) | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 dpad.leftId, | ||||||
|  |                 dpad.leftStatus | ||||||
|  |             ) | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 dpad.rightId, | ||||||
|  |                 dpad.rightStatus | ||||||
|  |             ) | ||||||
|  |             shouldUpdateView = true | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         for (joystick in overlayJoysticks) { | ||||||
|  |             if (!joystick.updateStatus(event)) { | ||||||
|  |                 continue | ||||||
|  |             } | ||||||
|  |             val axisID = joystick.joystickId | ||||||
|  |             NativeLibrary.onGamePadJoystickEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 axisID, | ||||||
|  |                 joystick.xAxis, | ||||||
|  |                 joystick.realYAxis | ||||||
|  |             ) | ||||||
|  |             NativeLibrary.onGamePadButtonEvent( | ||||||
|  |                 NativeLibrary.Player1Device, | ||||||
|  |                 joystick.buttonId, | ||||||
|  |                 joystick.buttonStatus | ||||||
|  |             ) | ||||||
|  |             shouldUpdateView = true | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (shouldUpdateView) | ||||||
|  |             invalidate() | ||||||
|  |  | ||||||
|  |         if (!preferences.getBoolean(Settings.PREF_TOUCH_ENABLED, true)) { | ||||||
|  |             return true | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         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 isActionMove = motionEvent == MotionEvent.ACTION_MOVE | ||||||
|  |         val isActionUp = | ||||||
|  |             motionEvent == MotionEvent.ACTION_UP || motionEvent == MotionEvent.ACTION_POINTER_UP | ||||||
|  |  | ||||||
|  |         if (isActionDown && !isTouchInputConsumed(pointerId)) { | ||||||
|  |             NativeLibrary.onTouchPressed(pointerId, xPosition.toFloat(), yPosition.toFloat()) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isActionMove) { | ||||||
|  |             for (i in 0 until event.pointerCount) { | ||||||
|  |                 val fingerId = event.getPointerId(i) | ||||||
|  |                 if (isTouchInputConsumed(fingerId)) { | ||||||
|  |                     continue | ||||||
|  |                 } | ||||||
|  |                 NativeLibrary.onTouchMoved(fingerId, event.getX(i), event.getY(i)) | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (isActionUp && !isTouchInputConsumed(pointerId)) { | ||||||
|  |             NativeLibrary.onTouchReleased(pointerId) | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun isTouchInputConsumed(track_id: Int): Boolean { | ||||||
|  |         for (button in overlayButtons) { | ||||||
|  |             if (button.trackId == track_id) { | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (dpad in overlayDpads) { | ||||||
|  |             if (dpad.trackId == track_id) { | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         for (joystick in overlayJoysticks) { | ||||||
|  |             if (joystick.trackId == track_id) { | ||||||
|  |                 return true | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         return false | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun onTouchWhileEditing(event: MotionEvent?): Boolean { | ||||||
|  |         // TODO: Reimplement this | ||||||
|  |         return true | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun onSensorChanged(event: SensorEvent) { | ||||||
|  |         if (event.sensor.type == Sensor.TYPE_ACCELEROMETER) { | ||||||
|  |             accel[0] = -event.values[1] / SensorManager.GRAVITY_EARTH | ||||||
|  |             accel[1] = event.values[0] / SensorManager.GRAVITY_EARTH | ||||||
|  |             accel[2] = -event.values[2] / SensorManager.GRAVITY_EARTH | ||||||
|  |         } | ||||||
|  |         if (event.sensor.type == Sensor.TYPE_GYROSCOPE) { | ||||||
|  |             // Investigate why sensor value is off by 12x | ||||||
|  |             gyro[0] = event.values[1] / 12.0f | ||||||
|  |             gyro[1] = -event.values[0] / 12.0f | ||||||
|  |             gyro[2] = event.values[2] / 12.0f | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         // Only update state on accelerometer data | ||||||
|  |         if (event.sensor.type != Sensor.TYPE_ACCELEROMETER) { | ||||||
|  |             return | ||||||
|  |         } | ||||||
|  |         val deltaTimestamp = (event.timestamp - motionTimestamp) / 1000 | ||||||
|  |         motionTimestamp = event.timestamp | ||||||
|  |         NativeLibrary.onGamePadMotionEvent( | ||||||
|  |             NativeLibrary.Player1Device, | ||||||
|  |             deltaTimestamp, | ||||||
|  |             gyro[0], | ||||||
|  |             gyro[1], | ||||||
|  |             gyro[2], | ||||||
|  |             accel[0], | ||||||
|  |             accel[1], | ||||||
|  |             accel[2] | ||||||
|  |         ) | ||||||
|  |         NativeLibrary.onGamePadMotionEvent( | ||||||
|  |             NativeLibrary.ConsoleDevice, | ||||||
|  |             deltaTimestamp, | ||||||
|  |             gyro[0], | ||||||
|  |             gyro[1], | ||||||
|  |             gyro[2], | ||||||
|  |             accel[0], | ||||||
|  |             accel[1], | ||||||
|  |             accel[2] | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun onAccuracyChanged(sensor: Sensor, i: Int) {} | ||||||
|  |     private fun addOverlayControls(orientation: String) { | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_a, | ||||||
|  |                     R.drawable.facebutton_a_depressed, | ||||||
|  |                     ButtonType.BUTTON_A, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_b, | ||||||
|  |                     R.drawable.facebutton_b_depressed, | ||||||
|  |                     ButtonType.BUTTON_B, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_x, | ||||||
|  |                     R.drawable.facebutton_x_depressed, | ||||||
|  |                     ButtonType.BUTTON_X, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_y, | ||||||
|  |                     R.drawable.facebutton_y_depressed, | ||||||
|  |                     ButtonType.BUTTON_Y, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.l_shoulder, | ||||||
|  |                     R.drawable.l_shoulder_depressed, | ||||||
|  |                     ButtonType.TRIGGER_L, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.r_shoulder, | ||||||
|  |                     R.drawable.r_shoulder_depressed, | ||||||
|  |                     ButtonType.TRIGGER_R, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.zl_trigger, | ||||||
|  |                     R.drawable.zl_trigger_depressed, | ||||||
|  |                     ButtonType.TRIGGER_ZL, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.zr_trigger, | ||||||
|  |                     R.drawable.zr_trigger_depressed, | ||||||
|  |                     ButtonType.TRIGGER_ZR, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_plus, | ||||||
|  |                     R.drawable.facebutton_plus_depressed, | ||||||
|  |                     ButtonType.BUTTON_PLUS, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_minus, | ||||||
|  |                     R.drawable.facebutton_minus_depressed, | ||||||
|  |                     ButtonType.BUTTON_MINUS, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) { | ||||||
|  |             overlayDpads.add( | ||||||
|  |                 initializeOverlayDpad( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.dpad_standard, | ||||||
|  |                     R.drawable.dpad_standard_cardinal_depressed, | ||||||
|  |                     R.drawable.dpad_standard_diagonal_depressed, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) { | ||||||
|  |             overlayJoysticks.add( | ||||||
|  |                 initializeOverlayJoystick( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.joystick_range, | ||||||
|  |                     R.drawable.joystick, | ||||||
|  |                     R.drawable.joystick_depressed, | ||||||
|  |                     StickType.STICK_L, | ||||||
|  |                     ButtonType.STICK_L, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) { | ||||||
|  |             overlayJoysticks.add( | ||||||
|  |                 initializeOverlayJoystick( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.joystick_range, | ||||||
|  |                     R.drawable.joystick, | ||||||
|  |                     R.drawable.joystick_depressed, | ||||||
|  |                     StickType.STICK_R, | ||||||
|  |                     ButtonType.STICK_R, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_home, | ||||||
|  |                     R.drawable.facebutton_home_depressed, | ||||||
|  |                     ButtonType.BUTTON_HOME, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |         if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) { | ||||||
|  |             overlayButtons.add( | ||||||
|  |                 initializeOverlayButton( | ||||||
|  |                     context, | ||||||
|  |                     R.drawable.facebutton_screenshot, | ||||||
|  |                     R.drawable.facebutton_screenshot_depressed, | ||||||
|  |                     ButtonType.BUTTON_CAPTURE, | ||||||
|  |                     orientation | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun refreshControls() { | ||||||
|  |         // Remove all the overlay buttons from the HashSet. | ||||||
|  |         overlayButtons.clear() | ||||||
|  |         overlayDpads.clear() | ||||||
|  |         overlayJoysticks.clear() | ||||||
|  |         val orientation = | ||||||
|  |             if (resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT) "-Portrait" else "" | ||||||
|  |  | ||||||
|  |         // Add all the enabled overlay items back to the HashSet. | ||||||
|  |         if (EmulationMenuSettings.showOverlay) { | ||||||
|  |             addOverlayControls(orientation) | ||||||
|  |         } | ||||||
|  |         invalidate() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) { | ||||||
|  |         PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() | ||||||
|  |             .putFloat("$sharedPrefsId$orientation-X", x.toFloat()) | ||||||
|  |             .putFloat("$sharedPrefsId$orientation-Y", y.toFloat()) | ||||||
|  |             .apply() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun setIsInEditMode(editMode: Boolean) { | ||||||
|  |         inEditMode = editMode | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun defaultOverlay() { | ||||||
|  |         if (!preferences.getBoolean(Settings.PREF_OVERLAY_INIT, false)) { | ||||||
|  |             defaultOverlayLandscape() | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         resetButtonPlacement() | ||||||
|  |         preferences.edit() | ||||||
|  |             .putBoolean(Settings.PREF_OVERLAY_INIT, true) | ||||||
|  |             .apply() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fun resetButtonPlacement() { | ||||||
|  |         defaultOverlayLandscape() | ||||||
|  |         refreshControls() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     private fun defaultOverlayLandscape() { | ||||||
|  |         // Get screen size | ||||||
|  |         val display = (context as Activity).windowManager.defaultDisplay | ||||||
|  |         val outMetrics = DisplayMetrics() | ||||||
|  |         display.getRealMetrics(outMetrics) | ||||||
|  |         var maxX = outMetrics.heightPixels.toFloat() | ||||||
|  |         var maxY = outMetrics.widthPixels.toFloat() | ||||||
|  |         // Height and width changes depending on orientation. Use the larger value for height. | ||||||
|  |         if (maxY > maxX) { | ||||||
|  |             val tmp = maxX | ||||||
|  |             maxX = maxY | ||||||
|  |             maxY = tmp | ||||||
|  |         } | ||||||
|  |         val res = resources | ||||||
|  |  | ||||||
|  |         // Each value is a percent from max X/Y stored as an int. Have to bring that value down | ||||||
|  |         // to a decimal before multiplying by MAX X/Y. | ||||||
|  |         preferences.edit() | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_A.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_A_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_A.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_A_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_B.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_B_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_B.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_B_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_X.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_X_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_X.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_X_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_Y.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_Y_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_Y.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_Y_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_ZL.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_ZL_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_ZL.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_ZL_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_ZR.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_ZR_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_ZR.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_ZR_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.DPAD_UP.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_DPAD_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.DPAD_UP.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_DPAD_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_L.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_L_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_L.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_L_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_R.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_R_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.TRIGGER_R.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_TRIGGER_R_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_PLUS.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_PLUS_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_PLUS.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_PLUS_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_MINUS.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_MINUS_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_MINUS.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_MINUS_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_HOME.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_HOME_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_HOME.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_HOME_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_CAPTURE.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.BUTTON_CAPTURE.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_BUTTON_CAPTURE_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.STICK_R.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_STICK_R_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.STICK_R.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_STICK_R_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.STICK_L.toString() + "-X", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_STICK_L_X).toFloat() / 1000 * maxX | ||||||
|  |             ) | ||||||
|  |             .putFloat( | ||||||
|  |                 ButtonType.STICK_L.toString() + "-Y", | ||||||
|  |                 res.getInteger(R.integer.SWITCH_STICK_L_Y).toFloat() / 1000 * maxY | ||||||
|  |             ) | ||||||
|  |             .commit() | ||||||
|  |         // We want to commit right away, otherwise the overlay could load before this is saved. | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     override fun isInEditMode(): Boolean { | ||||||
|  |         return inEditMode | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     companion object { | ||||||
|  |         /** | ||||||
|  |          * Resizes a [Bitmap] by a given scale factor | ||||||
|  |          * | ||||||
|  |          * @param vectorDrawable The {@link Bitmap} to scale. | ||||||
|  |          * @param scale          The scale factor for the bitmap. | ||||||
|  |          * @return The scaled [Bitmap] | ||||||
|  |          */ | ||||||
|  |         private fun getBitmap(vectorDrawable: VectorDrawable, scale: Float): Bitmap { | ||||||
|  |             val bitmap = Bitmap.createBitmap( | ||||||
|  |                 (vectorDrawable.intrinsicWidth * scale).toInt(), | ||||||
|  |                 (vectorDrawable.intrinsicHeight * scale).toInt(), | ||||||
|  |                 Bitmap.Config.ARGB_8888 | ||||||
|  |             ) | ||||||
|  |             val canvas = Canvas(bitmap) | ||||||
|  |             vectorDrawable.setBounds(0, 0, canvas.width, canvas.height) | ||||||
|  |             vectorDrawable.draw(canvas) | ||||||
|  |             return bitmap | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         private fun getBitmap(context: Context, drawableId: Int, scale: Float): Bitmap { | ||||||
|  |             return when (val drawable = ContextCompat.getDrawable(context, drawableId)) { | ||||||
|  |                 is BitmapDrawable -> BitmapFactory.decodeResource(context.resources, drawableId) | ||||||
|  |                 is VectorDrawable -> getBitmap(drawable, scale) | ||||||
|  |                 else -> throw IllegalArgumentException("Unsupported drawable type") | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Initializes an InputOverlayDrawableButton, given by resId, with all of the | ||||||
|  |          * parameters set for it to be properly shown on the InputOverlay. | ||||||
|  |          * | ||||||
|  |          * | ||||||
|  |          * This works due to the way the X and Y coordinates are stored within | ||||||
|  |          * the [SharedPreferences]. | ||||||
|  |          * | ||||||
|  |          * | ||||||
|  |          * In the input overlay configuration menu, | ||||||
|  |          * once a touch event begins and then ends (ie. Organizing the buttons to one's own liking for the overlay). | ||||||
|  |          * the X and Y coordinates of the button at the END of its touch event | ||||||
|  |          * (when you remove your finger/stylus from the touchscreen) are then stored | ||||||
|  |          * within a SharedPreferences instance so that those values can be retrieved here. | ||||||
|  |          * | ||||||
|  |          * | ||||||
|  |          * This has a few benefits over the conventional way of storing the values | ||||||
|  |          * (ie. within the yuzu ini file). | ||||||
|  |          * | ||||||
|  |          *  * No native calls | ||||||
|  |          *  * Keeps Android-only values inside the Android environment | ||||||
|  |          * | ||||||
|  |          * | ||||||
|  |          * | ||||||
|  |          * Technically no modifications should need to be performed on the returned | ||||||
|  |          * InputOverlayDrawableButton. Simply add it to the HashSet of overlay items and wait | ||||||
|  |          * for Android to call the onDraw method. | ||||||
|  |          * | ||||||
|  |          * @param context      The current [Context]. | ||||||
|  |          * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). | ||||||
|  |          * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). | ||||||
|  |          * @param buttonId     Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. | ||||||
|  |          * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. | ||||||
|  |          */ | ||||||
|  |         private fun initializeOverlayButton( | ||||||
|  |             context: Context, | ||||||
|  |             defaultResId: Int, | ||||||
|  |             pressedResId: Int, | ||||||
|  |             buttonId: Int, | ||||||
|  |             orientation: String | ||||||
|  |         ): InputOverlayDrawableButton { | ||||||
|  |             // Resources handle for fetching the initial Drawable resource. | ||||||
|  |             val res = context.resources | ||||||
|  |  | ||||||
|  |             // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. | ||||||
|  |             val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||||
|  |  | ||||||
|  |             // Decide scale based on button ID and user preference | ||||||
|  |             var scale: Float = when (buttonId) { | ||||||
|  |                 ButtonType.BUTTON_HOME, | ||||||
|  |                 ButtonType.BUTTON_CAPTURE, | ||||||
|  |                 ButtonType.BUTTON_PLUS, | ||||||
|  |                 ButtonType.BUTTON_MINUS -> 0.35f | ||||||
|  |                 ButtonType.TRIGGER_L, | ||||||
|  |                 ButtonType.TRIGGER_R, | ||||||
|  |                 ButtonType.TRIGGER_ZL, | ||||||
|  |                 ButtonType.TRIGGER_ZR -> 0.38f | ||||||
|  |                 else -> 0.43f | ||||||
|  |             } | ||||||
|  |             scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() | ||||||
|  |             scale /= 100f | ||||||
|  |  | ||||||
|  |             // Initialize the InputOverlayDrawableButton. | ||||||
|  |             val defaultStateBitmap = getBitmap(context, defaultResId, scale) | ||||||
|  |             val pressedStateBitmap = getBitmap(context, pressedResId, scale) | ||||||
|  |             val overlayDrawable = | ||||||
|  |                 InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) | ||||||
|  |  | ||||||
|  |             // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. | ||||||
|  |             // These were set in the input overlay configuration menu. | ||||||
|  |             val xKey = "$buttonId$orientation-X" | ||||||
|  |             val yKey = "$buttonId$orientation-Y" | ||||||
|  |             val drawableX = sPrefs.getFloat(xKey, 0f).toInt() | ||||||
|  |             val drawableY = sPrefs.getFloat(yKey, 0f).toInt() | ||||||
|  |             val width = overlayDrawable.width | ||||||
|  |             val height = overlayDrawable.height | ||||||
|  |  | ||||||
|  |             // Now set the bounds for the InputOverlayDrawableButton. | ||||||
|  |             // This will dictate where on the screen (and the what the size) the InputOverlayDrawableButton will be. | ||||||
|  |             overlayDrawable.setBounds( | ||||||
|  |                 drawableX - (width / 2), | ||||||
|  |                 drawableY - (height / 2), | ||||||
|  |                 drawableX + (width / 2), | ||||||
|  |                 drawableY + (height / 2) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             // Need to set the image's position | ||||||
|  |             overlayDrawable.setPosition( | ||||||
|  |                 drawableX - (width / 2), | ||||||
|  |                 drawableY - (height / 2) | ||||||
|  |             ) | ||||||
|  |             return overlayDrawable | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Initializes an [InputOverlayDrawableDpad] | ||||||
|  |          * | ||||||
|  |          * @param context                   The current [Context]. | ||||||
|  |          * @param defaultResId              The [Bitmap] resource ID of the default sate. | ||||||
|  |          * @param pressedOneDirectionResId  The [Bitmap] resource ID of the pressed sate in one direction. | ||||||
|  |          * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed sate in two directions. | ||||||
|  |          * @return the initialized [InputOverlayDrawableDpad] | ||||||
|  |          */ | ||||||
|  |         private fun initializeOverlayDpad( | ||||||
|  |             context: Context, | ||||||
|  |             defaultResId: Int, | ||||||
|  |             pressedOneDirectionResId: Int, | ||||||
|  |             pressedTwoDirectionsResId: Int, | ||||||
|  |             orientation: String | ||||||
|  |         ): InputOverlayDrawableDpad { | ||||||
|  |             // Resources handle for fetching the initial Drawable resource. | ||||||
|  |             val res = context.resources | ||||||
|  |  | ||||||
|  |             // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableDpad. | ||||||
|  |             val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||||
|  |  | ||||||
|  |             // Decide scale based on button ID and user preference | ||||||
|  |             var scale = 0.40f | ||||||
|  |             scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() | ||||||
|  |             scale /= 100f | ||||||
|  |  | ||||||
|  |             // Initialize the InputOverlayDrawableDpad. | ||||||
|  |             val defaultStateBitmap = | ||||||
|  |                 getBitmap(context, defaultResId, scale) | ||||||
|  |             val pressedOneDirectionStateBitmap = getBitmap(context, pressedOneDirectionResId, scale) | ||||||
|  |             val pressedTwoDirectionsStateBitmap = | ||||||
|  |                 getBitmap(context, pressedTwoDirectionsResId, scale) | ||||||
|  |  | ||||||
|  |             val overlayDrawable = InputOverlayDrawableDpad( | ||||||
|  |                 res, | ||||||
|  |                 defaultStateBitmap, | ||||||
|  |                 pressedOneDirectionStateBitmap, | ||||||
|  |                 pressedTwoDirectionsStateBitmap, | ||||||
|  |                 ButtonType.DPAD_UP, | ||||||
|  |                 ButtonType.DPAD_DOWN, | ||||||
|  |                 ButtonType.DPAD_LEFT, | ||||||
|  |                 ButtonType.DPAD_RIGHT | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. | ||||||
|  |             // These were set in the input overlay configuration menu. | ||||||
|  |             val drawableX = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-X", 0f).toInt() | ||||||
|  |             val drawableY = sPrefs.getFloat("${ButtonType.DPAD_UP}$orientation-Y", 0f).toInt() | ||||||
|  |             val width = overlayDrawable.width | ||||||
|  |             val height = overlayDrawable.height | ||||||
|  |  | ||||||
|  |             // Now set the bounds for the InputOverlayDrawableDpad. | ||||||
|  |             // This will dictate where on the screen (and the what the size) the InputOverlayDrawableDpad will be. | ||||||
|  |             overlayDrawable.setBounds( | ||||||
|  |                 drawableX - (width / 2), | ||||||
|  |                 drawableY - (height / 2), | ||||||
|  |                 drawableX + (width / 2), | ||||||
|  |                 drawableY + (height / 2) | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             // Need to set the image's position | ||||||
|  |             overlayDrawable.setPosition(drawableX - (width / 2), drawableY - (height / 2)) | ||||||
|  |             return overlayDrawable | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         /** | ||||||
|  |          * Initializes an [InputOverlayDrawableJoystick] | ||||||
|  |          * | ||||||
|  |          * @param context         The current [Context] | ||||||
|  |          * @param resOuter        Resource ID for the outer image of the joystick (the static image that shows the circular bounds). | ||||||
|  |          * @param defaultResInner Resource ID for the default inner image of the joystick (the one you actually move around). | ||||||
|  |          * @param pressedResInner Resource ID for the pressed inner image of the joystick. | ||||||
|  |          * @param joystick        Identifier for which joystick this is. | ||||||
|  |          * @param button          Identifier for which joystick button this is. | ||||||
|  |          * @return the initialized [InputOverlayDrawableJoystick]. | ||||||
|  |          */ | ||||||
|  |         private fun initializeOverlayJoystick( | ||||||
|  |             context: Context, | ||||||
|  |             resOuter: Int, | ||||||
|  |             defaultResInner: Int, | ||||||
|  |             pressedResInner: Int, | ||||||
|  |             joystick: Int, | ||||||
|  |             button: Int, | ||||||
|  |             orientation: String | ||||||
|  |         ): InputOverlayDrawableJoystick { | ||||||
|  |             // Resources handle for fetching the initial Drawable resource. | ||||||
|  |             val res = context.resources | ||||||
|  |  | ||||||
|  |             // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableJoystick. | ||||||
|  |             val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) | ||||||
|  |  | ||||||
|  |             // Decide scale based on user preference | ||||||
|  |             var scale = 0.40f | ||||||
|  |             scale *= (sPrefs.getInt(Settings.PREF_CONTROL_SCALE, 50) + 50).toFloat() | ||||||
|  |             scale /= 100f | ||||||
|  |  | ||||||
|  |             // Initialize the InputOverlayDrawableJoystick. | ||||||
|  |             val bitmapOuter = getBitmap(context, resOuter, scale) | ||||||
|  |             val bitmapInnerDefault = getBitmap(context, defaultResInner, 1.0f) | ||||||
|  |             val bitmapInnerPressed = getBitmap(context, pressedResInner, 1.0f) | ||||||
|  |  | ||||||
|  |             // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. | ||||||
|  |             // These were set in the input overlay configuration menu. | ||||||
|  |             val drawableX = sPrefs.getFloat("$button$orientation-X", 0f).toInt() | ||||||
|  |             val drawableY = sPrefs.getFloat("$button$orientation-Y", 0f).toInt() | ||||||
|  |             val outerScale = 1.66f | ||||||
|  |  | ||||||
|  |             // Now set the bounds for the InputOverlayDrawableJoystick. | ||||||
|  |             // This will dictate where on the screen (and the what the size) the InputOverlayDrawableJoystick will be. | ||||||
|  |             val outerSize = bitmapOuter.width | ||||||
|  |             val outerRect = Rect( | ||||||
|  |                 drawableX - (outerSize / 2), | ||||||
|  |                 drawableY - (outerSize / 2), | ||||||
|  |                 drawableX + (outerSize / 2), | ||||||
|  |                 drawableY + (outerSize / 2) | ||||||
|  |             ) | ||||||
|  |             val innerRect = | ||||||
|  |                 Rect(0, 0, (outerSize / outerScale).toInt(), (outerSize / outerScale).toInt()) | ||||||
|  |  | ||||||
|  |             // Send the drawableId to the joystick so it can be referenced when saving control position. | ||||||
|  |             val overlayDrawable = InputOverlayDrawableJoystick( | ||||||
|  |                 res, | ||||||
|  |                 bitmapOuter, | ||||||
|  |                 bitmapInnerDefault, | ||||||
|  |                 bitmapInnerPressed, | ||||||
|  |                 outerRect, | ||||||
|  |                 innerRect, | ||||||
|  |                 joystick, | ||||||
|  |                 button | ||||||
|  |             ) | ||||||
|  |  | ||||||
|  |             // Need to set the image's position | ||||||
|  |             overlayDrawable.setPosition(drawableX, drawableY) | ||||||
|  |             return overlayDrawable | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user