summaryrefslogtreecommitdiffstats
path: root/core
diff options
context:
space:
mode:
authorAdam Powell <adamp@google.com>2010-01-13 16:29:27 -0800
committerAdam Powell <adamp@google.com>2010-01-13 17:08:31 -0800
commitae542ff055301a4c3c8a18e8da1739df3a771958 (patch)
tree9e57899a12bd08fab4c83d2b8cb671d959469d2d /core
parent1193ae4e824c66b75083c444ce0f250594e138ee (diff)
downloadframeworks_base-ae542ff055301a4c3c8a18e8da1739df3a771958.zip
frameworks_base-ae542ff055301a4c3c8a18e8da1739df3a771958.tar.gz
frameworks_base-ae542ff055301a4c3c8a18e8da1739df3a771958.tar.bz2
TransformGestureDetector is now ScaleGestureDetector - scope reduced.
N1 screen can't reliably handle translation and scaling at the same time.
Diffstat (limited to 'core')
-rw-r--r--core/java/android/view/ScaleGestureDetector.java360
-rw-r--r--core/java/android/view/TransformGestureDetector.java316
2 files changed, 360 insertions, 316 deletions
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
new file mode 100644
index 0000000..2da8f14
--- /dev/null
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
+ * callback will notify users when a particular gesture event has occurred.
+ * This class should only be used with {@link MotionEvent}s reported via touch.
+ *
+ * To use this class:
+ * <ul>
+ * <li>Create an instance of the {@code ScaleGestureDetector} for your
+ * {@link View}
+ * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ * callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class ScaleGestureDetector {
+ /**
+ * The listener for receiving notifications when gestures occur.
+ * If you want to listen for all the different gestures then implement
+ * this interface. If you only want to listen for a subset it might
+ * be easier to extend {@link SimpleOnScaleGestureListener}.
+ *
+ * An application will receive events in the following order:
+ * <ul>
+ * <li>One {@link OnScaleGestureListener#onScaleBegin()}
+ * <li>Zero or more {@link OnScaleGestureListener#onScale()}
+ * <li>One {@link OnScaleGestureListener#onTransformEnd()}
+ * </ul>
+ */
+ public interface OnScaleGestureListener {
+ /**
+ * Responds to scaling events for a gesture in progress.
+ * Reported by pointer motion.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should consider this event
+ * as handled. If an event was not handled, the detector
+ * will continue to accumulate movement until an event is
+ * handled. This can be useful if an application, for example,
+ * only wants to update scaling factors if the change is
+ * greater than 0.01.
+ */
+ public boolean onScale(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the beginning of a scaling gesture. Reported by
+ * new pointers going down.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ * @return Whether or not the detector should continue recognizing
+ * this gesture. For example, if a gesture is beginning
+ * with a focal point outside of a region where it makes
+ * sense, onScaleBegin() may return false to ignore the
+ * rest of the gesture.
+ */
+ public boolean onScaleBegin(ScaleGestureDetector detector);
+
+ /**
+ * Responds to the end of a scale gesture. Reported by existing
+ * pointers going up. If the end of a gesture would result in a fling,
+ * {@link onTransformFling()} is called instead.
+ *
+ * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
+ * and {@link ScaleGestureDetector#getFocusY()} will return the location
+ * of the pointer remaining on the screen.
+ *
+ * @param detector The detector reporting the event - use this to
+ * retrieve extended info about event state.
+ */
+ public void onScaleEnd(ScaleGestureDetector detector);
+ }
+
+ /**
+ * A convenience class to extend when you only want to listen for a subset
+ * of scaling-related events. This implements all methods in
+ * {@link OnScaleGestureListener} but does nothing.
+ * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
+ * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
+ * {@code true}.
+ */
+ public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+
+ public boolean onScale(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public boolean onScaleBegin(ScaleGestureDetector detector) {
+ return true;
+ }
+
+ public void onScaleEnd(ScaleGestureDetector detector) {
+ // Intentionally empty
+ }
+ }
+
+ private static final float PRESSURE_THRESHOLD = 0.67f;
+
+ private Context mContext;
+ private OnScaleGestureListener mListener;
+ private boolean mGestureInProgress;
+
+ private MotionEvent mPrevEvent;
+ private MotionEvent mCurrEvent;
+
+ private float mFocusX;
+ private float mFocusY;
+ private float mPrevFingerDiffX;
+ private float mPrevFingerDiffY;
+ private float mCurrFingerDiffX;
+ private float mCurrFingerDiffY;
+ private float mCurrLen;
+ private float mPrevLen;
+ private float mScaleFactor;
+ private float mCurrPressure;
+ private float mPrevPressure;
+ private long mTimeDelta;
+
+ public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ mContext = context;
+ mListener = listener;
+ }
+
+ public boolean onTouchEvent(MotionEvent event) {
+ final int action = event.getAction();
+ boolean handled = true;
+
+ if (!mGestureInProgress) {
+ if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+ action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+ event.getPointerCount() >= 2) {
+ // We have a new multi-finger gesture
+
+ // Be paranoid in case we missed an event
+ reset();
+
+ mPrevEvent = MotionEvent.obtain(event);
+ mTimeDelta = 0;
+
+ setContext(event);
+ mGestureInProgress = mListener.onScaleBegin(this);
+ }
+ } else {
+ // Transform gesture in progress - attempt to handle it
+ switch (action) {
+ case MotionEvent.ACTION_POINTER_1_UP:
+ case MotionEvent.ACTION_POINTER_2_UP:
+ // Gesture ended
+ setContext(event);
+
+ // Set focus point to the remaining finger
+ int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
+ >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+ mFocusX = event.getX(id);
+ mFocusY = event.getY(id);
+
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ mListener.onScaleEnd(this);
+ mGestureInProgress = false;
+
+ reset();
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ setContext(event);
+
+ // Only accept the event if our relative pressure is within
+ // a certain limit - this can help filter shaky data as a
+ // finger is lifted.
+ if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
+ final boolean updatePrevious = mListener.onScale(this);
+
+ if (updatePrevious) {
+ mPrevEvent.recycle();
+ mPrevEvent = MotionEvent.obtain(event);
+ }
+ }
+ break;
+ }
+ }
+ return handled;
+ }
+
+ private void setContext(MotionEvent curr) {
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ }
+ mCurrEvent = MotionEvent.obtain(curr);
+
+ mCurrLen = -1;
+ mPrevLen = -1;
+ mScaleFactor = -1;
+
+ final MotionEvent prev = mPrevEvent;
+
+ final float px0 = prev.getX(0);
+ final float py0 = prev.getY(0);
+ final float px1 = prev.getX(1);
+ final float py1 = prev.getY(1);
+ final float cx0 = curr.getX(0);
+ final float cy0 = curr.getY(0);
+ final float cx1 = curr.getX(1);
+ final float cy1 = curr.getY(1);
+
+ final float pvx = px1 - px0;
+ final float pvy = py1 - py0;
+ final float cvx = cx1 - cx0;
+ final float cvy = cy1 - cy0;
+ mPrevFingerDiffX = pvx;
+ mPrevFingerDiffY = pvy;
+ mCurrFingerDiffX = cvx;
+ mCurrFingerDiffY = cvy;
+
+ mFocusX = cx0 + cvx * 0.5f;
+ mFocusY = cy0 + cvy * 0.5f;
+ mTimeDelta = curr.getEventTime() - prev.getEventTime();
+ mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
+ mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
+ }
+
+ private void reset() {
+ if (mPrevEvent != null) {
+ mPrevEvent.recycle();
+ mPrevEvent = null;
+ }
+ if (mCurrEvent != null) {
+ mCurrEvent.recycle();
+ mCurrEvent = null;
+ }
+ }
+
+ /**
+ * Returns {@code true} if a two-finger scale gesture is in progress.
+ * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+ */
+ public boolean isInProgress() {
+ return mGestureInProgress;
+ }
+
+ /**
+ * Get the X coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return X coordinate of the focal point in pixels.
+ */
+ public float getFocusX() {
+ return mFocusX;
+ }
+
+ /**
+ * Get the Y coordinate of the current gesture's focal point.
+ * If a gesture is in progress, the focal point is directly between
+ * the two pointers forming the gesture.
+ * If a gesture is ending, the focal point is the location of the
+ * remaining pointer on the screen.
+ * If {@link isInProgress()} would return false, the result of this
+ * function is undefined.
+ *
+ * @return Y coordinate of the focal point in pixels.
+ */
+ public float getFocusY() {
+ return mFocusY;
+ }
+
+ /**
+ * Return the current distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Distance between pointers in pixels.
+ */
+ public float getCurrentSpan() {
+ if (mCurrLen == -1) {
+ final float cvx = mCurrFingerDiffX;
+ final float cvy = mCurrFingerDiffY;
+ mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+ }
+ return mCurrLen;
+ }
+
+ /**
+ * Return the previous distance between the two pointers forming the
+ * gesture in progress.
+ *
+ * @return Previous distance between pointers in pixels.
+ */
+ public float getPreviousSpan() {
+ if (mPrevLen == -1) {
+ final float pvx = mPrevFingerDiffX;
+ final float pvy = mPrevFingerDiffY;
+ mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+ }
+ return mPrevLen;
+ }
+
+ /**
+ * Return the scaling factor from the previous scale event to the current
+ * event. This value is defined as
+ * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+ *
+ * @return The current scaling factor.
+ */
+ public float getScaleFactor() {
+ if (mScaleFactor == -1) {
+ mScaleFactor = getCurrentSpan() / getPreviousSpan();
+ }
+ return mScaleFactor;
+ }
+
+ /**
+ * Return the time difference in milliseconds between the previous
+ * accepted scaling event and the current scaling event.
+ *
+ * @return Time difference since the last scaling event in milliseconds.
+ */
+ public long getTimeDelta() {
+ return mTimeDelta;
+ }
+
+ /**
+ * Return the event time of the current event being processed.
+ *
+ * @return Current event time in milliseconds.
+ */
+ public long getEventTime() {
+ return mCurrEvent.getEventTime();
+ }
+}
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
deleted file mode 100644
index 196716a..0000000
--- a/core/java/android/view/TransformGestureDetector.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector.SimpleOnGestureListener;
-
-/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
- * will notify users when a particular gesture event has occurred. This class
- * should only be used with {@link MotionEvent}s reported via touch.
- *
- * To use this class:
- * <ul>
- * <li>Create an instance of the {@code TransformGestureDetector} for your
- * {@link View}
- * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- * {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- * callback will be executed when the events occur.
- * </ul>
- * @hide Pending API approval
- */
-public class TransformGestureDetector {
- /**
- * The listener for receiving notifications when gestures occur.
- * If you want to listen for all the different gestures then implement
- * this interface. If you only want to listen for a subset it might
- * be easier to extend {@link SimpleOnGestureListener}.
- *
- * An application will receive events in the following order:
- * One onTransformBegin()
- * Zero or more onTransform()
- * One onTransformEnd() or onTransformFling()
- */
- public interface OnTransformGestureListener {
- /**
- * Responds to transformation events for a gesture in progress.
- * Reported by pointer motion.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransform(TransformGestureDetector detector);
-
- /**
- * Responds to the beginning of a transformation gesture. Reported by
- * new pointers going down.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformBegin(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture. Reported by existing
- * pointers going up. If the end of a gesture would result in a fling,
- * onTransformFling is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformEnd(TransformGestureDetector detector);
-
- /**
- * Responds to the end of a transformation gesture that begins a fling.
- * Reported by existing pointers going up. If the end of a gesture
- * would not result in a fling, onTransformEnd is called instead.
- *
- * @param detector The detector reporting the event - use this to
- * retrieve extended info about event state.
- * @return true if the event was handled, false otherwise.
- */
- public boolean onTransformFling(TransformGestureDetector detector);
- }
-
- private static final boolean DEBUG = false;
-
- private static final int INITIAL_EVENT_IGNORES = 2;
-
- private Context mContext;
- private float mTouchSizeScale;
- private OnTransformGestureListener mListener;
- private int mVelocityTimeUnits;
- private MotionEvent mInitialEvent;
-
- private MotionEvent mPrevEvent;
- private MotionEvent mCurrEvent;
- private VelocityTracker mVelocityTracker;
-
- private float mCenterX;
- private float mCenterY;
- private float mTransX;
- private float mTransY;
- private float mPrevFingerDiffX;
- private float mPrevFingerDiffY;
- private float mCurrFingerDiffX;
- private float mCurrFingerDiffY;
- private float mRotateDegrees;
- private float mCurrLen;
- private float mPrevLen;
- private float mScaleFactor;
-
- // Units in pixels. Current value is pulled out of thin air for debugging only.
- private float mPointerJumpLimit = 30;
-
- private int mEventIgnoreCount;
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener,
- int velocityTimeUnits) {
- mContext = context;
- mListener = listener;
- mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
- mVelocityTimeUnits = velocityTimeUnits;
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
- this(context, listener, 1000);
- }
-
- public boolean onTouchEvent(MotionEvent event) {
- final int action = event.getAction();
- boolean handled = true;
-
- if (mInitialEvent == null) {
- // No transform gesture in progress
- if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
- action == MotionEvent.ACTION_POINTER_2_DOWN) &&
- event.getPointerCount() >= 2) {
- // We have a new multi-finger gesture
- mInitialEvent = MotionEvent.obtain(event);
- mPrevEvent = MotionEvent.obtain(event);
- mVelocityTracker = VelocityTracker.obtain();
- handled = mListener.onTransformBegin(this);
- }
- } else {
- // Transform gesture in progress - attempt to handle it
- switch (action) {
- case MotionEvent.ACTION_POINTER_1_UP:
- case MotionEvent.ACTION_POINTER_2_UP:
- // Gesture ended
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_CANCEL:
- handled = mListener.onTransformEnd(this);
-
- reset();
- break;
-
- case MotionEvent.ACTION_MOVE:
- setContext(event);
-
- // Our first few events can be crazy from some touchscreens - drop them.
- if (mEventIgnoreCount == 0) {
- mVelocityTracker.addMovement(event);
- handled = mListener.onTransform(this);
- } else {
- mEventIgnoreCount--;
- }
-
- mPrevEvent.recycle();
- mPrevEvent = MotionEvent.obtain(event);
- break;
- }
- }
- return handled;
- }
-
- private void setContext(MotionEvent curr) {
- mCurrEvent = MotionEvent.obtain(curr);
-
- mRotateDegrees = -1;
- mCurrLen = -1;
- mPrevLen = -1;
- mScaleFactor = -1;
-
- final MotionEvent prev = mPrevEvent;
-
- float px0 = prev.getX(0);
- float py0 = prev.getY(0);
- float px1 = prev.getX(1);
- float py1 = prev.getY(1);
- float cx0 = curr.getX(0);
- float cy0 = curr.getY(0);
- float cx1 = curr.getX(1);
- float cy1 = curr.getY(1);
-
- // Some touchscreens do weird things with pointer values where points are
- // too close along one axis. Try to detect this here and smooth things out.
- // The main indicator is that we get the X or Y value from the other pointer.
- final float dx0 = cx0 - px0;
- final float dy0 = cy0 - py0;
- final float dx1 = cx1 - px1;
- final float dy1 = cy1 - py1;
-
- if (cx0 == cx1) {
- if (Math.abs(dx0) > mPointerJumpLimit) {
- cx0 = px0;
- } else if (Math.abs(dx1) > mPointerJumpLimit) {
- cx1 = px1;
- }
- } else if (cy0 == cy1) {
- if (Math.abs(dy0) > mPointerJumpLimit) {
- cy0 = py0;
- } else if (Math.abs(dy1) > mPointerJumpLimit) {
- cy1 = py1;
- }
- }
-
- final float pvx = px1 - px0;
- final float pvy = py1 - py0;
- final float cvx = cx1 - cx0;
- final float cvy = cy1 - cy0;
- mPrevFingerDiffX = pvx;
- mPrevFingerDiffY = pvy;
- mCurrFingerDiffX = cvx;
- mCurrFingerDiffY = cvy;
-
- final float pmidx = px0 + pvx * 0.5f;
- final float pmidy = py0 + pvy * 0.5f;
- final float cmidx = cx0 + cvx * 0.5f;
- final float cmidy = cy0 + cvy * 0.5f;
-
- mCenterX = cmidx;
- mCenterY = cmidy;
- mTransX = cmidx - pmidx;
- mTransY = cmidy - pmidy;
- }
-
- private void reset() {
- if (mInitialEvent != null) {
- mInitialEvent.recycle();
- mInitialEvent = null;
- }
- if (mPrevEvent != null) {
- mPrevEvent.recycle();
- mPrevEvent = null;
- }
- if (mCurrEvent != null) {
- mCurrEvent.recycle();
- mCurrEvent = null;
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mEventIgnoreCount = INITIAL_EVENT_IGNORES;
- }
-
- public float getCenterX() {
- return mCenterX;
- }
-
- public float getCenterY() {
- return mCenterY;
- }
-
- public float getTranslateX() {
- return mTransX;
- }
-
- public float getTranslateY() {
- return mTransY;
- }
-
- public float getCurrentSpan() {
- if (mCurrLen == -1) {
- final float cvx = mCurrFingerDiffX;
- final float cvy = mCurrFingerDiffY;
- mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
- }
- return mCurrLen;
- }
-
- public float getPreviousSpan() {
- if (mPrevLen == -1) {
- final float pvx = mPrevFingerDiffX;
- final float pvy = mPrevFingerDiffY;
- mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
- }
- return mPrevLen;
- }
-
- public float getScaleFactor() {
- if (mScaleFactor == -1) {
- mScaleFactor = getCurrentSpan() / getPreviousSpan();
- }
- return mScaleFactor;
- }
-
- public float getRotation() {
- throw new UnsupportedOperationException();
- }
-}