From bd99b318e5dc269cde968bce4c68173d1460f7f2 Mon Sep 17 00:00:00 2001 From: Sean Maas Date: Wed, 21 Sep 2016 22:50:22 -0400 Subject: [PATCH] Android: Add on-screen dpad support --- .../dolphinemu/overlay/InputOverlay.java | 137 ++++++++++++++++++ .../overlay/InputOverlayDrawableDpad.java | 86 +++++++++++ 2 files changed, 223 insertions(+) create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java index d1159776d0..dea5b9214d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlay.java @@ -37,10 +37,12 @@ import java.util.Set; public final class InputOverlay extends SurfaceView implements OnTouchListener { private final Set overlayButtons = new HashSet<>(); + private final Set overlayDpads = new HashSet<>(); private final Set overlayJoysticks = new HashSet<>(); private boolean mIsInEditMode = false; private InputOverlayDrawableButton mButtonBeingConfigured; + private InputOverlayDrawableDpad mDpadBeingConfigured; private InputOverlayDrawableJoystick mJoystickBeingConfigured; /** @@ -84,6 +86,9 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener overlayButtons.add(initializeOverlayButton(context, R.drawable.gcpad_start, ButtonType.BUTTON_START)); overlayButtons.add(initializeOverlayButton(context, R.drawable.gcpad_l, ButtonType.TRIGGER_L)); overlayButtons.add(initializeOverlayButton(context, R.drawable.gcpad_r, ButtonType.TRIGGER_R)); + overlayDpads.add(initializeOverlayDpad(context, R.drawable.gcpad_dpad, + ButtonType.BUTTON_UP, ButtonType.BUTTON_DOWN, + ButtonType.BUTTON_LEFT, ButtonType.BUTTON_RIGHT)); overlayJoysticks.add(initializeOverlayJoystick(context, R.drawable.gcpad_joystick_range, R.drawable.gcpad_joystick, ButtonType.STICK_MAIN)); @@ -108,6 +113,11 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener button.draw(canvas); } + for (InputOverlayDrawableDpad dpad : overlayDpads) + { + dpad.draw(canvas); + } + for (InputOverlayDrawableJoystick joystick: overlayJoysticks) { joystick.draw(canvas); @@ -149,6 +159,48 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener } } + for (InputOverlayDrawableDpad dpad : overlayDpads) + { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + case MotionEvent.ACTION_MOVE: + // If a pointer enters the bounds of a button, press that button. + if (dpad.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex))) + { + if (dpad.getBounds().top + (dpad.getIntrinsicHeight() / 3) > (int)event.getY(pointerIndex)) + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(0), ButtonState.PRESSED); + } + if (dpad.getBounds().bottom - (dpad.getIntrinsicHeight() / 3) < (int)event.getY(pointerIndex)) + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(1), ButtonState.PRESSED); + } + if (dpad.getBounds().left + (dpad.getIntrinsicWidth() / 3) > (int)event.getX(pointerIndex)) + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(2), ButtonState.PRESSED); + } + if (dpad.getBounds().right - (dpad.getIntrinsicWidth() / 3) < (int)event.getX(pointerIndex)) + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(3), ButtonState.PRESSED); + } + } + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + // If a pointer ends, release the buttons. + if (dpad.getBounds().contains((int)event.getX(pointerIndex), (int)event.getY(pointerIndex))) + { + for(int i = 0; i < 4; i++) + { + NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, dpad.getId(i), ButtonState.RELEASED); + } + } + break; + } + } for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { @@ -209,6 +261,40 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener } } + for (InputOverlayDrawableDpad dpad : overlayDpads) + { + // Determine the button state to apply based on the MotionEvent action flag. + switch (event.getAction() & MotionEvent.ACTION_MASK) + { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + // If no button is being moved now, remember the currently touched button to move. + if (mButtonBeingConfigured == null && dpad.getBounds().contains(fingerPositionX, fingerPositionY)) + { + mDpadBeingConfigured = dpad; + mDpadBeingConfigured.onConfigureTouch(v, event); + } + break; + case MotionEvent.ACTION_MOVE: + if (mDpadBeingConfigured != null) + { + mDpadBeingConfigured.onConfigureTouch(v, event); + invalidate(); + return true; + } + break; + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_POINTER_UP: + if (mDpadBeingConfigured == dpad) + { + //Persist button position by saving new place. + saveControlPosition(mDpadBeingConfigured.getId(0), mDpadBeingConfigured.getBounds().left, mDpadBeingConfigured.getBounds().top); + mDpadBeingConfigured = null; + } + break; + } + } for (InputOverlayDrawableJoystick joystick : overlayJoysticks) { @@ -341,6 +427,57 @@ public final class InputOverlay extends SurfaceView implements OnTouchListener return overlayDrawable; } + /** + * Initializes an {@link InputOverlayDrawableDpad} + * + * @param context The current {@link Context}. + * @param resId The resource ID of the {@link Drawable} to get the {@link Bitmap} of. + * @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 resId, + int buttonUp, int buttonDown, + int buttonLeft, int buttonRight) + { + // 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); + + // Initialize the InputOverlayDrawableDpad. + float overlaySize = sPrefs.getInt("controls_size", 20); + overlaySize += 30; + overlaySize /= 50; + final Bitmap bitmap = resizeBitmap(context, BitmapFactory.decodeResource(res, resId), 0.30f * overlaySize); + final InputOverlayDrawableDpad overlayDrawable = new InputOverlayDrawableDpad(res, bitmap, + 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+"-X", 0f); + int drawableY = (int) sPrefs.getFloat(buttonUp+"-Y", 0f); + + // Intrinsic width and height of the InputOverlayDrawableDpad. + // For any who may not know, intrinsic width/height + // are the original unmodified width and height of the image. + int intrinWidth = overlayDrawable.getIntrinsicWidth(); + int intrinHeight = overlayDrawable.getIntrinsicHeight(); + + // 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, drawableY, drawableX+intrinWidth, drawableY+intrinHeight); + + // Need to set the image's position + overlayDrawable.setPosition(drawableX, drawableY); + + return overlayDrawable; + } + /** * Initializes an {@link InputOverlayDrawableJoystick} * diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java new file mode 100644 index 0000000000..7c1584e529 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/overlay/InputOverlayDrawableDpad.java @@ -0,0 +1,86 @@ +/** + * Copyright 2016 Dolphin Emulator Project + * Licensed under GPLv2+ + * Refer to the license.txt file included. + */ + +package org.dolphinemu.dolphinemu.overlay; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; +import android.view.MotionEvent; +import android.view.View; + +/** + * Custom {@link BitmapDrawable} that is capable + * of storing it's own ID. + */ +public final class InputOverlayDrawableDpad extends BitmapDrawable +{ + // The ID identifying what type of button this Drawable represents. + private int[] mButtonType = new int[4]; + private int mPreviousTouchX, mPreviousTouchY; + private int mControlPositionX, mControlPositionY; + + /** + * Constructor + * + * @param res {@link Resources} instance. + * @param bitmap {@link Bitmap} to use with this Drawable. + * @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. + */ + public InputOverlayDrawableDpad(Resources res, Bitmap bitmap, + int buttonUp, int buttonDown, + int buttonLeft, int buttonRight) + { + super(res, bitmap); + mButtonType[0] = buttonUp; + mButtonType[1] = buttonDown; + mButtonType[2] = buttonLeft; + mButtonType[3] = buttonRight; + } + + /** + * Gets one of the InputOverlayDrawableDpad's button IDs. + * + * @return the requested InputOverlayDrawableDpad's button ID. + */ + public int getId(int direction) + { + return mButtonType[direction]; + } + + public boolean onConfigureTouch(View v, MotionEvent event) + { + int pointerIndex = event.getActionIndex(); + int fingerPositionX = (int)event.getX(pointerIndex); + int fingerPositionY = (int)event.getY(pointerIndex); + switch (event.getAction()) + { + case MotionEvent.ACTION_DOWN: + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + case MotionEvent.ACTION_MOVE: + mControlPositionX += fingerPositionX - mPreviousTouchX; + mControlPositionY += fingerPositionY - mPreviousTouchY; + setBounds(new Rect(mControlPositionX, mControlPositionY, getBitmap().getWidth() + mControlPositionX, getBitmap().getHeight() + mControlPositionY)); + mPreviousTouchX = fingerPositionX; + mPreviousTouchY = fingerPositionY; + break; + + } + return true; + } + + public void setPosition(int x, int y) + { + mControlPositionX = x; + mControlPositionY = y; + } +}