11 package com.freerdp.freerdpcore.utils;
13 import android.content.Context;
14 import android.os.Handler;
15 import android.view.MotionEvent;
16 import android.view.ScaleGestureDetector;
18 import com.freerdp.freerdpcore.utils.GestureDetector.OnGestureListener;
24 private static final long DOUBLE_TOUCH_TIMEOUT = 100;
26 private static final long SINGLE_DOUBLE_TOUCH_TIMEOUT = 1000;
28 private static final int TAP = 1;
30 private static final int MODE_UNKNOWN = 0;
31 private static final int MODE_PINCH_ZOOM = 1;
32 private static final int MODE_SCROLL = 2;
33 private static final int SCROLL_SCORE_TO_REACH = 20;
35 private int mPointerDistanceSquare;
36 private int mCurrentMode;
37 private int mScrollDetectionScore;
38 private ScaleGestureDetector scaleGestureDetector;
39 private boolean mCancelDetection;
40 private boolean mDoubleInProgress;
41 private GestureHandler mHandler;
42 private MotionEvent mCurrentDownEvent;
43 private MotionEvent mCurrentDoubleDownEvent;
44 private MotionEvent mPreviousUpEvent;
45 private MotionEvent mPreviousPointerUpEvent;
59 init(context, handler);
62 private void init(Context context, Handler handler)
64 if (mListener ==
null)
66 throw new NullPointerException(
"OnGestureListener must not be null");
70 mHandler =
new GestureHandler(handler);
72 mHandler =
new GestureHandler();
77 float distInches = 0.5f / 2.54f;
78 float distPixelsX = distInches * context.getResources().getDisplayMetrics().xdpi;
79 float distPixelsY = distInches * context.getResources().getDisplayMetrics().ydpi;
81 mPointerDistanceSquare = (int)(distPixelsX * distPixelsX + distPixelsY * distPixelsY);
91 this.scaleGestureDetector = scaleGestureDetector;
104 boolean handled =
false;
105 final int action = ev.getAction();
108 switch (action & MotionEvent.ACTION_MASK)
110 case MotionEvent.ACTION_DOWN:
111 if (mCurrentDownEvent !=
null)
112 mCurrentDownEvent.recycle();
114 mCurrentMode = MODE_UNKNOWN;
115 mCurrentDownEvent = MotionEvent.obtain(ev);
116 mCancelDetection =
false;
117 mDoubleInProgress =
false;
118 mScrollDetectionScore = 0;
122 case MotionEvent.ACTION_POINTER_UP:
123 if (mPreviousPointerUpEvent !=
null)
124 mPreviousPointerUpEvent.recycle();
125 mPreviousPointerUpEvent = MotionEvent.obtain(ev);
128 case MotionEvent.ACTION_POINTER_DOWN:
131 if (ev.getPointerCount() > 2 ||
132 (ev.getEventTime() - mCurrentDownEvent.getEventTime()) > DOUBLE_TOUCH_TIMEOUT)
139 if (mCancelDetection)
143 mDoubleInProgress =
true;
144 if (mCurrentDoubleDownEvent !=
null)
145 mCurrentDoubleDownEvent.recycle();
146 mCurrentDoubleDownEvent = MotionEvent.obtain(ev);
149 mCurrentMode = MODE_UNKNOWN;
150 mHandler.sendEmptyMessageDelayed(TAP, SINGLE_DOUBLE_TOUCH_TIMEOUT);
155 case MotionEvent.ACTION_MOVE:
158 if (mCancelDetection || !mDoubleInProgress || ev.getPointerCount() != 2)
162 if (mCurrentMode == MODE_UNKNOWN)
165 if (pointerDistanceChanged(mCurrentDoubleDownEvent, ev))
167 handled |= scaleGestureDetector.onTouchEvent(mCurrentDownEvent);
168 MotionEvent e = MotionEvent.obtain(ev);
169 e.setAction(mCurrentDoubleDownEvent.getAction());
170 handled |= scaleGestureDetector.onTouchEvent(e);
171 mCurrentMode = MODE_PINCH_ZOOM;
176 mScrollDetectionScore++;
177 if (mScrollDetectionScore >= SCROLL_SCORE_TO_REACH)
178 mCurrentMode = MODE_SCROLL;
182 switch (mCurrentMode)
184 case MODE_PINCH_ZOOM:
185 if (scaleGestureDetector !=
null)
186 handled |= scaleGestureDetector.onTouchEvent(ev);
200 case MotionEvent.ACTION_UP:
202 if (mPreviousPointerUpEvent !=
null &&
203 (ev.getEventTime() - mPreviousPointerUpEvent.getEventTime()) >
204 DOUBLE_TOUCH_TIMEOUT)
206 mPreviousPointerUpEvent.recycle();
207 mPreviousPointerUpEvent =
null;
213 if (mCancelDetection || !mDoubleInProgress)
216 boolean hasTapEvent = mHandler.hasMessages(TAP);
217 MotionEvent currentUpEvent = MotionEvent.obtain(ev);
218 if (mCurrentMode == MODE_UNKNOWN && hasTapEvent)
220 else if (mCurrentMode == MODE_PINCH_ZOOM)
221 handled = scaleGestureDetector.onTouchEvent(ev);
223 if (mPreviousUpEvent !=
null)
224 mPreviousUpEvent.recycle();
227 mPreviousUpEvent = currentUpEvent;
231 case MotionEvent.ACTION_CANCEL:
236 if ((action == MotionEvent.ACTION_MOVE) && handled ==
false)
242 private void cancel()
244 mHandler.removeMessages(TAP);
245 mCurrentMode = MODE_UNKNOWN;
246 mCancelDetection =
true;
247 mDoubleInProgress =
false;
251 private boolean pointerDistanceChanged(MotionEvent oldEvent, MotionEvent newEvent)
253 int deltaX1 = Math.abs((
int)oldEvent.getX(0) - (
int)oldEvent.getX(1));
254 int deltaX2 = Math.abs((
int)newEvent.getX(0) - (
int)newEvent.getX(1));
255 int distXSquare = (deltaX2 - deltaX1) * (deltaX2 - deltaX1);
257 int deltaY1 = Math.abs((
int)oldEvent.getY(0) - (int)oldEvent.getY(1));
258 int deltaY2 = Math.abs((
int)newEvent.getY(0) - (int)newEvent.getY(1));
259 int distYSquare = (deltaY2 - deltaY1) * (deltaY2 - deltaY1);
261 return (distXSquare + distYSquare) > mPointerDistanceSquare;
337 private class GestureHandler
extends Handler
344 GestureHandler(Handler handler)
346 super(handler.getLooper());
void setScaleGestureDetector(ScaleGestureDetector scaleGestureDetector)
boolean onTouchEvent(MotionEvent ev)
DoubleGestureDetector(Context context, Handler handler, OnDoubleGestureListener listener)
boolean onDoubleTouchScroll(MotionEvent e1, MotionEvent e2)
boolean onDoubleTouchUp(MotionEvent e)
boolean onDoubleTouchDown(MotionEvent e)
boolean onDoubleTouchSingleTap(MotionEvent e)