mirror of
https://git.suyu.dev/suyu/suyu
synced 2024-12-26 11:22:33 -06:00
android: Convert InputOverlay to Kotlin
This commit is contained in:
parent
096cdc57bb
commit
a1c57de466
2 changed files with 886 additions and 656 deletions
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue