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