19 package com.freerdp.freerdpcore.utils;
21 import android.content.Context;
22 import android.os.Build;
23 import android.os.Handler;
24 import android.os.Message;
25 import android.util.DisplayMetrics;
26 import android.view.MotionEvent;
27 import android.view.ViewConfiguration;
32 private static final int TAP_TIMEOUT = 100;
33 private static final int DOUBLE_TAP_TIMEOUT = 200;
36 private static final int LARGE_TOUCH_SLOP = 18;
38 private static final int DOUBLE_TAP_SLOP = 100;
40 private static final int SHOW_PRESS = 1;
41 private static final int LONG_PRESS = 2;
42 private static final int TAP = 3;
43 private final Handler mHandler;
45 private int mTouchSlopSquare;
46 private int mLargeTouchSlopSquare;
47 private int mDoubleTapSlopSquare;
48 private int mLongpressTimeout = 100;
50 private boolean mStillDown;
51 private boolean mInLongPress;
52 private boolean mAlwaysInTapRegion;
53 private boolean mAlwaysInBiggerTapRegion;
54 private MotionEvent mCurrentDownEvent;
55 private MotionEvent mPreviousUpEvent;
60 private boolean mIsDoubleTapping;
61 private float mLastMotionY;
62 private float mLastMotionX;
63 private boolean mIsLongpressEnabled;
69 private boolean mIgnoreMultitouch;
82 this(context, listener,
null);
98 this(context, listener, handler,
100 context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.FROYO);
117 boolean ignoreMultitouch)
121 mHandler =
new GestureHandler(handler);
125 mHandler =
new GestureHandler();
127 mListener = listener;
132 init(context, ignoreMultitouch);
135 private void init(Context context,
boolean ignoreMultitouch)
137 if (mListener ==
null)
139 throw new NullPointerException(
"OnGestureListener must not be null");
141 mIsLongpressEnabled =
true;
142 mIgnoreMultitouch = ignoreMultitouch;
145 int touchSlop, largeTouchSlop, doubleTapSlop;
149 touchSlop = ViewConfiguration.getTouchSlop();
150 largeTouchSlop = touchSlop + 2;
151 doubleTapSlop = DOUBLE_TAP_SLOP;
155 final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
156 final float density = metrics.density;
157 final ViewConfiguration configuration = ViewConfiguration.get(context);
158 touchSlop = configuration.getScaledTouchSlop();
159 largeTouchSlop = (int)(density * LARGE_TOUCH_SLOP + 0.5f);
160 doubleTapSlop = configuration.getScaledDoubleTapSlop();
162 mTouchSlopSquare = touchSlop * touchSlop;
163 mLargeTouchSlopSquare = largeTouchSlop * largeTouchSlop;
164 mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
176 mDoubleTapListener = onDoubleTapListener;
198 return mIsLongpressEnabled;
201 public void setLongPressTimeout(
int timeout)
203 mLongpressTimeout = timeout;
216 final int action = ev.getAction();
217 final float y = ev.getY();
218 final float x = ev.getX();
220 boolean handled =
false;
222 switch (action & MotionEvent.ACTION_MASK)
224 case MotionEvent.ACTION_POINTER_DOWN:
225 if (mIgnoreMultitouch)
232 case MotionEvent.ACTION_POINTER_UP:
234 if (mIgnoreMultitouch && ev.getPointerCount() == 2)
236 int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK) >>
237 MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0)
240 mLastMotionX = ev.getX(index);
241 mLastMotionY = ev.getY(index);
245 case MotionEvent.ACTION_DOWN:
246 if (mDoubleTapListener !=
null)
248 boolean hadTapMessage = mHandler.hasMessages(TAP);
250 mHandler.removeMessages(TAP);
251 if ((mCurrentDownEvent !=
null) && (mPreviousUpEvent !=
null) &&
253 isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev))
256 mIsDoubleTapping =
true;
258 handled |= mDoubleTapListener.
onDoubleTap(mCurrentDownEvent);
265 mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
271 if (mCurrentDownEvent !=
null)
273 mCurrentDownEvent.recycle();
275 mCurrentDownEvent = MotionEvent.obtain(ev);
276 mAlwaysInTapRegion =
true;
277 mAlwaysInBiggerTapRegion =
true;
279 mInLongPress =
false;
281 if (mIsLongpressEnabled)
283 mHandler.removeMessages(LONG_PRESS);
284 mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() +
288 mHandler.sendEmptyMessageAtTime(SHOW_PRESS,
289 mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
290 handled |= mListener.
onDown(ev);
293 case MotionEvent.ACTION_MOVE:
294 if (mIgnoreMultitouch && ev.getPointerCount() > 1)
298 final float scrollX = mLastMotionX - x;
299 final float scrollY = mLastMotionY - y;
300 if (mIsDoubleTapping)
305 else if (mAlwaysInTapRegion)
307 final int deltaX = (int)(x - mCurrentDownEvent.getX());
308 final int deltaY = (int)(y - mCurrentDownEvent.getY());
309 int distance = (deltaX * deltaX) + (deltaY * deltaY);
310 if (distance > mTouchSlopSquare)
314 mAlwaysInTapRegion =
false;
315 mHandler.removeMessages(TAP);
316 mHandler.removeMessages(SHOW_PRESS);
317 mHandler.removeMessages(LONG_PRESS);
319 if (distance > mLargeTouchSlopSquare)
321 mAlwaysInBiggerTapRegion =
false;
323 handled = mListener.
onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
325 else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1))
327 handled = mListener.
onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
333 case MotionEvent.ACTION_UP:
335 MotionEvent currentUpEvent = MotionEvent.obtain(ev);
336 if (mIsDoubleTapping)
341 else if (mInLongPress)
343 mHandler.removeMessages(TAP);
345 mInLongPress =
false;
347 else if (mAlwaysInTapRegion)
355 if (mPreviousUpEvent !=
null)
357 mPreviousUpEvent.recycle();
360 mPreviousUpEvent = currentUpEvent;
361 mIsDoubleTapping =
false;
362 mHandler.removeMessages(SHOW_PRESS);
363 mHandler.removeMessages(LONG_PRESS);
364 handled |= mListener.
onUp(ev);
366 case MotionEvent.ACTION_CANCEL:
373 private void cancel()
375 mHandler.removeMessages(SHOW_PRESS);
376 mHandler.removeMessages(LONG_PRESS);
377 mHandler.removeMessages(TAP);
378 mAlwaysInTapRegion =
false;
380 mIsDoubleTapping =
false;
384 mInLongPress =
false;
388 private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
389 MotionEvent secondDown)
391 if (!mAlwaysInBiggerTapRegion)
396 if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT)
401 int deltaX = (int)firstDown.getX() - (int)secondDown.getX();
402 int deltaY = (int)firstDown.getY() - (int)secondDown.getY();
403 return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
406 private void dispatchLongPress()
408 mHandler.removeMessages(TAP);
473 boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX,
float distanceY);
548 public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX,
float distanceY)
562 public boolean onUp(MotionEvent e)
583 private class GestureHandler
extends Handler
590 GestureHandler(Handler handler)
592 super(handler.getLooper());
595 @Override
public void handleMessage(Message msg)
609 if (mDoubleTapListener !=
null && !mStillDown)
616 throw new RuntimeException(
"Unknown message " + msg);
boolean onSingleTapConfirmed(MotionEvent e)
boolean onDoubleTapEvent(MotionEvent e)
void onLongPressUp(MotionEvent e)
void onShowPress(MotionEvent e)
boolean onSingleTapUp(MotionEvent e)
boolean onDoubleTap(MotionEvent e)
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
void onLongPress(MotionEvent e)
boolean onDown(MotionEvent e)
boolean onUp(MotionEvent e)
boolean isLongpressEnabled()
void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener)
void setIsLongpressEnabled(boolean isLongpressEnabled)
GestureDetector(Context context, OnGestureListener listener, Handler handler, boolean ignoreMultitouch)
GestureDetector(Context context, OnGestureListener listener)
GestureDetector(Context context, OnGestureListener listener, Handler handler)
boolean onTouchEvent(MotionEvent ev)
boolean onDoubleTapEvent(MotionEvent e)
boolean onSingleTapConfirmed(MotionEvent e)
boolean onDoubleTap(MotionEvent e)
boolean onSingleTapUp(MotionEvent e)
void onLongPressUp(MotionEvent e)
boolean onDown(MotionEvent e)
void onLongPress(MotionEvent e)
void onShowPress(MotionEvent e)
boolean onUp(MotionEvent e)
boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)