package org.anddev.andengine.input.touch.detector; import org.anddev.andengine.engine.handler.IUpdateHandler; import org.anddev.andengine.engine.handler.timer.ITimerCallback; import org.anddev.andengine.engine.handler.timer.TimerHandler; import org.anddev.andengine.entity.scene.Scene; import org.anddev.andengine.input.touch.TouchEvent; import android.os.SystemClock; import android.view.MotionEvent; /** * Note: Needs to be registered as an {@link IUpdateHandler} to the {@link Engine} or {@link Scene} to work properly. * * (c) 2010 Nicolas Gramlich * (c) 2011 Zynga Inc. * * @author Nicolas Gramlich * @since 20:49:25 - 23.08.2010 */ public class HoldDetector extends BaseDetector implements IUpdateHandler { // =========================================================== // Constants // =========================================================== private static final long TRIGGER_HOLD_MINIMUM_MILLISECONDS_DEFAULT = 200; private static final float TRIGGER_HOLD_MAXIMUM_DISTANCE_DEFAULT = 10; private static final float TIME_BETWEEN_UPDATES_DEFAULT = 0.1f; // =========================================================== // Fields // =========================================================== private long mTriggerHoldMinimumMilliseconds; private float mTriggerHoldMaximumDistance; private final IHoldDetectorListener mHoldDetectorListener; private long mDownTimeMilliseconds = Long.MIN_VALUE; private float mDownX; private float mDownY; private float mHoldX; private float mHoldY; private boolean mMaximumDistanceExceeded = false; private boolean mTriggerOnHold = false; private boolean mTriggerOnHoldFinished = false; private final TimerHandler mTimerHandler; // =========================================================== // Constructors // =========================================================== public HoldDetector(final IHoldDetectorListener pClickDetectorListener) { this(TRIGGER_HOLD_MINIMUM_MILLISECONDS_DEFAULT, TRIGGER_HOLD_MAXIMUM_DISTANCE_DEFAULT, TIME_BETWEEN_UPDATES_DEFAULT, pClickDetectorListener); } public HoldDetector(final long pTriggerHoldMinimumMilliseconds, final float pTriggerHoldMaximumDistance, final float pTimeBetweenUpdates, final IHoldDetectorListener pClickDetectorListener) { this.mTriggerHoldMinimumMilliseconds = pTriggerHoldMinimumMilliseconds; this.mTriggerHoldMaximumDistance = pTriggerHoldMaximumDistance; this.mHoldDetectorListener = pClickDetectorListener; this.mTimerHandler = new TimerHandler(pTimeBetweenUpdates, true, new ITimerCallback() { @Override public void onTimePassed(final TimerHandler pTimerHandler) { HoldDetector.this.fireListener(); } }); } // =========================================================== // Getter & Setter // =========================================================== public long getTriggerHoldMinimumMilliseconds() { return this.mTriggerHoldMinimumMilliseconds; } public void setTriggerHoldMinimumMilliseconds(final long pTriggerHoldMinimumMilliseconds) { this.mTriggerHoldMinimumMilliseconds = pTriggerHoldMinimumMilliseconds; } public float getTriggerHoldMaximumDistance() { return this.mTriggerHoldMaximumDistance; } public void setTriggerHoldMaximumDistance(final float pTriggerHoldMaximumDistance) { this.mTriggerHoldMaximumDistance = pTriggerHoldMaximumDistance; } public boolean isHolding() { return this.mTriggerOnHold; } // =========================================================== // Methods for/from SuperClass/Interfaces // =========================================================== @Override public void onUpdate(final float pSecondsElapsed) { this.mTimerHandler.onUpdate(pSecondsElapsed); } @Override public void reset() { this.mTimerHandler.reset(); } @Override public boolean onManagedTouchEvent(final TouchEvent pSceneTouchEvent) { final MotionEvent motionEvent = pSceneTouchEvent.getMotionEvent(); this.mHoldX = pSceneTouchEvent.getX(); this.mHoldY = pSceneTouchEvent.getY(); switch(pSceneTouchEvent.getAction()) { case MotionEvent.ACTION_DOWN: this.mDownTimeMilliseconds = motionEvent.getDownTime(); this.mDownX = motionEvent.getX(); this.mDownY = motionEvent.getY(); this.mMaximumDistanceExceeded = false; return true; case MotionEvent.ACTION_MOVE: { final long currentTimeMilliseconds = motionEvent.getEventTime(); 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.mTriggerOnHold || !this.mMaximumDistanceExceeded) { final long holdTimeMilliseconds = currentTimeMilliseconds - this.mDownTimeMilliseconds; if(holdTimeMilliseconds >= this.mTriggerHoldMinimumMilliseconds) { this.mTriggerOnHold = true; } } return true; } case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: { final long upTimeMilliseconds = motionEvent.getEventTime(); 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.mTriggerOnHold || !this.mMaximumDistanceExceeded) { final long holdTimeMilliseconds = upTimeMilliseconds - this.mDownTimeMilliseconds; if(holdTimeMilliseconds >= this.mTriggerHoldMinimumMilliseconds) { this.mTriggerOnHoldFinished = true; } } return true; } default: return false; } } // =========================================================== // Methods // =========================================================== protected void fireListener() { if(this.mTriggerOnHoldFinished) { this.mHoldDetectorListener.onHoldFinished(this, SystemClock.uptimeMillis() - this.mDownTimeMilliseconds, this.mHoldX, this.mHoldY); this.mTriggerOnHoldFinished = false; this.mTriggerOnHold = false; } else if(this.mTriggerOnHold) { this.mHoldDetectorListener.onHold(this, SystemClock.uptimeMillis() - this.mDownTimeMilliseconds, this.mHoldX, this.mHoldY); } } // =========================================================== // Inner and Anonymous Classes // =========================================================== public static interface IHoldDetectorListener { // =========================================================== // Constants // =========================================================== // =========================================================== // Methods // =========================================================== public void onHold(final HoldDetector pHoldDetector, final long pHoldTimeMilliseconds, final float pHoldX, final float pHoldY); public void onHoldFinished(final HoldDetector pHoldDetector, final long pHoldTimeMilliseconds, final float pHoldX, final float pHoldY); } }