committed by
bunnei
2 changed files with 886 additions and 656 deletions
-
656src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.java
-
886src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt
@ -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 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue