| package com.doumee.lib_coremodel.view.zoomview; | 
|   | 
| /******************************************************************************* | 
|  * Copyright 2011, 2012 Chris Banes. | 
|  * | 
|  * 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. | 
|  *******************************************************************************/ | 
|   | 
| import android.annotation.TargetApi; | 
| import android.content.Context; | 
| import android.os.Build; | 
| import android.view.MotionEvent; | 
| import android.view.ScaleGestureDetector; | 
| import android.view.ScaleGestureDetector.OnScaleGestureListener; | 
| import android.view.VelocityTracker; | 
| import android.view.ViewConfiguration; | 
|   | 
| public abstract class VersionedGestureDetector { | 
|     static final String LOG_TAG = "VersionedGestureDetector"; | 
|     OnGestureListener mListener; | 
|   | 
|     public static VersionedGestureDetector newInstance(Context context, OnGestureListener listener) { | 
|         final int sdkVersion = Build.VERSION.SDK_INT; | 
|         VersionedGestureDetector detector = null; | 
|   | 
|         if (sdkVersion < Build.VERSION_CODES.ECLAIR) { | 
|             detector = new CupcakeDetector(context); | 
|         } else if (sdkVersion < Build.VERSION_CODES.FROYO) { | 
|             detector = new EclairDetector(context); | 
|         } else { | 
|             detector = new FroyoDetector(context); | 
|         } | 
|   | 
|         detector.mListener = listener; | 
|   | 
|         return detector; | 
|     } | 
|   | 
|     public abstract boolean onTouchEvent(MotionEvent ev); | 
|   | 
|     public abstract boolean isScaling(); | 
|   | 
|     public static interface OnGestureListener { | 
|         public void onDrag(float dx, float dy); | 
|   | 
|         public void onFling(float startX, float startY, float velocityX, float velocityY); | 
|   | 
|         public void onScale(float scaleFactor, float focusX, float focusY); | 
|     } | 
|   | 
|     private static class CupcakeDetector extends VersionedGestureDetector { | 
|   | 
|         float mLastTouchX; | 
|         float mLastTouchY; | 
|         final float mTouchSlop; | 
|         final float mMinimumVelocity; | 
|   | 
|         public CupcakeDetector(Context context) { | 
|             final ViewConfiguration configuration = ViewConfiguration.get(context); | 
|             mMinimumVelocity = configuration.getScaledMinimumFlingVelocity(); | 
|             mTouchSlop = configuration.getScaledTouchSlop(); | 
|         } | 
|   | 
|         private VelocityTracker mVelocityTracker; | 
|         private boolean mIsDragging; | 
|   | 
|         float getActiveX(MotionEvent ev) { | 
|             return ev.getX(); | 
|         } | 
|   | 
|         float getActiveY(MotionEvent ev) { | 
|             return ev.getY(); | 
|         } | 
|   | 
|         public boolean isScaling() { | 
|             return false; | 
|         } | 
|   | 
|         @Override | 
|         public boolean onTouchEvent(MotionEvent ev) { | 
|             switch (ev.getAction()) { | 
|                 case MotionEvent.ACTION_DOWN: { | 
|                     mVelocityTracker = VelocityTracker.obtain(); | 
|                     mVelocityTracker.addMovement(ev); | 
|   | 
|                     mLastTouchX = getActiveX(ev); | 
|                     mLastTouchY = getActiveY(ev); | 
|                     mIsDragging = false; | 
|                     break; | 
|                 } | 
|   | 
|                 case MotionEvent.ACTION_MOVE: { | 
|                     final float x = getActiveX(ev); | 
|                     final float y = getActiveY(ev); | 
|                     final float dx = x - mLastTouchX, dy = y - mLastTouchY; | 
|   | 
|                     if (!mIsDragging) { | 
|                         // Use Pythagoras to see if drag length is larger than | 
|                         // touch slop | 
| //                        mIsDragging = FloatMath.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; | 
|                         mIsDragging = Math.sqrt((dx * dx) + (dy * dy)) >= mTouchSlop; | 
|                     } | 
|   | 
|                     if (mIsDragging) { | 
|                         mListener.onDrag(dx, dy); | 
|                         mLastTouchX = x; | 
|                         mLastTouchY = y; | 
|   | 
|                         if (null != mVelocityTracker) { | 
|                             mVelocityTracker.addMovement(ev); | 
|                         } | 
|                     } | 
|                     break; | 
|                 } | 
|   | 
|                 case MotionEvent.ACTION_CANCEL: { | 
|                     // Recycle Velocity Tracker | 
|                     if (null != mVelocityTracker) { | 
|                         mVelocityTracker.recycle(); | 
|                         mVelocityTracker = null; | 
|                     } | 
|                     break; | 
|                 } | 
|   | 
|                 case MotionEvent.ACTION_UP: { | 
|                     if (mIsDragging) { | 
|                         if (null != mVelocityTracker) { | 
|                             mLastTouchX = getActiveX(ev); | 
|                             mLastTouchY = getActiveY(ev); | 
|   | 
|                             // Compute velocity within the last 1000ms | 
|                             mVelocityTracker.addMovement(ev); | 
|                             mVelocityTracker.computeCurrentVelocity(1000); | 
|   | 
|                             final float vX = mVelocityTracker.getXVelocity(), vY = mVelocityTracker.getYVelocity(); | 
|   | 
|                             // If the velocity is greater than minVelocity, call | 
|                             // listener | 
|                             if (Math.max(Math.abs(vX), Math.abs(vY)) >= mMinimumVelocity) { | 
|                                 mListener.onFling(mLastTouchX, mLastTouchY, -vX, -vY); | 
|                             } | 
|                         } | 
|                     } | 
|   | 
|                     // Recycle Velocity Tracker | 
|                     if (null != mVelocityTracker) { | 
|                         mVelocityTracker.recycle(); | 
|                         mVelocityTracker = null; | 
|                     } | 
|                     break; | 
|                 } | 
|             } | 
|   | 
|             return true; | 
|         } | 
|     } | 
|   | 
|     @TargetApi(5) | 
|     private static class EclairDetector extends CupcakeDetector { | 
|         private static final int INVALID_POINTER_ID = -1; | 
|         private int mActivePointerId = INVALID_POINTER_ID; | 
|         private int mActivePointerIndex = 0; | 
|   | 
|         public EclairDetector(Context context) { | 
|             super(context); | 
|         } | 
|   | 
|         @Override | 
|         float getActiveX(MotionEvent ev) { | 
|             try { | 
|                 return ev.getX(mActivePointerIndex); | 
|             } catch (Exception e) { | 
|                 return ev.getX(); | 
|             } | 
|         } | 
|   | 
|         @Override | 
|         float getActiveY(MotionEvent ev) { | 
|             try { | 
|                 return ev.getY(mActivePointerIndex); | 
|             } catch (Exception e) { | 
|                 return ev.getY(); | 
|             } | 
|         } | 
|   | 
|         @Override | 
|         public boolean onTouchEvent(MotionEvent ev) { | 
|             final int action = ev.getAction(); | 
|             switch (action & MotionEvent.ACTION_MASK) { | 
|                 case MotionEvent.ACTION_DOWN: | 
|                     mActivePointerId = ev.getPointerId(0); | 
|                     break; | 
|                 case MotionEvent.ACTION_CANCEL: | 
|                 case MotionEvent.ACTION_UP: | 
|                     mActivePointerId = INVALID_POINTER_ID; | 
|                     break; | 
|                 case MotionEvent.ACTION_POINTER_UP: | 
|                     final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; | 
|                     final int pointerId = ev.getPointerId(pointerIndex); | 
|                     if (pointerId == mActivePointerId) { | 
|                         // This was our active pointer going up. Choose a new | 
|                         // active pointer and adjust accordingly. | 
|                         final int newPointerIndex = pointerIndex == 0 ? 1 : 0; | 
|                         mActivePointerId = ev.getPointerId(newPointerIndex); | 
|                         mLastTouchX = ev.getX(newPointerIndex); | 
|                         mLastTouchY = ev.getY(newPointerIndex); | 
|                     } | 
|                     break; | 
|             } | 
|   | 
|             mActivePointerIndex = ev.findPointerIndex(mActivePointerId != INVALID_POINTER_ID ? mActivePointerId : 0); | 
|             return super.onTouchEvent(ev); | 
|         } | 
|     } | 
|   | 
|     @TargetApi(8) | 
|     private static class FroyoDetector extends EclairDetector { | 
|   | 
|         private final ScaleGestureDetector mDetector; | 
|   | 
|         // Needs to be an inner class so that we don't hit | 
|         // VerifyError's on API 4. | 
|         private final OnScaleGestureListener mScaleListener = new OnScaleGestureListener() { | 
|   | 
|             @Override | 
|             public boolean onScale(ScaleGestureDetector detector) { | 
|                 mListener.onScale(detector.getScaleFactor(), detector.getFocusX(), detector.getFocusY()); | 
|                 return true; | 
|             } | 
|   | 
|             @Override | 
|             public boolean onScaleBegin(ScaleGestureDetector detector) { | 
|                 return true; | 
|             } | 
|   | 
|             @Override | 
|             public void onScaleEnd(ScaleGestureDetector detector) { | 
|                 // NO-OP | 
|             } | 
|         }; | 
|   | 
|         public FroyoDetector(Context context) { | 
|             super(context); | 
|             mDetector = new ScaleGestureDetector(context, mScaleListener); | 
|         } | 
|   | 
|         @Override | 
|         public boolean isScaling() { | 
|             return mDetector.isInProgress(); | 
|         } | 
|   | 
|         @Override | 
|         public boolean onTouchEvent(MotionEvent ev) { | 
|             mDetector.onTouchEvent(ev); | 
|             return super.onTouchEvent(ev); | 
|         } | 
|   | 
|     } | 
| } |