diff options
Diffstat (limited to 'android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java')
-rw-r--r-- | android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java | 332 |
1 files changed, 0 insertions, 332 deletions
diff --git a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java b/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java deleted file mode 100644 index 4f9e39857e81..000000000000 --- a/android/experimental/LOAndroid2/app/src/main/java/org/mozilla/gecko/ui/SimpleScaleGestureDetector.java +++ /dev/null @@ -1,332 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2012 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton <pcwalton@mozilla.com> - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -package org.mozilla.gecko.ui; - -import org.mozilla.gecko.gfx.PointUtils; -import org.json.JSONException; -import android.graphics.PointF; -import android.util.Log; -import android.view.MotionEvent; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Stack; - -/** - * A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector. - * - * This gesture detector is more reliable than the built-in ScaleGestureDetector because: - * - * - It doesn't assume that pointer IDs are numbered 0 and 1. - * - * - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some - * devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many - * pointers are down, with disastrous results (bug 706684). - * - * - Cancelling a zoom into a pan is handled correctly. - * - * - Starting with three or more fingers down, releasing fingers so that only two are down, and - * then performing a scale gesture is handled correctly. - * - * - It doesn't take pressure into account, which results in smoother scaling. - */ -public class SimpleScaleGestureDetector { - private static final String LOGTAG = "GeckoSimpleScaleGestureDetector"; - - private SimpleScaleGestureListener mListener; - private long mLastEventTime; - - /* Information about all pointers that are down. */ - private LinkedList<PointerInfo> mPointerInfo; - - /** Creates a new gesture detector with the given listener. */ - public SimpleScaleGestureDetector(SimpleScaleGestureListener listener) { - mListener = listener; - mPointerInfo = new LinkedList<PointerInfo>(); - } - - /** Forward touch events to this function. */ - public void onTouchEvent(MotionEvent event) { - switch (event.getAction() & MotionEvent.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - onTouchStart(event); - break; - case MotionEvent.ACTION_MOVE: - onTouchMove(event); - break; - case MotionEvent.ACTION_POINTER_UP: - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: - onTouchEnd(event); - break; - } - } - - private int getPointersDown() { - return mPointerInfo.size(); - } - - private int getActionIndex(MotionEvent event) { - return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) - >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; - } - - private void onTouchStart(MotionEvent event) { - mLastEventTime = event.getEventTime(); - mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event))); - if (getPointersDown() == 2) { - sendScaleGesture(EventType.BEGIN); - } - } - - private void onTouchMove(MotionEvent event) { - mLastEventTime = event.getEventTime(); - for (int i = 0; i < event.getPointerCount(); i++) { - PointerInfo pointerInfo = pointerInfoForEventIndex(event, i); - if (pointerInfo != null) { - pointerInfo.populate(event, i); - } - } - - if (getPointersDown() == 2) { - sendScaleGesture(EventType.CONTINUE); - } - } - - private void onTouchEnd(MotionEvent event) { - mLastEventTime = event.getEventTime(); - - int id = event.getPointerId(getActionIndex(event)); - ListIterator<PointerInfo> iterator = mPointerInfo.listIterator(); - while (iterator.hasNext()) { - PointerInfo pointerInfo = iterator.next(); - if (pointerInfo.getId() != id) { - continue; - } - - // One of the pointers we were tracking was lifted. Remove its info object from the - // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this - // ended the gesture. - iterator.remove(); - pointerInfo.recycle(); - if (getPointersDown() == 1) { - sendScaleGesture(EventType.END); - } - } - } - - /** - * Returns the X coordinate of the focus location (the midpoint of the two fingers). If only - * one finger is down, returns the location of that finger. - */ - public float getFocusX() { - switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().x; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f; - } - - Log.e(LOGTAG, "No gesture taking place in getFocusX()!"); - return 0.0f; - } - - /** - * Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only - * one finger is down, returns the location of that finger. - */ - public float getFocusY() { - switch (getPointersDown()) { - case 1: - return mPointerInfo.getFirst().getCurrent().y; - case 2: - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f; - } - - Log.e(LOGTAG, "No gesture taking place in getFocusY()!"); - return 0.0f; - } - - /** Returns the most recent distance between the two pointers. */ - public float getCurrentSpan() { - if (getPointersDown() != 2) { - Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!"); - return 0.0f; - } - - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent()); - } - - /** Returns the second most recent distance between the two pointers. */ - public float getPreviousSpan() { - if (getPointersDown() != 2) { - Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!"); - return 0.0f; - } - - PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); - PointF a = pointerA.getPrevious(), b = pointerB.getPrevious(); - if (a == null || b == null) { - a = pointerA.getCurrent(); - b = pointerB.getCurrent(); - } - - return PointUtils.distance(a, b); - } - - /** Returns the time of the last event related to the gesture. */ - public long getEventTime() { - return mLastEventTime; - } - - /** Returns true if the scale gesture is in progress and false otherwise. */ - public boolean isInProgress() { - return getPointersDown() == 2; - } - - /* Sends the requested scale gesture notification to the listener. */ - private void sendScaleGesture(EventType eventType) { - switch (eventType) { - case BEGIN: mListener.onScaleBegin(this); break; - case CONTINUE: mListener.onScale(this); break; - case END: mListener.onScaleEnd(this); break; - } - } - - /* - * Returns the pointer info corresponding to the given pointer index, or null if the pointer - * isn't one that's being tracked. - */ - private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) { - int id = event.getPointerId(index); - for (PointerInfo pointerInfo : mPointerInfo) { - if (pointerInfo.getId() == id) { - return pointerInfo; - } - } - return null; - } - - private enum EventType { - BEGIN, - CONTINUE, - END, - } - - /* Encapsulates information about one of the two fingers involved in the gesture. */ - private static class PointerInfo { - /* A free list that recycles pointer info objects, to reduce GC pauses. */ - private static Stack<PointerInfo> sPointerInfoFreeList; - - private int mId; - private PointF mCurrent, mPrevious; - - private PointerInfo() { - // External users should use create() instead. - } - - /* Creates or recycles a new PointerInfo instance from an event and a pointer index. */ - public static PointerInfo create(MotionEvent event, int index) { - if (sPointerInfoFreeList == null) { - sPointerInfoFreeList = new Stack<PointerInfo>(); - } - - PointerInfo pointerInfo; - if (sPointerInfoFreeList.empty()) { - pointerInfo = new PointerInfo(); - } else { - pointerInfo = sPointerInfoFreeList.pop(); - } - - pointerInfo.populate(event, index); - return pointerInfo; - } - - /* - * Fills in the fields of this instance from the given motion event and pointer index - * within that event. - */ - public void populate(MotionEvent event, int index) { - mId = event.getPointerId(index); - mPrevious = mCurrent; - mCurrent = new PointF(event.getX(index), event.getY(index)); - } - - public void recycle() { - mId = -1; - mPrevious = mCurrent = null; - sPointerInfoFreeList.push(this); - } - - public int getId() { return mId; } - public PointF getCurrent() { return mCurrent; } - public PointF getPrevious() { return mPrevious; } - - @Override - public String toString() { - if (mId == -1) { - return "(up)"; - } - - try { - String prevString; - if (mPrevious == null) { - prevString = "n/a"; - } else { - prevString = PointUtils.toJSON(mPrevious).toString(); - } - - // The current position should always be non-null. - String currentString = PointUtils.toJSON(mCurrent).toString(); - return "id=" + mId + " cur=" + currentString + " prev=" + prevString; - } catch (JSONException e) { - throw new RuntimeException(e); - } - } - } - - public static interface SimpleScaleGestureListener { - public boolean onScale(SimpleScaleGestureDetector detector); - public boolean onScaleBegin(SimpleScaleGestureDetector detector); - public void onScaleEnd(SimpleScaleGestureDetector detector); - } -} - |