Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7ae6faa authored by The Android Automerger's avatar The Android Automerger
Browse files

Merge branch 'eclair' into eclair-release

parents 8f7e1a69 0409b1ba
Loading
Loading
Loading
Loading
+360 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
@@ -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;
@@ -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;
            }
        }
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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();
@@ -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();
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -411,6 +411,7 @@ public class WebSettings {
     */
    public void setSupportZoom(boolean support) {
        mSupportZoom = support;
        mWebView.updateMultiTouchSupport(mContext);
    }

    /**
@@ -425,6 +426,7 @@ public class WebSettings {
     */
    public void setBuiltInZoomControls(boolean enabled) {
        mBuiltInZoomControls = enabled;
        mWebView.updateMultiTouchSupport(mContext);
    }
    
    /**
+56 −56

File changed.

Preview size limit exceeded, changes collapsed.

+2 −4
Original line number Diff line number Diff line
@@ -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">
+47 −29
Original line number Diff line number Diff line
@@ -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;

@@ -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);
@@ -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;
@@ -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;
@@ -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;
@@ -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