LibreOffice Module android (master) 1
SimpleScaleGestureDetector.java
Go to the documentation of this file.
1/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6package org.mozilla.gecko.gfx;
7
8import android.graphics.PointF;
9import android.util.Log;
10import android.view.MotionEvent;
11
12import org.json.JSONException;
13
14import java.util.LinkedList;
15import java.util.ListIterator;
16import java.util.Stack;
17
37 private static final String LOGTAG = "ScaleGestureDetector";
38
40 private long mLastEventTime;
41 private boolean mScaleResult;
42
43 /* Information about all pointers that are down. */
44 private LinkedList<PointerInfo> mPointerInfo;
45
48 mListener = listener;
49 mPointerInfo = new LinkedList<PointerInfo>();
50 }
51
53 public void onTouchEvent(MotionEvent event) {
54 switch (event.getAction() & MotionEvent.ACTION_MASK) {
55 case MotionEvent.ACTION_DOWN:
56 // If we get ACTION_DOWN while still tracking any pointers,
57 // something is wrong. Cancel the current gesture and start over.
58 if (getPointersDown() > 0)
59 onTouchEnd(event);
60 onTouchStart(event);
61 break;
62 case MotionEvent.ACTION_POINTER_DOWN:
63 onTouchStart(event);
64 break;
65 case MotionEvent.ACTION_MOVE:
66 onTouchMove(event);
67 break;
68 case MotionEvent.ACTION_POINTER_UP:
69 case MotionEvent.ACTION_UP:
70 case MotionEvent.ACTION_CANCEL:
71 onTouchEnd(event);
72 break;
73 }
74 }
75
76 private int getPointersDown() {
77 return mPointerInfo.size();
78 }
79
80 private int getActionIndex(MotionEvent event) {
81 return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
82 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
83 }
84
85 private void onTouchStart(MotionEvent event) {
86 mLastEventTime = event.getEventTime();
87 mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event)));
88 if (getPointersDown() == 2) {
90 }
91 }
92
93 private void onTouchMove(MotionEvent event) {
94 mLastEventTime = event.getEventTime();
95 for (int i = 0; i < event.getPointerCount(); i++) {
96 PointerInfo pointerInfo = pointerInfoForEventIndex(event, i);
97 if (pointerInfo != null) {
98 pointerInfo.populate(event, i);
99 }
100 }
101
102 if (getPointersDown() == 2) {
104 }
105 }
106
107 private void onTouchEnd(MotionEvent event) {
108 mLastEventTime = event.getEventTime();
109
110 int action = event.getAction() & MotionEvent.ACTION_MASK;
111 boolean isCancel = (action == MotionEvent.ACTION_CANCEL ||
112 action == MotionEvent.ACTION_DOWN);
113
114 int id = event.getPointerId(getActionIndex(event));
115 ListIterator<PointerInfo> iterator = mPointerInfo.listIterator();
116 while (iterator.hasNext()) {
117 PointerInfo pointerInfo = iterator.next();
118 if (!(isCancel || pointerInfo.getId() == id)) {
119 continue;
120 }
121
122 // One of the pointers we were tracking was lifted. Remove its info object from the
123 // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this
124 // ended the gesture.
125 iterator.remove();
126 pointerInfo.recycle();
127 if (getPointersDown() == 1) {
129 }
130 }
131 }
132
137 public float getFocusX() {
138 switch (getPointersDown()) {
139 case 1:
140 return mPointerInfo.getFirst().getCurrent().x;
141 case 2:
142 PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
143 return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f;
144 }
145
146 Log.e(LOGTAG, "No gesture taking place in getFocusX()!");
147 return 0.0f;
148 }
149
154 public float getFocusY() {
155 switch (getPointersDown()) {
156 case 1:
157 return mPointerInfo.getFirst().getCurrent().y;
158 case 2:
159 PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
160 return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f;
161 }
162
163 Log.e(LOGTAG, "No gesture taking place in getFocusY()!");
164 return 0.0f;
165 }
166
168 public float getCurrentSpan() {
169 if (getPointersDown() != 2) {
170 Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!");
171 return 0.0f;
172 }
173
174 PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
175 return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent());
176 }
177
179 public float getPreviousSpan() {
180 if (getPointersDown() != 2) {
181 Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!");
182 return 0.0f;
183 }
184
185 PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast();
186 PointF a = pointerA.getPrevious(), b = pointerB.getPrevious();
187 if (a == null || b == null) {
188 a = pointerA.getCurrent();
189 b = pointerB.getCurrent();
190 }
191
192 return PointUtils.distance(a, b);
193 }
194
196 public long getEventTime() {
197 return mLastEventTime;
198 }
199
201 public boolean isInProgress() {
202 return getPointersDown() == 2;
203 }
204
205 /* Sends the requested scale gesture notification to the listener. */
206 private void sendScaleGesture(EventType eventType) {
207 switch (eventType) {
208 case BEGIN:
210 break;
211 case CONTINUE:
212 if (mScaleResult) {
213 mListener.onScale(this);
214 }
215 break;
216 case END:
217 if (mScaleResult) {
218 mListener.onScaleEnd(this);
219 }
220 break;
221 }
222 }
223
224 /*
225 * Returns the pointer info corresponding to the given pointer index, or null if the pointer
226 * isn't one that's being tracked.
227 */
228 private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) {
229 int id = event.getPointerId(index);
230 for (PointerInfo pointerInfo : mPointerInfo) {
231 if (pointerInfo.getId() == id) {
232 return pointerInfo;
233 }
234 }
235 return null;
236 }
237
238 private enum EventType {
242 }
243
244 /* Encapsulates information about one of the two fingers involved in the gesture. */
245 private static class PointerInfo {
246 /* A free list that recycles pointer info objects, to reduce GC pauses. */
247 private static Stack<PointerInfo> sPointerInfoFreeList;
248
249 private int mId;
250 private PointF mCurrent, mPrevious;
251
252 private PointerInfo() {
253 // External users should use create() instead.
254 }
255
256 /* Creates or recycles a new PointerInfo instance from an event and a pointer index. */
257 public static PointerInfo create(MotionEvent event, int index) {
258 if (sPointerInfoFreeList == null) {
259 sPointerInfoFreeList = new Stack<PointerInfo>();
260 }
261
262 PointerInfo pointerInfo;
263 if (sPointerInfoFreeList.empty()) {
264 pointerInfo = new PointerInfo();
265 } else {
266 pointerInfo = sPointerInfoFreeList.pop();
267 }
268
269 pointerInfo.populate(event, index);
270 return pointerInfo;
271 }
272
273 /*
274 * Fills in the fields of this instance from the given motion event and pointer index
275 * within that event.
276 */
277 public void populate(MotionEvent event, int index) {
278 mId = event.getPointerId(index);
280 mCurrent = new PointF(event.getX(index), event.getY(index));
281 }
282
283 public void recycle() {
284 mId = -1;
285 mPrevious = mCurrent = null;
286 sPointerInfoFreeList.push(this);
287 }
288
289 public int getId() { return mId; }
290 public PointF getCurrent() { return mCurrent; }
291 public PointF getPrevious() { return mPrevious; }
292
293 @Override
294 public String toString() {
295 if (mId == -1) {
296 return "(up)";
297 }
298
299 try {
300 String prevString;
301 if (mPrevious == null) {
302 prevString = "n/a";
303 } else {
304 prevString = PointUtils.toJSON(mPrevious).toString();
305 }
306
307 // The current position should always be non-null.
308 String currentString = PointUtils.toJSON(mCurrent).toString();
309 return "id=" + mId + " cur=" + currentString + " prev=" + prevString;
310 } catch (JSONException e) {
311 throw new RuntimeException(e);
312 }
313 }
314 }
315
316 public static interface SimpleScaleGestureListener {
317 public boolean onScale(SimpleScaleGestureDetector detector);
318 public boolean onScaleBegin(SimpleScaleGestureDetector detector);
320 }
321}
322
static JSONObject toJSON(PointF point)
Definition: PointUtils.java:41
static float distance(PointF point)
Definition: PointUtils.java:32
A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector.
float getPreviousSpan()
Returns the second most recent distance between the two pointers.
boolean isInProgress()
Returns true if the scale gesture is in progress and false otherwise.
PointerInfo pointerInfoForEventIndex(MotionEvent event, int index)
float getCurrentSpan()
Returns the most recent distance between the two pointers.
float getFocusY()
Returns the Y coordinate of the focus location (the midpoint of the two fingers).
SimpleScaleGestureDetector(SimpleScaleGestureListener listener)
Creates a new gesture detector with the given listener.
void onTouchEvent(MotionEvent event)
Forward touch events to this function.
float getFocusX()
Returns the X coordinate of the focus location (the midpoint of the two fingers).
long getEventTime()
Returns the time of the last event related to the gesture.
uno_Any a
int i
index
action
END
BEGIN