package org.andengine.input.touch.detector;
import org.andengine.input.touch.TouchEvent;
import android.view.MotionEvent;
/**
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @author Greg Haynes
* @since 20:49:25 - 23.08.2010
*/
public class HoldDetector extends BaseDetector {
// ===========================================================
// Constants
// ===========================================================
protected static final long TRIGGER_HOLD_MINIMUM_MILLISECONDS_DEFAULT = 200;
protected static final float TRIGGER_HOLD_MAXIMUM_DISTANCE_DEFAULT = 10;
// ===========================================================
// Fields
// ===========================================================
protected long mTriggerHoldMinimumMilliseconds;
protected float mTriggerHoldMaximumDistance;
protected final IHoldDetectorListener mHoldDetectorListener;
protected int mPointerID = TouchEvent.INVALID_POINTER_ID;
protected long mDownTimeMilliseconds = Long.MIN_VALUE;
protected float mDownX;
protected float mDownY;
protected float mHoldX;
protected float mHoldY;
protected boolean mMaximumDistanceExceeded;
protected boolean mTriggering;
// ===========================================================
// Constructors
// ===========================================================
public HoldDetector(final IHoldDetectorListener pHoldDetectorListener) {
this(HoldDetector.TRIGGER_HOLD_MINIMUM_MILLISECONDS_DEFAULT, HoldDetector.TRIGGER_HOLD_MAXIMUM_DISTANCE_DEFAULT, pHoldDetectorListener);
}
public HoldDetector(final long pTriggerHoldMinimumMilliseconds, final float pTriggerHoldMaximumDistance, final IHoldDetectorListener pHoldDetectorListener) {
this.setTriggerHoldMinimumMilliseconds(pTriggerHoldMinimumMilliseconds);
this.setTriggerHoldMaximumDistance(pTriggerHoldMaximumDistance);
this.mHoldDetectorListener = pHoldDetectorListener;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public long getTriggerHoldMinimumMilliseconds() {
return this.mTriggerHoldMinimumMilliseconds;
}
public void setTriggerHoldMinimumMilliseconds(final long pTriggerHoldMinimumMilliseconds) {
if(pTriggerHoldMinimumMilliseconds < 0) {
throw new IllegalArgumentException("pTriggerHoldMinimumMilliseconds must not be < 0.");
}
this.mTriggerHoldMinimumMilliseconds = pTriggerHoldMinimumMilliseconds;
}
public float getTriggerHoldMaximumDistance() {
return this.mTriggerHoldMaximumDistance;
}
public void setTriggerHoldMaximumDistance(final float pTriggerHoldMaximumDistance) {
if(pTriggerHoldMaximumDistance < 0) {
throw new IllegalArgumentException("pTriggerHoldMaximumDistance must not be < 0.");
}
this.mTriggerHoldMaximumDistance = pTriggerHoldMaximumDistance;
}
public boolean isHolding() {
return this.mTriggering;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
/**
* When {@link HoldDetector#isHolding()} this method will call through to {@link IHoldDetectorListener#onHoldFinished(HoldDetector, long, int, float, float).
*/
@Override
public void reset() {
if(this.mTriggering) {
this.triggerOnHoldFinished(System.currentTimeMillis() - this.mDownTimeMilliseconds);
}
this.mTriggering = false;
this.mMaximumDistanceExceeded = false;
this.mDownTimeMilliseconds = Long.MIN_VALUE;
this.mPointerID = TouchEvent.INVALID_POINTER_ID;
}
@Override
public boolean onManagedTouchEvent(final TouchEvent pSceneTouchEvent) {
final MotionEvent motionEvent = pSceneTouchEvent.getMotionEvent();
switch(pSceneTouchEvent.getAction()) {
case TouchEvent.ACTION_DOWN:
if(this.mPointerID == TouchEvent.INVALID_POINTER_ID) {
this.prepareHold(pSceneTouchEvent);
return true;
} else {
return false;
}
case TouchEvent.ACTION_MOVE:
{
if(this.mPointerID == pSceneTouchEvent.getPointerID()) {
this.mHoldX = pSceneTouchEvent.getX();
this.mHoldY = pSceneTouchEvent.getY();
final long holdTimeMilliseconds = System.currentTimeMillis() - this.mDownTimeMilliseconds;
if(holdTimeMilliseconds >= this.mTriggerHoldMinimumMilliseconds) {
if(this.mTriggering) {
this.triggerOnHold(holdTimeMilliseconds);
} else {
final float triggerHoldMaximumDistance = this.mTriggerHoldMaximumDistance;
this.mMaximumDistanceExceeded = this.mMaximumDistanceExceeded || Math.abs(this.mDownX - motionEvent.getX()) > triggerHoldMaximumDistance || Math.abs(this.mDownY - motionEvent.getY()) > triggerHoldMaximumDistance;
if(!this.mMaximumDistanceExceeded) {
if(!this.mTriggering) {
this.triggerOnHoldStarted();
} else {
this.triggerOnHold(holdTimeMilliseconds);
}
}
}
}
return true;
} else {
return false;
}
}
case TouchEvent.ACTION_UP:
case TouchEvent.ACTION_CANCEL:
{
if(this.mPointerID == pSceneTouchEvent.getPointerID()) {
this.mHoldX = pSceneTouchEvent.getX();
this.mHoldY = pSceneTouchEvent.getY();
final long holdTimeMilliseconds = System.currentTimeMillis() - this.mDownTimeMilliseconds;
if(holdTimeMilliseconds >= this.mTriggerHoldMinimumMilliseconds) {
if(this.mTriggering) {
this.triggerOnHoldFinished(holdTimeMilliseconds);
} else {
final float triggerHoldMaximumDistance = this.mTriggerHoldMaximumDistance;
this.mMaximumDistanceExceeded = this.mMaximumDistanceExceeded || Math.abs(this.mDownX - motionEvent.getX()) > triggerHoldMaximumDistance || Math.abs(this.mDownY - motionEvent.getY()) > triggerHoldMaximumDistance;
if(!this.mMaximumDistanceExceeded) {
this.triggerOnHoldFinished(holdTimeMilliseconds);
}
}
}
this.mPointerID = TouchEvent.INVALID_POINTER_ID;
return true;
} else {
return false;
}
}
default:
return false;
}
}
// ===========================================================
// Methods
// ===========================================================
protected void prepareHold(final TouchEvent pSceneTouchEvent) {
final MotionEvent motionEvent = pSceneTouchEvent.getMotionEvent();
this.mDownTimeMilliseconds = System.currentTimeMillis();
this.mDownX = motionEvent.getX();
this.mDownY = motionEvent.getY();
this.mMaximumDistanceExceeded = false;
this.mPointerID = pSceneTouchEvent.getPointerID();
this.mHoldX = pSceneTouchEvent.getX();
this.mHoldY = pSceneTouchEvent.getY();
if(this.mTriggerHoldMinimumMilliseconds == 0) {
this.triggerOnHoldStarted();
}
}
protected void triggerOnHoldStarted() {
this.mTriggering = true;
if(this.mPointerID != TouchEvent.INVALID_POINTER_ID) {
this.mHoldDetectorListener.onHoldStarted(this, this.mPointerID, this.mHoldX, this.mHoldY);
}
}
protected void triggerOnHold(final long pHoldTimeMilliseconds) {
if(this.mPointerID != TouchEvent.INVALID_POINTER_ID) {
this.mHoldDetectorListener.onHold(this, pHoldTimeMilliseconds, this.mPointerID, this.mHoldX, this.mHoldY);
}
}
protected void triggerOnHoldFinished(final long pHoldTimeMilliseconds) {
this.mTriggering = false;
if(this.mPointerID != TouchEvent.INVALID_POINTER_ID) {
this.mHoldDetectorListener.onHoldFinished(this, pHoldTimeMilliseconds, this.mPointerID, this.mHoldX, this.mHoldY);
}
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
public static interface IHoldDetectorListener {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Methods
// ===========================================================
public void onHoldStarted(final HoldDetector pHoldDetector, final int pPointerID, final float pHoldX, final float pHoldY);
public void onHold(final HoldDetector pHoldDetector, final long pHoldTimeMilliseconds, final int pPointerID, final float pHoldX, final float pHoldY);
public void onHoldFinished(final HoldDetector pHoldDetector, final long pHoldTimeMilliseconds, final int pPointerID, final float pHoldX, final float pHoldY);
}
}