package eu.se_bastiaan.popcorntimeremote.widget;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import eu.se_bastiaan.popcorntimeremote.utils.PixelUtils;
public class JoystickView extends View {
// Constants
private final Handler mHandler = new Handler();
private final DisplayMetrics mMetrics = new DisplayMetrics();
private final double RAD = 57.2957795;
public enum Direction { CENTER, RIGHT, UP, LEFT, DOWN };
// Variables
private OnJoystickMoveListener mOnJoystickMoveListener;
private int mPositionX = 0;
private int mPositionY = 0;
private int mCenterX = 0;
private int mCenterY = 0;
private Paint mCirclePaint;
private Paint mButtonPaint;
private Paint mButtonImagePaint;
private Bitmap mCenterImage, mLeftImage, mRightImage, mUpImage, mDownImage;
private float mJoystickRadius;
private float mButtonRadius;
private float mOuterCircleRadius;
private int mLastAngle = 0;
private int mLastPower = 0;
private boolean mUserIsTouching = false;
private boolean mCalledOnce = false;
private boolean mDrawn = false;
private Context mContext;
private final Runnable mCallbackRunnable = new Runnable() {
@Override
public void run() {
mCalledOnce = true;
mOnJoystickMoveListener.onValueChanged(getAngle(), getPower(), getDirection());
mHandler.postDelayed(this, 500);
}
};
public JoystickView(Context context) {
super(context);
initJoystickView(context);
}
public JoystickView(Context context, AttributeSet attrs) {
super(context, attrs);
initJoystickView(context);
}
public JoystickView(Context context, AttributeSet attrs, int defaultStyle) {
super(context, attrs, defaultStyle);
initJoystickView(context);
}
protected void initJoystickView(Context context) {
mContext = context;
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(Color.parseColor("#444C53"));
mCirclePaint.setStyle(Paint.Style.STROKE);
mCirclePaint.setStrokeWidth(PixelUtils.getPixelsFromDp(mContext, 2));
mCirclePaint.setAntiAlias(true);
mButtonPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mButtonPaint.setColor(Color.parseColor("#1976D2"));
mButtonPaint.setStyle(Paint.Style.FILL);
mButtonPaint.setAntiAlias(true);
mButtonImagePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(mMetrics);
}
@Override
protected void onFinishInflate() {
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// setting the measured values to resize the view to a certain width and
// height
int widthPixels = mMetrics.widthPixels;
setMeasuredDimension(widthPixels, widthPixels);
int baseRadius = widthPixels / 2;
mButtonRadius = baseRadius * 0.1824074074f;
mJoystickRadius = baseRadius * 0.3009259259f;
mOuterCircleRadius = baseRadius * 0.7314814815f;
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
if(!mDrawn) {
mPositionX = mCenterX = getWidth() / 2;
mPositionY = mCenterY = getHeight() / 2;
mDrawn = true;
}
if(!mUserIsTouching) {
if(mCenterX != mPositionX) {
if(mCenterX > mPositionX) {
mPositionX = mPositionX + 8;
if(mPositionX > mCenterX) mPositionX = mCenterX;
} else {
mPositionX = mPositionX - 8;
if(mPositionX < mCenterX) mPositionX = mCenterX;
}
}
if(mCenterY != mPositionY) {
if(mCenterY > mPositionY) {
mPositionY = mPositionY + 8;
if(mPositionY > mCenterY) mPositionY = mCenterY;
} else {
mPositionY = mPositionY - 8;
if(mPositionY < mCenterY) mPositionY = mCenterY;
}
}
}
Float centerBetweenRadius = mJoystickRadius + (mOuterCircleRadius - mJoystickRadius) / 1.5f;
// painting the main circle
canvas.drawCircle(mCenterX, mCenterY, mJoystickRadius, mCirclePaint);
canvas.drawCircle(mCenterX, mCenterX, mOuterCircleRadius, mCirclePaint);
// painting the move button
if(mUserIsTouching) {
mButtonPaint.setShadowLayer(2.0f, 0.0f, 0.0f, 0x99000000);
} else {
mButtonPaint.setShadowLayer(8.0f, 0.0f, 0.0f, 0x99000000);
}
try {
if (mUpImage != null) {
canvas.drawBitmap(mUpImage, mCenterX - (mUpImage.getWidth() / 2), mCenterY - centerBetweenRadius - (mCenterImage.getHeight() / 2), mButtonImagePaint);
}
if (mDownImage != null) {
canvas.drawBitmap(mDownImage, mCenterX - (mDownImage.getWidth() / 2), mCenterY + centerBetweenRadius - (mCenterImage.getHeight() / 2), mButtonImagePaint);
}
if (mLeftImage != null) {
canvas.drawBitmap(mLeftImage, mCenterX - centerBetweenRadius - (mLeftImage.getWidth() / 2), mCenterY - (mRightImage.getHeight() / 2), mButtonImagePaint);
}
if (mRightImage != null) {
canvas.drawBitmap(mRightImage, mCenterX + centerBetweenRadius - (mRightImage.getWidth() / 2), mCenterY - (mRightImage.getHeight() / 2), mButtonImagePaint);
}
canvas.drawCircle(mPositionX, mPositionY, mButtonRadius, mButtonPaint);
if (mCenterImage != null) {
canvas.drawBitmap(mCenterImage, mPositionX - (mCenterImage.getWidth() / 2), mPositionY - (mCenterImage.getHeight() / 2), mButtonImagePaint);
}
} catch (Exception e) {
e.printStackTrace();
}
if(!mUserIsTouching && (mCenterX != mPositionX || mCenterY != mPositionY)) invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
double abs = Math.sqrt(Math.pow(event.getX() - mCenterX, 2) + Math.pow(event.getY() - mCenterY, 2));
boolean move = 100 * abs / mJoystickRadius > 30;
if(move) {
mPositionX = (int) event.getX();
mPositionY = (int) event.getY();
if (abs > mJoystickRadius) {
mPositionX = (int) ((mPositionX - mCenterX) * mJoystickRadius / abs + mCenterX);
mPositionY = (int) ((mPositionY - mCenterY) * mJoystickRadius / abs + mCenterY);
}
invalidate();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
mUserIsTouching = false;
mButtonPaint.setAlpha(255);
mHandler.removeCallbacksAndMessages(null);
invalidate();
if(mOnJoystickMoveListener != null && !mCalledOnce) {
mOnJoystickMoveListener.onValueChanged(getAngle(), getPower(), getDirection());
}
mCalledOnce = false;
}
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mUserIsTouching = true;
if(!move) mButtonPaint.setAlpha(190);
if(mOnJoystickMoveListener != null) {
mHandler.postDelayed(mCallbackRunnable, 200);
}
invalidate();
}
return true;
}
private int getAngle() {
if (mPositionX > mCenterX) {
if (mPositionY < mCenterY) {
return mLastAngle = (int) (Math.atan((mPositionY - mCenterY)
/ (mPositionX - mCenterX))
* RAD + 90);
} else if (mPositionY > mCenterY) {
return mLastAngle = (int) (Math.atan((mPositionY - mCenterY)
/ (mPositionX - mCenterX)) * RAD) + 90;
} else {
return mLastAngle = 90;
}
} else if (mPositionX < mCenterX) {
if (mPositionY < mCenterY) {
return mLastAngle = (int) (Math.atan((mPositionY - mCenterY)
/ (mPositionX - mCenterX))
* RAD - 90);
} else if (mPositionY > mCenterY) {
return mLastAngle = (int) (Math.atan((mPositionY - mCenterY)
/ (mPositionX - mCenterX)) * RAD) - 90;
} else {
return mLastAngle = -90;
}
} else {
if (mPositionY <= mCenterY) {
return mLastAngle = 0;
} else {
if (mLastAngle < 0) {
return mLastAngle = -180;
} else {
return mLastAngle = 180;
}
}
}
}
private int getPower() {
return mLastPower = (int) (100 * Math.sqrt(Math.pow(mPositionX - mCenterX, 2) + Math.pow(mPositionY - mCenterY, 2)) / mJoystickRadius);
}
private Direction getDirection() {
if (mLastPower == 0 && mLastAngle == 0) {
return Direction.CENTER;
}
int a = 0;
if (mLastAngle <= 0) {
a = (mLastAngle * -1) + 90;
} else if (mLastAngle > 0) {
if (mLastAngle <= 90) {
a = 90 - mLastAngle;
} else {
a = 360 - (mLastAngle - 90);
}
}
int direction = (a + 45) / 90 + 1;
if (direction > 4) {
direction = 1;
}
return Direction.values()[direction];
}
public void setOnJoystickMoveListener(OnJoystickMoveListener listener) {
mOnJoystickMoveListener = listener;
}
public void setJoystickImage(Direction direction, Integer drawableRes) {
Resources res = getResources();
Bitmap resource = BitmapFactory.decodeResource(res, drawableRes);
if(mDrawn) {
resource = Bitmap.createScaledBitmap(resource, getWidth() * (2 / 3), getHeight() * (2 / 3), false);
}
switch (direction) {
case CENTER:
mCenterImage = resource;
break;
case UP:
mUpImage = resource;
break;
case DOWN:
mDownImage = resource;
break;
case LEFT:
mLeftImage = resource;
break;
case RIGHT:
mRightImage = resource;
break;
default:
break;
}
}
public void setJoystickImage(Direction direction, Bitmap bitmap) {
if(bitmap != null) {
if(mDrawn) {
bitmap = Bitmap.createScaledBitmap(bitmap, getWidth() * (2 / 3), getHeight() * (2 / 3), false);
}
switch (direction) {
case CENTER:
mCenterImage = bitmap;
break;
case UP:
mUpImage = bitmap;
break;
case DOWN:
mDownImage = bitmap;
break;
case LEFT:
mLeftImage = bitmap;
break;
case RIGHT:
mRightImage = bitmap;
break;
default:
break;
}
} else {
mCenterImage = null;
}
}
public void setDirection(Direction direction) {
switch (direction) {
case DOWN:
mPositionX = mCenterX;
mPositionY = getWidth();
break;
case UP:
mPositionX = mCenterX;
mPositionY = 0;
break;
case LEFT:
mPositionY = mCenterY;
mPositionX = 0;
break;
case RIGHT:
mPositionY = mCenterY;
mPositionX = getWidth();
break;
default:
break;
}
double abs = Math.sqrt((mPositionX - mCenterX) * (mPositionX - mCenterX)
+ (mPositionY - mCenterY) * (mPositionY - mCenterY));
if (abs > mJoystickRadius) {
mPositionX = (int) ((mPositionX - mCenterX) * mJoystickRadius / abs + mCenterX);
mPositionY = (int) ((mPositionY - mCenterY) * mJoystickRadius / abs + mCenterY);
}
invalidate();
if(mOnJoystickMoveListener != null) mOnJoystickMoveListener.onValueChanged(getAngle(), getPower(), direction);
}
public static interface OnJoystickMoveListener {
public void onValueChanged(int angle, int power, Direction direction);
}
}