Loading core/java/android/view/TransformGestureDetector.java→core/java/android/view/ScaleGestureDetector.java +360 −0 Original line number Diff line number Diff line Loading @@ -17,18 +17,16 @@ 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. * 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 TransformGestureDetector} for your * <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 Loading @@ -36,121 +34,133 @@ import android.view.GestureDetector.SimpleOnGestureListener; * </ul> * @hide Pending API approval */ public class TransformGestureDetector { 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 SimpleOnGestureListener}. * be easier to extend {@link SimpleOnScaleGestureListener}. * * An application will receive events in the following order: * One onTransformBegin() * Zero or more onTransform() * One onTransformEnd() or onTransformFling() * <ul> * <li>One {@link OnScaleGestureListener#onScaleBegin()} * <li>Zero or more {@link OnScaleGestureListener#onScale()} * <li>One {@link OnScaleGestureListener#onTransformEnd()} * </ul> */ public interface OnTransformGestureListener { public interface OnScaleGestureListener { /** * Responds to transformation events for a gesture in progress. * 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 true if the event was handled, false otherwise. * @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 onTransform(TransformGestureDetector detector); public boolean onScale(ScaleGestureDetector detector); /** * Responds to the beginning of a transformation gesture. Reported by * 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 true if the event was handled, false otherwise. * @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 onTransformBegin(TransformGestureDetector detector); public boolean onScaleBegin(ScaleGestureDetector detector); /** * Responds to the end of a transformation gesture. Reported by existing * 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, * onTransformFling is called instead. * {@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. * @return true if the event was handled, false otherwise. */ public boolean onTransformEnd(TransformGestureDetector detector); public void onScaleEnd(ScaleGestureDetector 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. * 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 boolean onTransformFling(TransformGestureDetector detector); public class SimpleOnScaleGestureListener implements OnScaleGestureListener { public boolean onScale(ScaleGestureDetector detector) { return true; } private static final boolean DEBUG = false; public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } public void onScaleEnd(ScaleGestureDetector detector) { // Intentionally empty } } private static final int INITIAL_EVENT_IGNORES = 2; private static final float PRESSURE_THRESHOLD = 0.67f; private Context mContext; private float mTouchSizeScale; private OnTransformGestureListener mListener; private int mVelocityTimeUnits; private MotionEvent mInitialEvent; private OnScaleGestureListener mListener; private boolean mGestureInProgress; private MotionEvent mPrevEvent; private MotionEvent mCurrEvent; private VelocityTracker mVelocityTracker; private float mCenterX; private float mCenterY; private float mTransX; private float mTransY; private float mFocusX; private float mFocusY; private float mPrevFingerDiffX; private float mPrevFingerDiffY; private float mCurrFingerDiffX; private float mCurrFingerDiffY; private float mRotateDegrees; private float mCurrLen; private float mPrevLen; private float mScaleFactor; private float mCurrPressure; private float mPrevPressure; private long mTimeDelta; // 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) { public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 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 (!mGestureInProgress) { 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); // Be paranoid in case we missed an event reset(); mPrevEvent = MotionEvent.obtain(event); mVelocityTracker = VelocityTracker.obtain(); handled = mListener.onTransformBegin(this); mTimeDelta = 0; setContext(event); mGestureInProgress = mListener.onScaleBegin(this); } } else { // Transform gesture in progress - attempt to handle it Loading @@ -158,13 +168,23 @@ public class TransformGestureDetector { case MotionEvent.ACTION_POINTER_1_UP: case MotionEvent.ACTION_POINTER_2_UP: // Gesture ended handled = mListener.onTransformEnd(this); 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: handled = mListener.onTransformEnd(this); mListener.onScaleEnd(this); mGestureInProgress = false; reset(); break; Loading @@ -172,16 +192,17 @@ public class TransformGestureDetector { 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--; } // 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; } } Loading @@ -189,45 +210,25 @@ public class TransformGestureDetector { } private void setContext(MotionEvent curr) { if (mCurrEvent != null) { mCurrEvent.recycle(); } 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 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; Loading @@ -238,22 +239,14 @@ public class TransformGestureDetector { 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; 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 (mInitialEvent != null) { mInitialEvent.recycle(); mInitialEvent = null; } if (mPrevEvent != null) { mPrevEvent.recycle(); mPrevEvent = null; Loading @@ -262,29 +255,52 @@ public class TransformGestureDetector { 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; /** * 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; } public float getTranslateX() { return mTransX; /** * 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; } public float getTranslateY() { return mTransY; /** * 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; Loading @@ -294,6 +310,12 @@ public class TransformGestureDetector { 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; Loading @@ -303,6 +325,13 @@ public class TransformGestureDetector { 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(); Loading @@ -310,7 +339,22 @@ public class TransformGestureDetector { return mScaleFactor; } public float getRotation() { throw new UnsupportedOperationException(); /** * 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(); } } core/java/android/webkit/WebSettings.java +2 −0 Original line number Diff line number Diff line Loading @@ -411,6 +411,7 @@ public class WebSettings { */ public void setSupportZoom(boolean support) { mSupportZoom = support; mWebView.updateMultiTouchSupport(mContext); } /** Loading @@ -425,6 +426,7 @@ public class WebSettings { */ public void setBuiltInZoomControls(boolean enabled) { mBuiltInZoomControls = enabled; mWebView.updateMultiTouchSupport(mContext); } /** Loading core/java/android/webkit/WebView.java +56 −56 File changed.Preview size limit exceeded, changes collapsed. Show changes docs/html/sdk/download.jd +2 −4 Original line number Diff line number Diff line Loading @@ -58,10 +58,8 @@ The License Agreement constitutes a contract between you and Google with respect <h2>Thank you for downloading the Android SDK!</h2> <p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p> <p>To set up your Android development environment, please read the guide to <a href="installing.html">Installing the Android SDK</a>. Once you have completed the installation, see the <a href="/guide/index.html">Dev Guide</a> for documentation about developing Android applications.</p> <a href="installing.html">Installing the Android SDK</a> and ensure that your development machine meets the system requirements linked on that page.</p> </div> <script type="text/javascript"> Loading tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java +47 −29 Original line number Diff line number Diff line Loading @@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.TransformGestureDetector; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.LinearLayout; Loading @@ -49,9 +48,6 @@ public class TransformTestActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LayoutInflater li = (LayoutInflater)getSystemService( LAYOUT_INFLATER_SERVICE); this.setTitle(R.string.act_title); LinearLayout root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); Loading @@ -71,15 +67,19 @@ public class TransformTestActivity extends Activity { private float mPosY; private float mScale = 1.f; private Matrix mMatrix; private TransformGestureDetector mDetector; private ScaleGestureDetector mDetector; private float mLastX; private float mLastY; private class Listener implements TransformGestureDetector.OnTransformGestureListener { private class Listener implements ScaleGestureDetector.OnScaleGestureListener { public boolean onTransform(TransformGestureDetector detector) { Log.d("ttest", "Translation: (" + detector.getTranslateX() + ", " + detector.getTranslateY() + ")"); public boolean onScale(ScaleGestureDetector detector) { float scale = detector.getScaleFactor(); Log.d("ttest", "Scale: " + scale); // Limit the scale so our object doesn't get too big or disappear if (mScale * scale > 0.1f) { if (mScale * scale < 10.f) { mScale *= scale; Loading @@ -90,15 +90,12 @@ public class TransformTestActivity extends Activity { mScale = 0.1f; } mPosX += detector.getTranslateX(); mPosY += detector.getTranslateY(); Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")"); float sizeX = mDrawable.getIntrinsicWidth()/2; float sizeY = mDrawable.getIntrinsicHeight()/2; float centerX = detector.getCenterX(); float centerY = detector.getCenterY(); float centerX = detector.getFocusX(); float centerY = detector.getFocusY(); float diffX = centerX - mPosX; float diffY = centerY - mPosY; diffX = diffX*scale - diffX; Loading @@ -115,24 +112,20 @@ public class TransformTestActivity extends Activity { return true; } public boolean onTransformBegin(TransformGestureDetector detector) { public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } public boolean onTransformEnd(TransformGestureDetector detector) { return true; public void onScaleEnd(ScaleGestureDetector detector) { mLastX = detector.getFocusX(); mLastY = detector.getFocusY(); } public boolean onTransformFling(TransformGestureDetector detector) { return false; } } public TransformView(Context context) { super(context); mMatrix = new Matrix(); mDetector = new TransformGestureDetector(context, new Listener()); mDetector = new ScaleGestureDetector(context, new Listener()); DisplayMetrics metrics = context.getResources().getDisplayMetrics(); mPosX = metrics.widthPixels/2; mPosY = metrics.heightPixels/2; Loading @@ -151,12 +144,37 @@ public class TransformTestActivity extends Activity { @Override public boolean onTouchEvent(MotionEvent event) { boolean handled = mDetector.onTouchEvent(event); mDetector.onTouchEvent(event); // Handling single finger pan if (!mDetector.isInProgress()) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = event.getX(); mLastY = event.getY(); break; case MotionEvent.ACTION_MOVE: final float x = event.getX(); final float y = event.getY(); mPosX += x - mLastX; mPosY += y - mLastY; mLastX = x; mLastY = y; float sizeX = mDrawable.getIntrinsicWidth()/2; float sizeY = mDrawable.getIntrinsicHeight()/2; int pointerCount = event.getPointerCount(); Log.d("ttest", "pointerCount: " + pointerCount); mMatrix.reset(); mMatrix.postTranslate(-sizeX, -sizeY); mMatrix.postScale(mScale, mScale); mMatrix.postTranslate(mPosX, mPosY); invalidate(); break; } } return handled; return true; } @Override Loading Loading
core/java/android/view/TransformGestureDetector.java→core/java/android/view/ScaleGestureDetector.java +360 −0 Original line number Diff line number Diff line Loading @@ -17,18 +17,16 @@ 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. * 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 TransformGestureDetector} for your * <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 Loading @@ -36,121 +34,133 @@ import android.view.GestureDetector.SimpleOnGestureListener; * </ul> * @hide Pending API approval */ public class TransformGestureDetector { 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 SimpleOnGestureListener}. * be easier to extend {@link SimpleOnScaleGestureListener}. * * An application will receive events in the following order: * One onTransformBegin() * Zero or more onTransform() * One onTransformEnd() or onTransformFling() * <ul> * <li>One {@link OnScaleGestureListener#onScaleBegin()} * <li>Zero or more {@link OnScaleGestureListener#onScale()} * <li>One {@link OnScaleGestureListener#onTransformEnd()} * </ul> */ public interface OnTransformGestureListener { public interface OnScaleGestureListener { /** * Responds to transformation events for a gesture in progress. * 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 true if the event was handled, false otherwise. * @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 onTransform(TransformGestureDetector detector); public boolean onScale(ScaleGestureDetector detector); /** * Responds to the beginning of a transformation gesture. Reported by * 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 true if the event was handled, false otherwise. * @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 onTransformBegin(TransformGestureDetector detector); public boolean onScaleBegin(ScaleGestureDetector detector); /** * Responds to the end of a transformation gesture. Reported by existing * 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, * onTransformFling is called instead. * {@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. * @return true if the event was handled, false otherwise. */ public boolean onTransformEnd(TransformGestureDetector detector); public void onScaleEnd(ScaleGestureDetector 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. * 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 boolean onTransformFling(TransformGestureDetector detector); public class SimpleOnScaleGestureListener implements OnScaleGestureListener { public boolean onScale(ScaleGestureDetector detector) { return true; } private static final boolean DEBUG = false; public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } public void onScaleEnd(ScaleGestureDetector detector) { // Intentionally empty } } private static final int INITIAL_EVENT_IGNORES = 2; private static final float PRESSURE_THRESHOLD = 0.67f; private Context mContext; private float mTouchSizeScale; private OnTransformGestureListener mListener; private int mVelocityTimeUnits; private MotionEvent mInitialEvent; private OnScaleGestureListener mListener; private boolean mGestureInProgress; private MotionEvent mPrevEvent; private MotionEvent mCurrEvent; private VelocityTracker mVelocityTracker; private float mCenterX; private float mCenterY; private float mTransX; private float mTransY; private float mFocusX; private float mFocusY; private float mPrevFingerDiffX; private float mPrevFingerDiffY; private float mCurrFingerDiffX; private float mCurrFingerDiffY; private float mRotateDegrees; private float mCurrLen; private float mPrevLen; private float mScaleFactor; private float mCurrPressure; private float mPrevPressure; private long mTimeDelta; // 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) { public ScaleGestureDetector(Context context, OnScaleGestureListener listener) { 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 (!mGestureInProgress) { 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); // Be paranoid in case we missed an event reset(); mPrevEvent = MotionEvent.obtain(event); mVelocityTracker = VelocityTracker.obtain(); handled = mListener.onTransformBegin(this); mTimeDelta = 0; setContext(event); mGestureInProgress = mListener.onScaleBegin(this); } } else { // Transform gesture in progress - attempt to handle it Loading @@ -158,13 +168,23 @@ public class TransformGestureDetector { case MotionEvent.ACTION_POINTER_1_UP: case MotionEvent.ACTION_POINTER_2_UP: // Gesture ended handled = mListener.onTransformEnd(this); 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: handled = mListener.onTransformEnd(this); mListener.onScaleEnd(this); mGestureInProgress = false; reset(); break; Loading @@ -172,16 +192,17 @@ public class TransformGestureDetector { 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--; } // 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; } } Loading @@ -189,45 +210,25 @@ public class TransformGestureDetector { } private void setContext(MotionEvent curr) { if (mCurrEvent != null) { mCurrEvent.recycle(); } 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 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; Loading @@ -238,22 +239,14 @@ public class TransformGestureDetector { 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; 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 (mInitialEvent != null) { mInitialEvent.recycle(); mInitialEvent = null; } if (mPrevEvent != null) { mPrevEvent.recycle(); mPrevEvent = null; Loading @@ -262,29 +255,52 @@ public class TransformGestureDetector { 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; /** * 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; } public float getTranslateX() { return mTransX; /** * 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; } public float getTranslateY() { return mTransY; /** * 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; Loading @@ -294,6 +310,12 @@ public class TransformGestureDetector { 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; Loading @@ -303,6 +325,13 @@ public class TransformGestureDetector { 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(); Loading @@ -310,7 +339,22 @@ public class TransformGestureDetector { return mScaleFactor; } public float getRotation() { throw new UnsupportedOperationException(); /** * 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(); } }
core/java/android/webkit/WebSettings.java +2 −0 Original line number Diff line number Diff line Loading @@ -411,6 +411,7 @@ public class WebSettings { */ public void setSupportZoom(boolean support) { mSupportZoom = support; mWebView.updateMultiTouchSupport(mContext); } /** Loading @@ -425,6 +426,7 @@ public class WebSettings { */ public void setBuiltInZoomControls(boolean enabled) { mBuiltInZoomControls = enabled; mWebView.updateMultiTouchSupport(mContext); } /** Loading
core/java/android/webkit/WebView.java +56 −56 File changed.Preview size limit exceeded, changes collapsed. Show changes
docs/html/sdk/download.jd +2 −4 Original line number Diff line number Diff line Loading @@ -58,10 +58,8 @@ The License Agreement constitutes a contract between you and Google with respect <h2>Thank you for downloading the Android SDK!</h2> <p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p> <p>To set up your Android development environment, please read the guide to <a href="installing.html">Installing the Android SDK</a>. Once you have completed the installation, see the <a href="/guide/index.html">Dev Guide</a> for documentation about developing Android applications.</p> <a href="installing.html">Installing the Android SDK</a> and ensure that your development machine meets the system requirements linked on that page.</p> </div> <script type="text/javascript"> Loading
tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java +47 −29 Original line number Diff line number Diff line Loading @@ -24,9 +24,8 @@ import android.graphics.drawable.Drawable; import android.os.Bundle; import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.TransformGestureDetector; import android.view.ScaleGestureDetector; import android.view.View; import android.widget.LinearLayout; Loading @@ -49,9 +48,6 @@ public class TransformTestActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LayoutInflater li = (LayoutInflater)getSystemService( LAYOUT_INFLATER_SERVICE); this.setTitle(R.string.act_title); LinearLayout root = new LinearLayout(this); root.setOrientation(LinearLayout.VERTICAL); Loading @@ -71,15 +67,19 @@ public class TransformTestActivity extends Activity { private float mPosY; private float mScale = 1.f; private Matrix mMatrix; private TransformGestureDetector mDetector; private ScaleGestureDetector mDetector; private float mLastX; private float mLastY; private class Listener implements TransformGestureDetector.OnTransformGestureListener { private class Listener implements ScaleGestureDetector.OnScaleGestureListener { public boolean onTransform(TransformGestureDetector detector) { Log.d("ttest", "Translation: (" + detector.getTranslateX() + ", " + detector.getTranslateY() + ")"); public boolean onScale(ScaleGestureDetector detector) { float scale = detector.getScaleFactor(); Log.d("ttest", "Scale: " + scale); // Limit the scale so our object doesn't get too big or disappear if (mScale * scale > 0.1f) { if (mScale * scale < 10.f) { mScale *= scale; Loading @@ -90,15 +90,12 @@ public class TransformTestActivity extends Activity { mScale = 0.1f; } mPosX += detector.getTranslateX(); mPosY += detector.getTranslateY(); Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")"); float sizeX = mDrawable.getIntrinsicWidth()/2; float sizeY = mDrawable.getIntrinsicHeight()/2; float centerX = detector.getCenterX(); float centerY = detector.getCenterY(); float centerX = detector.getFocusX(); float centerY = detector.getFocusY(); float diffX = centerX - mPosX; float diffY = centerY - mPosY; diffX = diffX*scale - diffX; Loading @@ -115,24 +112,20 @@ public class TransformTestActivity extends Activity { return true; } public boolean onTransformBegin(TransformGestureDetector detector) { public boolean onScaleBegin(ScaleGestureDetector detector) { return true; } public boolean onTransformEnd(TransformGestureDetector detector) { return true; public void onScaleEnd(ScaleGestureDetector detector) { mLastX = detector.getFocusX(); mLastY = detector.getFocusY(); } public boolean onTransformFling(TransformGestureDetector detector) { return false; } } public TransformView(Context context) { super(context); mMatrix = new Matrix(); mDetector = new TransformGestureDetector(context, new Listener()); mDetector = new ScaleGestureDetector(context, new Listener()); DisplayMetrics metrics = context.getResources().getDisplayMetrics(); mPosX = metrics.widthPixels/2; mPosY = metrics.heightPixels/2; Loading @@ -151,12 +144,37 @@ public class TransformTestActivity extends Activity { @Override public boolean onTouchEvent(MotionEvent event) { boolean handled = mDetector.onTouchEvent(event); mDetector.onTouchEvent(event); // Handling single finger pan if (!mDetector.isInProgress()) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mLastX = event.getX(); mLastY = event.getY(); break; case MotionEvent.ACTION_MOVE: final float x = event.getX(); final float y = event.getY(); mPosX += x - mLastX; mPosY += y - mLastY; mLastX = x; mLastY = y; float sizeX = mDrawable.getIntrinsicWidth()/2; float sizeY = mDrawable.getIntrinsicHeight()/2; int pointerCount = event.getPointerCount(); Log.d("ttest", "pointerCount: " + pointerCount); mMatrix.reset(); mMatrix.postTranslate(-sizeX, -sizeY); mMatrix.postScale(mScale, mScale); mMatrix.postTranslate(mPosX, mPosY); invalidate(); break; } } return handled; return true; } @Override Loading