/** * Copyright 2013 Dolphin Emulator Project * Licensed under GPLv2+ * Refer to the license.txt file included. */ package org.citra.citra_emu.overlay; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.view.MotionEvent; import org.citra.citra_emu.NativeLibrary.ButtonType; import org.citra.citra_emu.utils.EmulationMenuSettings; /** * Custom {@link BitmapDrawable} that is capable * of storing it's own ID. */ public final class InputOverlayDrawableJoystick { private final int[] axisIDs = {0, 0, 0, 0}; private final float[] axises = {0f, 0f}; private int trackId = -1; private int mJoystickType; private int mControlPositionX, mControlPositionY; private int mPreviousTouchX, mPreviousTouchY; private int mWidth; private int mHeight; private Rect mVirtBounds; private Rect mOrigBounds; private BitmapDrawable mOuterBitmap; private BitmapDrawable mDefaultStateInnerBitmap; private BitmapDrawable mPressedStateInnerBitmap; private BitmapDrawable mBoundsBoxBitmap; private boolean mPressedState = false; /** * Constructor * * @param res {@link Resources} instance. * @param bitmapOuter {@link Bitmap} which represents the outer non-movable part of the joystick. * @param bitmapInnerDefault {@link Bitmap} which represents the default inner movable part of the joystick. * @param bitmapInnerPressed {@link Bitmap} which represents the pressed inner movable part of the joystick. * @param rectOuter {@link Rect} which represents the outer joystick bounds. * @param rectInner {@link Rect} which represents the inner joystick bounds. * @param joystick Identifier for which joystick this is. */ public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bitmapInnerDefault, Bitmap bitmapInnerPressed, Rect rectOuter, Rect rectInner, int joystick) { axisIDs[0] = joystick + 1; // Up axisIDs[1] = joystick + 2; // Down axisIDs[2] = joystick + 3; // Left axisIDs[3] = joystick + 4; // Right mJoystickType = joystick; mOuterBitmap = new BitmapDrawable(res, bitmapOuter); mDefaultStateInnerBitmap = new BitmapDrawable(res, bitmapInnerDefault); mPressedStateInnerBitmap = new BitmapDrawable(res, bitmapInnerPressed); mBoundsBoxBitmap = new BitmapDrawable(res, bitmapOuter); mWidth = bitmapOuter.getWidth(); mHeight = bitmapOuter.getHeight(); setBounds(rectOuter); mDefaultStateInnerBitmap.setBounds(rectInner); mPressedStateInnerBitmap.setBounds(rectInner); mVirtBounds = getBounds(); mOrigBounds = mOuterBitmap.copyBounds(); mBoundsBoxBitmap.setAlpha(0); mBoundsBoxBitmap.setBounds(getVirtBounds()); SetInnerBounds(); } /** * Gets this InputOverlayDrawableJoystick's button ID. * * @return this InputOverlayDrawableJoystick's button ID. */ public int getId() { return mJoystickType; } public void draw(Canvas canvas) { mOuterBitmap.draw(canvas); getCurrentStateBitmapDrawable().draw(canvas); mBoundsBoxBitmap.draw(canvas); } public void TrackEvent(MotionEvent event) { int pointerIndex = event.getActionIndex(); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_POINTER_DOWN: if (getBounds().contains((int) event.getX(pointerIndex), (int) event.getY(pointerIndex))) { mPressedState = true; mOuterBitmap.setAlpha(0); mBoundsBoxBitmap.setAlpha(255); if (EmulationMenuSettings.getJoystickRelCenter()) { getVirtBounds().offset((int) event.getX(pointerIndex) - getVirtBounds().centerX(), (int) event.getY(pointerIndex) - getVirtBounds().centerY()); } mBoundsBoxBitmap.setBounds(getVirtBounds()); trackId = event.getPointerId(pointerIndex); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_POINTER_UP: if (trackId == event.getPointerId(pointerIndex)) { mPressedState = false; axises[0] = axises[1] = 0.0f; mOuterBitmap.setAlpha(255); mBoundsBoxBitmap.setAlpha(0); setVirtBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, mOrigBounds.bottom)); setBounds(new Rect(mOrigBounds.left, mOrigBounds.top, mOrigBounds.right, mOrigBounds.bottom)); SetInnerBounds(); trackId = -1; } break; } if (trackId == -1) return; for (int i = 0; i < event.getPointerCount(); i++) { if (trackId == event.getPointerId(i)) { float touchX = event.getX(i); float touchY = event.getY(i); float maxY = getVirtBounds().bottom; float maxX = getVirtBounds().right; touchX -= getVirtBounds().centerX(); maxX -= getVirtBounds().centerX(); touchY -= getVirtBounds().centerY(); maxY -= getVirtBounds().centerY(); final float AxisX = touchX / maxX; final float AxisY = touchY / maxY; // Clamp the circle pad input to a circle final float angle = (float) Math.atan2(AxisY, AxisX); float radius = (float) Math.sqrt(AxisX * AxisX + AxisY * AxisY); if(radius > 1.0f) { radius = 1.0f; } axises[0] = ((float)Math.cos(angle) * radius); axises[1] = ((float)Math.sin(angle) * radius); SetInnerBounds(); } } } public boolean onConfigureTouch(MotionEvent event) { int pointerIndex = event.getActionIndex(); int fingerPositionX = (int) event.getX(pointerIndex); int fingerPositionY = (int) event.getY(pointerIndex); int scale = 1; if (mJoystickType == ButtonType.STICK_C) { // C-stick is scaled down to be half the size of the circle pad scale = 2; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mPreviousTouchX = fingerPositionX; mPreviousTouchY = fingerPositionY; break; case MotionEvent.ACTION_MOVE: int deltaX = fingerPositionX - mPreviousTouchX; int deltaY = fingerPositionY - mPreviousTouchY; mControlPositionX += deltaX; mControlPositionY += deltaY; setBounds(new Rect(mControlPositionX, mControlPositionY, mOuterBitmap.getIntrinsicWidth() / scale + mControlPositionX, mOuterBitmap.getIntrinsicHeight() / scale + mControlPositionY)); setVirtBounds(new Rect(mControlPositionX, mControlPositionY, mOuterBitmap.getIntrinsicWidth() / scale + mControlPositionX, mOuterBitmap.getIntrinsicHeight() / scale + mControlPositionY)); SetInnerBounds(); setOrigBounds(new Rect(new Rect(mControlPositionX, mControlPositionY, mOuterBitmap.getIntrinsicWidth() / scale + mControlPositionX, mOuterBitmap.getIntrinsicHeight() / scale + mControlPositionY))); mPreviousTouchX = fingerPositionX; mPreviousTouchY = fingerPositionY; break; } return true; } public float[] getAxisValues() { return axises; } public int[] getAxisIDs() { return axisIDs; } private void SetInnerBounds() { int X = getVirtBounds().centerX() + (int) ((axises[0]) * (getVirtBounds().width() / 2)); int Y = getVirtBounds().centerY() + (int) ((axises[1]) * (getVirtBounds().height() / 2)); if (mJoystickType == ButtonType.STICK_LEFT) { X += 1; Y += 1; } if (X > getVirtBounds().centerX() + (getVirtBounds().width() / 2)) X = getVirtBounds().centerX() + (getVirtBounds().width() / 2); if (X < getVirtBounds().centerX() - (getVirtBounds().width() / 2)) X = getVirtBounds().centerX() - (getVirtBounds().width() / 2); if (Y > getVirtBounds().centerY() + (getVirtBounds().height() / 2)) Y = getVirtBounds().centerY() + (getVirtBounds().height() / 2); if (Y < getVirtBounds().centerY() - (getVirtBounds().height() / 2)) Y = getVirtBounds().centerY() - (getVirtBounds().height() / 2); int width = mPressedStateInnerBitmap.getBounds().width() / 2; int height = mPressedStateInnerBitmap.getBounds().height() / 2; mDefaultStateInnerBitmap.setBounds(X - width, Y - height, X + width, Y + height); mPressedStateInnerBitmap.setBounds(mDefaultStateInnerBitmap.getBounds()); } public void setPosition(int x, int y) { mControlPositionX = x; mControlPositionY = y; } private BitmapDrawable getCurrentStateBitmapDrawable() { return mPressedState ? mPressedStateInnerBitmap : mDefaultStateInnerBitmap; } public Rect getBounds() { return mOuterBitmap.getBounds(); } public void setBounds(Rect bounds) { mOuterBitmap.setBounds(bounds); } private void setOrigBounds(Rect bounds) { mOrigBounds = bounds; } private Rect getVirtBounds() { return mVirtBounds; } private void setVirtBounds(Rect bounds) { mVirtBounds = bounds; } public int getWidth() { return mWidth; } public int getHeight() { return mHeight; } }