PreviewGestures.java revision 571a6f842d972e4ac5eccfd16ed639b6779ebf98
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 Handler mHandler = new Handler() { 61 public void handleMessage(Message msg) { 62 if (msg.what == MSG_PIE) { 63 mMode = MODE_PIE; 64 openPie(); 65 cancelActivityTouchHandling(mDown); 66 } 67 } 68 }; 69 70 public PreviewGestures(CameraActivity ctx, CameraModule module, 71 RenderOverlay overlay, ZoomRenderer zoom, 72 PieRenderer pie) { 73 mActivity = ctx; 74 mModule = module; 75 mOverlay = overlay; 76 mPie = pie; 77 mZoom = zoom; 78 mMode = MODE_ALL; 79 mScale = new ScaleGestureDetector(ctx, this); 80 mSlop = (int) ctx.getResources().getDimension(R.dimen.pie_touch_slop); 81 mTapTimeout = ViewConfiguration.getTapTimeout(); 82 mEnabled = true; 83 } 84 85 public void setEnabled(boolean enabled) { 86 mEnabled = enabled; 87 if (!enabled) { 88 cancelPie(); 89 } 90 } 91 92 public void setZoomOnly(boolean zoom) { 93 mZoomOnly = true; 94 } 95 96 public void addTouchReceiver(View v) { 97 if (mReceivers == null) { 98 mReceivers = new ArrayList<View>(); 99 } 100 mReceivers.add(v); 101 } 102 103 public boolean dispatchTouch(MotionEvent m) { 104 if (!mEnabled) { 105 return mActivity.superDispatchTouchEvent(m); 106 } 107 if (MotionEvent.ACTION_DOWN == m.getActionMasked()) { 108 if (checkReceivers(m)) { 109 mMode = MODE_MODULE; 110 return mActivity.superDispatchTouchEvent(m); 111 } else { 112 mMode = MODE_ALL; 113 mDown = MotionEvent.obtain(m); 114 if (mPie != null && !mZoomOnly) { 115 mHandler.sendEmptyMessageDelayed(MSG_PIE, TIMEOUT_PIE); 116 } 117 if (mZoom != null) { 118 mScale.onTouchEvent(m); 119 } 120 // make sure this is ok 121 return mActivity.superDispatchTouchEvent(m); 122 } 123 } else if (mMode == MODE_NONE) { 124 return false; 125 } else if (mMode == MODE_PIE) { 126 return sendToPie(m); 127 } else if (mMode == MODE_ZOOM) { 128 return mScale.onTouchEvent(m); 129 } else if (mMode == MODE_MODULE) { 130 return mActivity.superDispatchTouchEvent(m); 131 } else { 132 if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) { 133 if (!mZoomOnly) { 134 cancelPie(); 135 } 136 } 137 // not zoom or pie mode and no timeout yet 138 if (mZoom != null) { 139 boolean res = mScale.onTouchEvent(m); 140 if (mScale.isInProgress()) { 141 cancelPie(); 142 cancelActivityTouchHandling(m); 143 return res; 144 } 145 } 146 if (MotionEvent.ACTION_UP == m.getActionMasked()) { 147 cancelPie(); 148 cancelActivityTouchHandling(m); 149 // must have been tap 150 if (m.getEventTime() - mDown.getEventTime() < mTapTimeout) { 151 mModule.onSingleTapUp(null, (int) mDown.getX(), (int) mDown.getY()); 152 return true; 153 } else { 154 return mActivity.superDispatchTouchEvent(m); 155 } 156 } else if (MotionEvent.ACTION_MOVE == m.getActionMasked()) { 157 if ((Math.abs(m.getX() - mDown.getX()) > mSlop) 158 || Math.abs(m.getY() - mDown.getY()) > mSlop) { 159 // moved too far and no timeout yet, no focus or pie 160 cancelPie(); 161 float dx = m.getX() - mDown.getX(); 162 if (dx < 0 163 && Math.abs(m.getY() - mDown.getY()) / -dx < 0.6f) { 164 // within about 37 degrees of x-axis 165 mMode = MODE_MODULE; 166 return mActivity.superDispatchTouchEvent(m); 167 } else { 168 mMode = MODE_NONE; 169 cancelActivityTouchHandling(m); 170 } 171 } 172 } 173 return false; 174 } 175 } 176 177 private boolean checkReceivers(MotionEvent m) { 178 if (mReceivers != null) { 179 for (View receiver : mReceivers) { 180 if (isInside(m, receiver)) { 181 return true; 182 } 183 } 184 } 185 return false; 186 } 187 188 private boolean isInside(MotionEvent evt, View v) { 189 return (v.getVisibility() == View.VISIBLE 190 && evt.getX() >= v.getLeft() && evt.getX() < v.getRight() 191 && evt.getY() >= v.getTop() && evt.getY() < v.getBottom()); 192 } 193 194 public void cancelActivityTouchHandling(MotionEvent m) { 195 MotionEvent c = MotionEvent.obtain(m); 196 c.setAction(MotionEvent.ACTION_CANCEL); 197 mActivity.superDispatchTouchEvent(c); 198 } 199 200 private void openPie() { 201 mDown.offsetLocation(-mOverlay.getWindowPositionX(), 202 -mOverlay.getWindowPositionY()); 203 mOverlay.directDispatchTouch(mDown, mPie); 204 } 205 206 private void cancelPie() { 207 mHandler.removeMessages(MSG_PIE); 208 } 209 210 private boolean sendToPie(MotionEvent m) { 211 m.offsetLocation(-mOverlay.getWindowPositionX(), 212 -mOverlay.getWindowPositionY()); 213 return mOverlay.directDispatchTouch(m, mPie); 214 } 215 216 @Override 217 public boolean onScale(ScaleGestureDetector detector) { 218 return mZoom.onScale(detector); 219 } 220 221 @Override 222 public boolean onScaleBegin(ScaleGestureDetector detector) { 223 mMode = MODE_ZOOM; 224 return mZoom.onScaleBegin(detector); 225 } 226 227 @Override 228 public void onScaleEnd(ScaleGestureDetector detector) { 229 mZoom.onScaleEnd(detector); 230 } 231}