1 /* 2 * Copyright (C) 2013 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 17 package com.android.camera; 18 19 import android.view.GestureDetector; 20 import android.view.MotionEvent; 21 import android.view.ScaleGestureDetector; 22 import android.view.View; 23 24 import com.android.camera.ui.PieRenderer; 25 import com.android.camera.ui.RenderOverlay; 26 import com.android.camera.ui.ZoomRenderer; 27 28 /* PreviewGestures disambiguates touch events received on RenderOverlay 29 * and dispatch them to the proper recipient (i.e. zoom renderer or pie renderer). 30 * Touch events on CameraControls will be handled by framework. 31 * */ 32 public class PreviewGestures 33 implements ScaleGestureDetector.OnScaleGestureListener { 34 35 private static final String TAG = "CAM_gestures"; 36 37 private static final int MODE_NONE = 0; 38 private static final int MODE_ZOOM = 2; 39 40 public static final int DIR_UP = 0; 41 public static final int DIR_DOWN = 1; 42 public static final int DIR_LEFT = 2; 43 public static final int DIR_RIGHT = 3; 44 45 private SingleTapListener mTapListener; 46 private RenderOverlay mOverlay; 47 private PieRenderer mPie; 48 private ZoomRenderer mZoom; 49 private MotionEvent mDown; 50 private MotionEvent mCurrent; 51 private ScaleGestureDetector mScale; 52 private int mMode; 53 private boolean mZoomEnabled; 54 private boolean mEnabled; 55 private boolean mZoomOnly; 56 private GestureDetector mGestureDetector; 57 58 private GestureDetector.SimpleOnGestureListener mGestureListener = new GestureDetector.SimpleOnGestureListener() { 59 @Override 60 public void onLongPress (MotionEvent e) { 61 // Open pie 62 if (!mZoomOnly && mPie != null && !mPie.showsItems()) { 63 openPie(); 64 } 65 } 66 67 @Override 68 public boolean onSingleTapUp (MotionEvent e) { 69 // Tap to focus when pie is not open 70 if (mPie == null || !mPie.showsItems()) { 71 mTapListener.onSingleTapUp(null, (int) e.getX(), (int) e.getY()); 72 return true; 73 } 74 return false; 75 } 76 77 @Override 78 public boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 79 if (e1 == null) { 80 // e1 can be null if for some cases. 81 return false; 82 } 83 if (mZoomOnly || mMode == MODE_ZOOM) return false; 84 int deltaX = (int) (e1.getX() - e2.getX()); 85 int deltaY = (int) (e1.getY() - e2.getY()); 86 if (deltaY > 2 * deltaX && deltaY > -2 * deltaX) { 87 // Open pie on swipe up 88 if (mPie != null && !mPie.showsItems()) { 89 openPie(); 90 return true; 91 } 92 } 93 return false; 94 } 95 }; 96 97 public interface SingleTapListener { 98 public void onSingleTapUp(View v, int x, int y); 99 } 100 101 public PreviewGestures(CameraActivity ctx, SingleTapListener tapListener, 102 ZoomRenderer zoom, PieRenderer pie) { 103 mTapListener = tapListener; 104 mPie = pie; 105 mZoom = zoom; 106 mMode = MODE_NONE; 107 mScale = new ScaleGestureDetector(ctx, this); 108 mEnabled = true; 109 mGestureDetector = new GestureDetector(mGestureListener); 110 } 111 112 public void setRenderOverlay(RenderOverlay overlay) { 113 mOverlay = overlay; 114 } 115 116 public void setEnabled(boolean enabled) { 117 mEnabled = enabled; 118 } 119 120 public void setZoomEnabled(boolean enable) { 121 mZoomEnabled = enable; 122 } 123 124 public void setZoomOnly(boolean zoom) { 125 mZoomOnly = zoom; 126 } 127 128 public boolean isEnabled() { 129 return mEnabled; 130 } 131 132 public boolean dispatchTouch(MotionEvent m) { 133 if (!mEnabled) { 134 return false; 135 } 136 mCurrent = m; 137 if (MotionEvent.ACTION_DOWN == m.getActionMasked()) { 138 mMode = MODE_NONE; 139 mDown = MotionEvent.obtain(m); 140 } 141 142 // If pie is open, redirects all the touch events to pie. 143 if (mPie != null && mPie.isOpen()) { 144 return sendToPie(m); 145 } 146 147 // If pie is not open, send touch events to gesture detector and scale 148 // listener to recognize the gesture. 149 mGestureDetector.onTouchEvent(m); 150 if (mZoom != null) { 151 mScale.onTouchEvent(m); 152 if (MotionEvent.ACTION_POINTER_DOWN == m.getActionMasked()) { 153 mMode = MODE_ZOOM; 154 if (mZoomEnabled) { 155 // Start showing zoom UI as soon as there is a second finger down 156 mZoom.onScaleBegin(mScale); 157 } 158 } else if (MotionEvent.ACTION_POINTER_UP == m.getActionMasked()) { 159 mZoom.onScaleEnd(mScale); 160 } 161 } 162 return true; 163 } 164 165 private MotionEvent makeCancelEvent(MotionEvent m) { 166 MotionEvent c = MotionEvent.obtain(m); 167 c.setAction(MotionEvent.ACTION_CANCEL); 168 return c; 169 } 170 171 private void openPie() { 172 mGestureDetector.onTouchEvent(makeCancelEvent(mDown)); 173 mScale.onTouchEvent(makeCancelEvent(mDown)); 174 mOverlay.directDispatchTouch(mDown, mPie); 175 } 176 177 private boolean sendToPie(MotionEvent m) { 178 return mOverlay.directDispatchTouch(m, mPie); 179 } 180 181 // OnScaleGestureListener implementation 182 @Override 183 public boolean onScale(ScaleGestureDetector detector) { 184 return mZoom.onScale(detector); 185 } 186 187 @Override 188 public boolean onScaleBegin(ScaleGestureDetector detector) { 189 if (mPie == null || !mPie.isOpen()) { 190 mMode = MODE_ZOOM; 191 mGestureDetector.onTouchEvent(makeCancelEvent(mCurrent)); 192 if (!mZoomEnabled) return false; 193 return mZoom.onScaleBegin(detector); 194 } 195 return false; 196 } 197 198 @Override 199 public void onScaleEnd(ScaleGestureDetector detector) { 200 mZoom.onScaleEnd(detector); 201 } 202 } 203 204