public class

HoldDetector

extends BaseDetector
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);
	}
}