From b10f5d1c85e1a95db7b99edde8b40e79ee5628f7 Mon Sep 17 00:00:00 2001 From: Ricky Barrette Date: Tue, 11 Dec 2012 11:47:12 -0500 Subject: [PATCH] Added Joy stick classes and resources Signed-off-by: Ricky Barrette --- IOIOTruck/.classpath | 1 - IOIOTruck/project.properties | 4 +- IOIOTruck/res/layout/dualjoystick.xml | 37 ++ IOIOTruck/res/layout/joystick.xml | 28 + IOIOTruck/res/values/attr.xml | 17 + IOIOTruck/res/values/dimen.xml | 19 + .../Widgets/Joystick/DualJoystickView.java | 156 +++++ .../Joystick/JoystickClickedListener.java | 7 + .../Joystick/JoystickMovedListener.java | 9 + .../Widgets/Joystick/JoystickView.java | 542 ++++++++++++++++++ 10 files changed, 817 insertions(+), 3 deletions(-) create mode 100644 IOIOTruck/res/layout/dualjoystick.xml create mode 100644 IOIOTruck/res/layout/joystick.xml create mode 100644 IOIOTruck/res/values/attr.xml create mode 100644 IOIOTruck/res/values/dimen.xml create mode 100644 IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/DualJoystickView.java create mode 100644 IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickClickedListener.java create mode 100644 IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickMovedListener.java create mode 100644 IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickView.java diff --git a/IOIOTruck/.classpath b/IOIOTruck/.classpath index 50759ed..a4763d1 100644 --- a/IOIOTruck/.classpath +++ b/IOIOTruck/.classpath @@ -4,6 +4,5 @@ - diff --git a/IOIOTruck/project.properties b/IOIOTruck/project.properties index 9cfacd3..515507a 100644 --- a/IOIOTruck/project.properties +++ b/IOIOTruck/project.properties @@ -9,6 +9,6 @@ # Project target. target=Google Inc.:Google APIs:17 -android.library.reference.3=../ioiolib/IOIOLib android.library.reference.1=../../exception_handler_library/ExceptionHandlerLib -android.library.reference.2=../../location_library/LocationLib +android.library.reference.2=../ioiolib/IOIOLib +android.library.reference.3=../../location_library/LocationLib diff --git a/IOIOTruck/res/layout/dualjoystick.xml b/IOIOTruck/res/layout/dualjoystick.xml new file mode 100644 index 0000000..3bbaba5 --- /dev/null +++ b/IOIOTruck/res/layout/dualjoystick.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IOIOTruck/res/layout/joystick.xml b/IOIOTruck/res/layout/joystick.xml new file mode 100644 index 0000000..f0e7121 --- /dev/null +++ b/IOIOTruck/res/layout/joystick.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/IOIOTruck/res/values/attr.xml b/IOIOTruck/res/values/attr.xml new file mode 100644 index 0000000..bd0438c --- /dev/null +++ b/IOIOTruck/res/values/attr.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/IOIOTruck/res/values/dimen.xml b/IOIOTruck/res/values/dimen.xml new file mode 100644 index 0000000..6e5533b --- /dev/null +++ b/IOIOTruck/res/values/dimen.xml @@ -0,0 +1,19 @@ + + + + + + 66dip + + 132dip + \ No newline at end of file diff --git a/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/DualJoystickView.java b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/DualJoystickView.java new file mode 100644 index 0000000..f35cbb5 --- /dev/null +++ b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/DualJoystickView.java @@ -0,0 +1,156 @@ +package com.MobileAnarchy.Android.Widgets.Joystick; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; + +public class DualJoystickView extends LinearLayout { + @SuppressWarnings("unused") + private static final String TAG = DualJoystickView.class.getSimpleName(); + + private final boolean D = false; + private Paint dbgPaint1; + + private JoystickView stickL; + private JoystickView stickR; + + private View pad; + + public DualJoystickView(Context context) { + super(context); + stickL = new JoystickView(context); + stickR = new JoystickView(context); + initDualJoystickView(); + } + + public DualJoystickView(Context context, AttributeSet attrs) { + super(context, attrs); + stickL = new JoystickView(context, attrs); + stickR = new JoystickView(context, attrs); + initDualJoystickView(); + } + + private void initDualJoystickView() { + setOrientation(LinearLayout.HORIZONTAL); + + if (D) { + dbgPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG); + dbgPaint1.setColor(Color.CYAN); + dbgPaint1.setStrokeWidth(1); + dbgPaint1.setStyle(Paint.Style.STROKE); + } + + pad = new View(getContext()); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + removeView(stickL); + removeView(stickR); + + float padW = getMeasuredWidth() - (getMeasuredHeight() * 2); + int joyWidth = (int) ((getMeasuredWidth() - padW) / 2); + LayoutParams joyLParams = new LayoutParams(joyWidth, + getMeasuredHeight()); + + stickL.setLayoutParams(joyLParams); + stickR.setLayoutParams(joyLParams); + + stickL.TAG = "L"; + stickR.TAG = "R"; + stickL.setPointerId(JoystickView.INVALID_POINTER_ID); + stickR.setPointerId(JoystickView.INVALID_POINTER_ID); + + addView(stickL); + + ViewGroup.LayoutParams padLParams = new ViewGroup.LayoutParams( + (int) padW, getMeasuredHeight()); + removeView(pad); + pad.setLayoutParams(padLParams); + addView(pad); + + addView(stickR); + } + + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) { + super.onLayout(changed, l, t, r, b); + stickR.setTouchOffset(stickR.getLeft(), stickR.getTop()); + } + + public void setAutoReturnToCenter(boolean left, boolean right) { + stickL.setAutoReturnToCenter(left); + stickR.setAutoReturnToCenter(right); + } + + public void setOnJostickMovedListener(JoystickMovedListener left, + JoystickMovedListener right) { + stickL.setOnJostickMovedListener(left); + stickR.setOnJostickMovedListener(right); + } + + public void setOnJostickClickedListener(JoystickClickedListener left, + JoystickClickedListener right) { + stickL.setOnJostickClickedListener(left); + stickR.setOnJostickClickedListener(right); + } + + public void setYAxisInverted(boolean leftYAxisInverted, + boolean rightYAxisInverted) { + stickL.setYAxisInverted(leftYAxisInverted); + stickL.setYAxisInverted(rightYAxisInverted); + } + + public void setMovementConstraint(int movementConstraint) { + stickL.setMovementConstraint(movementConstraint); + stickR.setMovementConstraint(movementConstraint); + } + + public void setMovementRange(float movementRangeLeft, + float movementRangeRight) { + stickL.setMovementRange(movementRangeLeft); + stickR.setMovementRange(movementRangeRight); + } + + public void setMoveResolution(float leftMoveResolution, + float rightMoveResolution) { + stickL.setMoveResolution(leftMoveResolution); + stickR.setMoveResolution(rightMoveResolution); + } + + public void setUserCoordinateSystem(int leftCoordinateSystem, + int rightCoordinateSystem) { + stickL.setUserCoordinateSystem(leftCoordinateSystem); + stickR.setUserCoordinateSystem(rightCoordinateSystem); + } + + @Override + protected void dispatchDraw(Canvas canvas) { + super.dispatchDraw(canvas); + if (D) { + canvas.drawRect(1, 1, getMeasuredWidth() - 1, + getMeasuredHeight() - 1, dbgPaint1); + } + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + boolean l = stickL.dispatchTouchEvent(ev); + boolean r = stickR.dispatchTouchEvent(ev); + return l || r; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + boolean l = stickL.onTouchEvent(ev); + boolean r = stickR.onTouchEvent(ev); + return l || r; + } +} diff --git a/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickClickedListener.java b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickClickedListener.java new file mode 100644 index 0000000..abf013f --- /dev/null +++ b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickClickedListener.java @@ -0,0 +1,7 @@ +package com.MobileAnarchy.Android.Widgets.Joystick; + +public interface JoystickClickedListener { + public void OnClicked(); + + public void OnReleased(); +} diff --git a/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickMovedListener.java b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickMovedListener.java new file mode 100644 index 0000000..6d3a2d5 --- /dev/null +++ b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickMovedListener.java @@ -0,0 +1,9 @@ +package com.MobileAnarchy.Android.Widgets.Joystick; + +public interface JoystickMovedListener { + public void OnMoved(int pan, int tilt); + + public void OnReleased(); + + public void OnReturnedToCenter(); +} diff --git a/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickView.java b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickView.java new file mode 100644 index 0000000..b5a4e57 --- /dev/null +++ b/IOIOTruck/src/com/MobileAnarchy/Android/Widgets/Joystick/JoystickView.java @@ -0,0 +1,542 @@ +package com.MobileAnarchy.Android.Widgets.Joystick; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.util.AttributeSet; +import android.util.Log; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; + +public class JoystickView extends View { + public static final int INVALID_POINTER_ID = -1; + + // ========================================= + // Private Members + // ========================================= + private final boolean D = false; + String TAG = "JoystickView"; + + private Paint dbgPaint1; + private Paint dbgPaint2; + + private Paint bgPaint; + private Paint handlePaint; + + private int innerPadding; + private int bgRadius; + private int handleRadius; + private int movementRadius; + private int handleInnerBoundaries; + + private JoystickMovedListener moveListener; + private JoystickClickedListener clickListener; + + // # of pixels movement required between reporting to the listener + private float moveResolution; + + private boolean yAxisInverted; + private boolean autoReturnToCenter; + + // Max range of movement in user coordinate system + public final static int CONSTRAIN_BOX = 0; + public final static int CONSTRAIN_CIRCLE = 1; + private int movementConstraint; + private float movementRange; + + public final static int COORDINATE_CARTESIAN = 0; // Regular cartesian + // coordinates + public final static int COORDINATE_DIFFERENTIAL = 1; // Uses polar rotation + // of 45 degrees to + // calc differential + // drive paramaters + private int userCoordinateSystem; + + // Records touch pressure for click handling + private float touchPressure; + private boolean clicked; + private float clickThreshold; + + // Last touch point in view coordinates + private int pointerId = INVALID_POINTER_ID; + private float touchX, touchY; + + // Last reported position in view coordinates (allows different reporting + // sensitivities) + private float reportX, reportY; + + // Handle center in view coordinates + private float handleX, handleY; + + // Center of the view in view coordinates + private int cX, cY; + + // Size of the view in view coordinates + private int dimX; + + // Cartesian coordinates of last touch point - joystick center is (0,0) + private int cartX, cartY; + + // Polar coordinates of the touch point from joystick center + private double radial; + private double angle; + + // User coordinates of last touch point + private int userX, userY; + + // Offset co-ordinates (used when touch events are received from parent's + // coordinate origin) + private int offsetX; + private int offsetY; + + // ========================================= + // Constructors + // ========================================= + + public JoystickView(Context context) { + super(context); + initJoystickView(); + } + + public JoystickView(Context context, AttributeSet attrs) { + super(context, attrs); + initJoystickView(); + } + + public JoystickView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + initJoystickView(); + } + + // ========================================= + // Initialization + // ========================================= + + private void initJoystickView() { + setFocusable(true); + + dbgPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG); + dbgPaint1.setColor(Color.RED); + dbgPaint1.setStrokeWidth(1); + dbgPaint1.setStyle(Paint.Style.STROKE); + + dbgPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG); + dbgPaint2.setColor(Color.GREEN); + dbgPaint2.setStrokeWidth(1); + dbgPaint2.setStyle(Paint.Style.STROKE); + + bgPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + bgPaint.setColor(Color.GRAY); + bgPaint.setStrokeWidth(1); + bgPaint.setAlpha(80); + bgPaint.setStyle(Paint.Style.FILL_AND_STROKE); + + handlePaint = new Paint(Paint.ANTI_ALIAS_FLAG); + handlePaint.setColor(Color.DKGRAY); + handlePaint.setStrokeWidth(1); + handlePaint.setStyle(Paint.Style.FILL_AND_STROKE); + + innerPadding = 10; + + setMovementRange(10); + setMoveResolution(1.0f); + setClickThreshold(0.4f); + setYAxisInverted(true); + setUserCoordinateSystem(COORDINATE_CARTESIAN); + setAutoReturnToCenter(true); + } + + public void setAutoReturnToCenter(boolean autoReturnToCenter) { + this.autoReturnToCenter = autoReturnToCenter; + } + + public boolean isAutoReturnToCenter() { + return autoReturnToCenter; + } + + public void setUserCoordinateSystem(int userCoordinateSystem) { + if (userCoordinateSystem < COORDINATE_CARTESIAN + || movementConstraint > COORDINATE_DIFFERENTIAL) + Log.e(TAG, "invalid value for userCoordinateSystem"); + else + this.userCoordinateSystem = userCoordinateSystem; + } + + public int getUserCoordinateSystem() { + return userCoordinateSystem; + } + + public void setMovementConstraint(int movementConstraint) { + if (movementConstraint < CONSTRAIN_BOX + || movementConstraint > CONSTRAIN_CIRCLE) + Log.e(TAG, "invalid value for movementConstraint"); + else + this.movementConstraint = movementConstraint; + } + + public int getMovementConstraint() { + return movementConstraint; + } + + public boolean isYAxisInverted() { + return yAxisInverted; + } + + public void setYAxisInverted(boolean yAxisInverted) { + this.yAxisInverted = yAxisInverted; + } + + /** + * Set the pressure sensitivity for registering a click + * + * @param clickThreshold + * threshold 0...1.0f inclusive. 0 will cause clicks to never be + * reported, 1.0 is a very hard click + */ + public void setClickThreshold(float clickThreshold) { + if (clickThreshold < 0 || clickThreshold > 1.0f) + Log.e(TAG, "clickThreshold must range from 0...1.0f inclusive"); + else + this.clickThreshold = clickThreshold; + } + + public float getClickThreshold() { + return clickThreshold; + } + + public void setMovementRange(float movementRange) { + this.movementRange = movementRange; + } + + public float getMovementRange() { + return movementRange; + } + + public void setMoveResolution(float moveResolution) { + this.moveResolution = moveResolution; + } + + public float getMoveResolution() { + return moveResolution; + } + + // ========================================= + // Public Methods + // ========================================= + + public void setOnJostickMovedListener(JoystickMovedListener listener) { + this.moveListener = listener; + } + + public void setOnJostickClickedListener(JoystickClickedListener listener) { + this.clickListener = listener; + } + + // ========================================= + // Drawing Functionality + // ========================================= + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + // Here we make sure that we have a perfect circle + int measuredWidth = measure(widthMeasureSpec); + int measuredHeight = measure(heightMeasureSpec); + setMeasuredDimension(measuredWidth, measuredHeight); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, + int bottom) { + super.onLayout(changed, left, top, right, bottom); + + int d = Math.min(getMeasuredWidth(), getMeasuredHeight()); + + dimX = d; + // dimY = d; + + cX = d / 2; + cY = d / 2; + + bgRadius = dimX / 2 - innerPadding; + handleRadius = (int) (d * 0.25); + handleInnerBoundaries = handleRadius; + movementRadius = Math.min(cX, cY) - handleInnerBoundaries; + } + + private int measure(int measureSpec) { + int result = 0; + // Decode the measurement specifications. + int specMode = MeasureSpec.getMode(measureSpec); + int specSize = MeasureSpec.getSize(measureSpec); + if (specMode == MeasureSpec.UNSPECIFIED) { + // Return a default size of 200 if no bounds are specified. + result = 200; + } else { + // As you want to fill the available space + // always return the full available bounds. + result = specSize; + } + return result; + } + + @Override + protected void onDraw(Canvas canvas) { + canvas.save(); + // Draw the background + canvas.drawCircle(cX, cY, bgRadius, bgPaint); + + // Draw the handle + handleX = touchX + cX; + handleY = touchY + cY; + canvas.drawCircle(handleX, handleY, handleRadius, handlePaint); + + if (D) { + canvas.drawRect(1, 1, getMeasuredWidth() - 1, + getMeasuredHeight() - 1, dbgPaint1); + + canvas.drawCircle(handleX, handleY, 3, dbgPaint1); + + if (movementConstraint == CONSTRAIN_CIRCLE) { + canvas.drawCircle(cX, cY, this.movementRadius, dbgPaint1); + } else { + canvas.drawRect(cX - movementRadius, cY - movementRadius, cX + + movementRadius, cY + movementRadius, dbgPaint1); + } + + // Origin to touch point + canvas.drawLine(cX, cY, handleX, handleY, dbgPaint2); + + int baseY = (int) (touchY < 0 ? cY + handleRadius : cY + - handleRadius); + canvas.drawText( + String.format("%s (%.0f,%.0f)", TAG, touchX, touchY), + handleX - 20, baseY - 7, dbgPaint2); + canvas.drawText( + "(" + + String.format("%.0f, %.1f", radial, + angle * 57.2957795) + (char) 0x00B0 + ")", + handleX - 20, baseY + 15, dbgPaint2); + } + + // Log.d(TAG, String.format("touch(%f,%f)", touchX, touchY)); + // Log.d(TAG, String.format("onDraw(%.1f,%.1f)\n\n", handleX, handleY)); + canvas.restore(); + } + + // Constrain touch within a box + private void constrainBox() { + touchX = Math.max(Math.min(touchX, movementRadius), -movementRadius); + touchY = Math.max(Math.min(touchY, movementRadius), -movementRadius); + } + + // Constrain touch within a circle + private void constrainCircle() { + float diffX = touchX; + float diffY = touchY; + double radial = Math.sqrt((diffX * diffX) + (diffY * diffY)); + if (radial > movementRadius) { + touchX = (int) ((diffX / radial) * movementRadius); + touchY = (int) ((diffY / radial) * movementRadius); + } + } + + public void setPointerId(int id) { + this.pointerId = id; + } + + public int getPointerId() { + return pointerId; + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); + switch (action & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_MOVE: { + return processMoveEvent(ev); + } + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: { + if (pointerId != INVALID_POINTER_ID) { + // Log.d(TAG, "ACTION_UP"); + returnHandleToCenter(); + setPointerId(INVALID_POINTER_ID); + } + break; + } + case MotionEvent.ACTION_POINTER_UP: { + if (pointerId != INVALID_POINTER_ID) { + final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == this.pointerId) { + // Log.d(TAG, "ACTION_POINTER_UP: " + pointerId); + returnHandleToCenter(); + setPointerId(INVALID_POINTER_ID); + return true; + } + } + break; + } + case MotionEvent.ACTION_DOWN: { + if (pointerId == INVALID_POINTER_ID) { + int x = (int) ev.getX(); + if (x >= offsetX && x < offsetX + dimX) { + setPointerId(ev.getPointerId(0)); + // Log.d(TAG, "ACTION_DOWN: " + getPointerId()); + return true; + } + } + break; + } + case MotionEvent.ACTION_POINTER_DOWN: { + if (pointerId == INVALID_POINTER_ID) { + final int pointerIndex = (action & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + final int pointerId = ev.getPointerId(pointerIndex); + int x = (int) ev.getX(pointerId); + if (x >= offsetX && x < offsetX + dimX) { + // Log.d(TAG, "ACTION_POINTER_DOWN: " + pointerId); + setPointerId(pointerId); + return true; + } + } + break; + } + } + return false; + } + + private boolean processMoveEvent(MotionEvent ev) { + if (pointerId != INVALID_POINTER_ID) { + final int pointerIndex = ev.findPointerIndex(pointerId); + + // Translate touch position to center of view + float x = ev.getX(pointerIndex); + touchX = x - cX - offsetX; + float y = ev.getY(pointerIndex); + touchY = y - cY - offsetY; + + // Log.d(TAG, + // String.format("ACTION_MOVE: (%03.0f, %03.0f) => (%03.0f, %03.0f)", + // x, y, touchX, touchY)); + + reportOnMoved(); + invalidate(); + + touchPressure = ev.getPressure(pointerIndex); + reportOnPressure(); + + return true; + } + return false; + } + + private void reportOnMoved() { + if (movementConstraint == CONSTRAIN_CIRCLE) + constrainCircle(); + else + constrainBox(); + + calcUserCoordinates(); + + if (moveListener != null) { + boolean rx = Math.abs(touchX - reportX) >= moveResolution; + boolean ry = Math.abs(touchY - reportY) >= moveResolution; + if (rx || ry) { + this.reportX = touchX; + this.reportY = touchY; + + // Log.d(TAG, String.format("moveListener.OnMoved(%d,%d)", + // (int)userX, (int)userY)); + moveListener.OnMoved(userX, userY); + } + } + } + + private void calcUserCoordinates() { + // First convert to cartesian coordinates + cartX = (int) (touchX / movementRadius * movementRange); + cartY = (int) (touchY / movementRadius * movementRange); + + radial = Math.sqrt((cartX * cartX) + (cartY * cartY)); + angle = Math.atan2(cartY, cartX); + + // Invert Y axis if requested + if (!yAxisInverted) + cartY *= -1; + + if (userCoordinateSystem == COORDINATE_CARTESIAN) { + userX = cartX; + userY = cartY; + } else if (userCoordinateSystem == COORDINATE_DIFFERENTIAL) { + userX = cartY + cartX / 4; + userY = cartY - cartX / 4; + + if (userX < -movementRange) + userX = (int) -movementRange; + if (userX > movementRange) + userX = (int) movementRange; + + if (userY < -movementRange) + userY = (int) -movementRange; + if (userY > movementRange) + userY = (int) movementRange; + } + + } + + // Simple pressure click + private void reportOnPressure() { + // Log.d(TAG, String.format("touchPressure=%.2f", this.touchPressure)); + if (clickListener != null) { + if (clicked && touchPressure < clickThreshold) { + clickListener.OnReleased(); + this.clicked = false; + // Log.d(TAG, "reset click"); + invalidate(); + } else if (!clicked && touchPressure >= clickThreshold) { + clicked = true; + clickListener.OnClicked(); + // Log.d(TAG, "click"); + invalidate(); + performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); + } + } + } + + private void returnHandleToCenter() { + if (autoReturnToCenter) { + final int numberOfFrames = 5; + final double intervalsX = (0 - touchX) / numberOfFrames; + final double intervalsY = (0 - touchY) / numberOfFrames; + + for (int i = 0; i < numberOfFrames; i++) { + final int j = i; + postDelayed(new Runnable() { + @Override + public void run() { + touchX += intervalsX; + touchY += intervalsY; + + reportOnMoved(); + invalidate(); + + if (moveListener != null && j == numberOfFrames - 1) { + moveListener.OnReturnedToCenter(); + } + } + }, i * 40); + } + + if (moveListener != null) { + moveListener.OnReleased(); + } + } + } + + public void setTouchOffset(int x, int y) { + offsetX = x; + offsetY = y; + } +}