1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.example.android.touchexample; 17 18 import android.content.Context; 19 import android.os.Build; 20 import android.util.Log; 21 import android.view.MotionEvent; 22 import android.view.ScaleGestureDetector; 23 24 public abstract class VersionedGestureDetector { 25 private static final String TAG = "VersionedGestureDetector"; 26 27 OnGestureListener mListener; 28 newInstance(Context context, OnGestureListener listener)29 public static VersionedGestureDetector newInstance(Context context, 30 OnGestureListener listener) { 31 final int sdkVersion = Integer.parseInt(Build.VERSION.SDK); 32 VersionedGestureDetector detector = null; 33 if (sdkVersion < Build.VERSION_CODES.ECLAIR) { 34 detector = new CupcakeDetector(); 35 } else if (sdkVersion < Build.VERSION_CODES.FROYO) { 36 detector = new EclairDetector(); 37 } else { 38 detector = new FroyoDetector(context); 39 } 40 41 Log.d(TAG, "Created new " + detector.getClass()); 42 detector.mListener = listener; 43 44 return detector; 45 } 46 onTouchEvent(MotionEvent ev)47 public abstract boolean onTouchEvent(MotionEvent ev); 48 49 public interface OnGestureListener { onDrag(float dx, float dy)50 public void onDrag(float dx, float dy); onScale(float scaleFactor)51 public void onScale(float scaleFactor); 52 } 53 54 private static class CupcakeDetector extends VersionedGestureDetector { 55 float mLastTouchX; 56 float mLastTouchY; 57 getActiveX(MotionEvent ev)58 float getActiveX(MotionEvent ev) { 59 return ev.getX(); 60 } 61 getActiveY(MotionEvent ev)62 float getActiveY(MotionEvent ev) { 63 return ev.getY(); 64 } 65 shouldDrag()66 boolean shouldDrag() { 67 return true; 68 } 69 70 @Override onTouchEvent(MotionEvent ev)71 public boolean onTouchEvent(MotionEvent ev) { 72 switch (ev.getAction()) { 73 case MotionEvent.ACTION_DOWN: { 74 mLastTouchX = getActiveX(ev); 75 mLastTouchY = getActiveY(ev); 76 break; 77 } 78 case MotionEvent.ACTION_MOVE: { 79 final float x = getActiveX(ev); 80 final float y = getActiveY(ev); 81 82 if (shouldDrag()) { 83 mListener.onDrag(x - mLastTouchX, y - mLastTouchY); 84 } 85 86 mLastTouchX = x; 87 mLastTouchY = y; 88 break; 89 } 90 } 91 return true; 92 } 93 } 94 95 private static class EclairDetector extends CupcakeDetector { 96 private static final int INVALID_POINTER_ID = -1; 97 private int mActivePointerId = INVALID_POINTER_ID; 98 private int mActivePointerIndex = 0; 99 100 @Override getActiveX(MotionEvent ev)101 float getActiveX(MotionEvent ev) { 102 return ev.getX(mActivePointerIndex); 103 } 104 105 @Override getActiveY(MotionEvent ev)106 float getActiveY(MotionEvent ev) { 107 return ev.getY(mActivePointerIndex); 108 } 109 110 @Override onTouchEvent(MotionEvent ev)111 public boolean onTouchEvent(MotionEvent ev) { 112 final int action = ev.getAction(); 113 switch (action & MotionEvent.ACTION_MASK) { 114 case MotionEvent.ACTION_DOWN: 115 mActivePointerId = ev.getPointerId(0); 116 break; 117 case MotionEvent.ACTION_CANCEL: 118 case MotionEvent.ACTION_UP: 119 mActivePointerId = INVALID_POINTER_ID; 120 break; 121 case MotionEvent.ACTION_POINTER_UP: 122 final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) 123 >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; 124 final int pointerId = ev.getPointerId(pointerIndex); 125 if (pointerId == mActivePointerId) { 126 // This was our active pointer going up. Choose a new 127 // active pointer and adjust accordingly. 128 final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 129 mActivePointerId = ev.getPointerId(newPointerIndex); 130 mLastTouchX = ev.getX(newPointerIndex); 131 mLastTouchY = ev.getY(newPointerIndex); 132 } 133 break; 134 } 135 136 mActivePointerIndex = ev.findPointerIndex(mActivePointerId); 137 return super.onTouchEvent(ev); 138 } 139 } 140 141 private static class FroyoDetector extends EclairDetector { 142 private ScaleGestureDetector mDetector; 143 FroyoDetector(Context context)144 public FroyoDetector(Context context) { 145 mDetector = new ScaleGestureDetector(context, 146 new ScaleGestureDetector.SimpleOnScaleGestureListener() { 147 @Override public boolean onScale(ScaleGestureDetector detector) { 148 mListener.onScale(detector.getScaleFactor()); 149 return true; 150 } 151 }); 152 } 153 154 @Override shouldDrag()155 boolean shouldDrag() { 156 return !mDetector.isInProgress(); 157 } 158 159 @Override onTouchEvent(MotionEvent ev)160 public boolean onTouchEvent(MotionEvent ev) { 161 mDetector.onTouchEvent(ev); 162 return super.onTouchEvent(ev); 163 } 164 } 165 } 166