PreviewGestures.java revision 471c997edfddccfa0762fa720c82cc95777584e8
1/* 2 * Copyright (C) 2012 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 17package com.android.camera; 18 19import android.os.Handler; 20import android.os.Message; 21import android.util.Log; 22import android.view.MotionEvent; 23import android.view.ScaleGestureDetector; 24import android.view.View; 25import android.view.ViewConfiguration; 26 27import com.android.camera.ui.PieRenderer; 28import com.android.camera.ui.RenderOverlay; 29import com.android.camera.ui.ZoomRenderer; 30 31import java.util.ArrayList; 32import java.util.List; 33 34public class PreviewGestures 35 implements ScaleGestureDetector.OnScaleGestureListener { 36 37 private static final String TAG = "CAM_gestures"; 38 39 private static final long TIMEOUT_PIE = 200; 40 private static final int MSG_PIE = 1; 41 private static final int MODE_NONE = 0; 42 private static final int MODE_PIE = 1; 43 private static final int MODE_ZOOM = 2; 44 private static final int MODE_MODULE = 3; 45 private static final int MODE_ALL = 4; 46 47 private CameraActivity mActivity; 48 private CameraModule mModule; 49 private RenderOverlay mOverlay; 50 private PieRenderer mPie; 51 private ZoomRenderer mZoom; 52 private MotionEvent mDown; 53 private ScaleGestureDetector mScale; 54 private List<View> mReceivers; 55 private int mMode; 56 private int mSlop; 57 private int mTapTimeout; 58 private boolean mEnabled; 59 private boolean mZoomOnly; 60 private int mOrientation; 61 private Handler mHandler = new Handler() { 62 public void handleMessage(Message msg) { 63 if (msg.what == MSG_PIE) { 64 mMode = MODE_PIE; 65 openPie(); 66 cancelActivityTouchHandling(mDown); 67 } 68 } 69 }; 70 71 public PreviewGestures(CameraActivity ctx, CameraModule module, 72 ZoomRenderer zoom, PieRenderer pie) { 73 mActivity = ctx; 74 mModule = module; 75 mPie = pie; 76 mZoom = zoom; 77 mMode = MODE_ALL; 78 mScale = new ScaleGestureDetector(ctx, this); 79 mSlop = (int) ctx.getResources().getDimension(R.dimen.pie_touch_slop); 80 mTapTimeout = ViewConfiguration.getTapTimeout(); 81 mEnabled = true; 82 } 83 84 public void setRenderOverlay(RenderOverlay overlay) { 85 mOverlay = overlay; 86 } 87 88 public void setOrientation(int orientation) { 89 mOrientation = orientation; 90 } 91 92 public void setEnabled(boolean enabled) { 93 mEnabled = enabled; 94 if (!enabled) { 95 cancelPie(); 96 } 97 } 98 99 public void setZoomOnly(boolean zoom) { 100 mZoomOnly = zoom; 101 } 102 103 public void addTouchReceiver(View v) { 104 if (mReceivers == null) { 105 mReceivers = new ArrayList<View>(); 106 } 107 mReceivers.add(v); 108 } 109 110 public void clearTouchReceivers() { 111 if (mReceivers != null) { 112 mReceivers.clear(); 113 } 114 } 115 116 public boolean dispatchTouch(MotionEvent m) { 117 if (!mEnabled) { 118 return mActivity.superDispatchTouchEvent(m); 119 } 120 if (MotionEvent.ACTION_DOWN == m.getActionMasked()) { 121 if (checkReceivers(m)) { 122 mMode = MODE_MODULE; 123 return mActivity.superDispatchTouchEvent(m); 124 } else { 125 mMode = MODE_ALL; 126 mDown = MotionEvent.obtain(m); 127 if (mPie != null && !mZoomOnly) { 128 mHandler.sendEmptyMessageDelayed(MSG_PIE, TIMEOUT_PIE); 129 } 130 if (mZoom != null) { 131 mScale.onTouchEvent(m); 132 } 133 // make sure this is ok 134 return mActivity.superDispatchTouchEvent(m); 135 } 136 } else if (mMode == MODE_NONE) { 137 return false; 138 } else if (mMode == MODE_PIE) { 139 return sendToPie(m); 140 } else if (mMode == MODE_ZOOM) { 141 return mScale.onTouchEvent(m); 142 } else if (mMode == MODE_MODULE) { 143 return mActivity.superDispatchTouchEvent(m); 144 } else { 145 if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) { 146 if (!mZoomOnly) { 147 cancelPie(); 148 } 149 } 150 // not zoom or pie mode and no timeout yet 151 if (mZoom != null) { 152 boolean res = mScale.onTouchEvent(m); 153 if (mScale.isInProgress()) { 154 cancelPie(); 155 cancelActivityTouchHandling(m); 156 return res; 157 } 158 } 159 if (MotionEvent.ACTION_UP == m.getActionMasked()) { 160 cancelPie(); 161 cancelActivityTouchHandling(m); 162 // must have been tap 163 if (m.getEventTime() - mDown.getEventTime() < mTapTimeout) { 164 mModule.onSingleTapUp(null, 165 (int) mDown.getX() - mOverlay.getWindowPositionX(), 166 (int) mDown.getY() - mOverlay.getWindowPositionY()); 167 return true; 168 } else { 169 return mActivity.superDispatchTouchEvent(m); 170 } 171 } else if (MotionEvent.ACTION_MOVE == m.getActionMasked()) { 172 if ((Math.abs(m.getX() - mDown.getX()) > mSlop) 173 || Math.abs(m.getY() - mDown.getY()) > mSlop) { 174 // moved too far and no timeout yet, no focus or pie 175 cancelPie(); 176 if (isSwipe(m)) { 177 mMode = MODE_MODULE; 178 return mActivity.superDispatchTouchEvent(m); 179 } else { 180 mMode = MODE_NONE; 181 cancelActivityTouchHandling(m); 182 } 183 } 184 } 185 return false; 186 } 187 } 188 189 private boolean checkReceivers(MotionEvent m) { 190 if (mReceivers != null) { 191 for (View receiver : mReceivers) { 192 if (isInside(m, receiver)) { 193 return true; 194 } 195 } 196 } 197 return false; 198 } 199 200 private boolean isSwipe(MotionEvent m) { 201 float dx = 0; 202 switch (mOrientation) { 203 case 0: 204 dx = m.getX() - mDown.getX(); 205 return (dx < 0 && Math.abs(m.getY() - mDown.getY()) / -dx < 0.6f); 206 case 90: 207 dx = - (m.getY() - mDown.getY()); 208 return (dx < 0 && Math.abs(m.getX() - mDown.getX()) / -dx < 0.6f); 209 case 180: 210 dx = -(m.getX() - mDown.getX()); 211 return (dx < 0 && Math.abs(m.getY() - mDown.getY()) / -dx < 0.6f); 212 case 270: 213 dx = m.getY() - mDown.getY(); 214 return (dx < 0 && Math.abs(m.getX() - mDown.getX()) / -dx < 0.6f); 215 } 216 return false; 217 } 218 219 private boolean isInside(MotionEvent evt, View v) { 220 return (v.getVisibility() == View.VISIBLE 221 && evt.getX() >= v.getLeft() && evt.getX() < v.getRight() 222 && evt.getY() >= v.getTop() && evt.getY() < v.getBottom()); 223 } 224 225 public void cancelActivityTouchHandling(MotionEvent m) { 226 MotionEvent c = MotionEvent.obtain(m); 227 c.setAction(MotionEvent.ACTION_CANCEL); 228 mActivity.superDispatchTouchEvent(c); 229 } 230 231 private void openPie() { 232 mDown.offsetLocation(-mOverlay.getWindowPositionX(), 233 -mOverlay.getWindowPositionY()); 234 mOverlay.directDispatchTouch(mDown, mPie); 235 } 236 237 private void cancelPie() { 238 mHandler.removeMessages(MSG_PIE); 239 } 240 241 private boolean sendToPie(MotionEvent m) { 242 m.offsetLocation(-mOverlay.getWindowPositionX(), 243 -mOverlay.getWindowPositionY()); 244 return mOverlay.directDispatchTouch(m, mPie); 245 } 246 247 @Override 248 public boolean onScale(ScaleGestureDetector detector) { 249 return mZoom.onScale(detector); 250 } 251 252 @Override 253 public boolean onScaleBegin(ScaleGestureDetector detector) { 254 mMode = MODE_ZOOM; 255 return mZoom.onScaleBegin(detector); 256 } 257 258 @Override 259 public void onScaleEnd(ScaleGestureDetector detector) { 260 mZoom.onScaleEnd(detector); 261 } 262}