/utils/src/com/google/marvin/widget/GestureOverlay.java
Java | 220 lines | 146 code | 46 blank | 28 comment | 41 complexity | 69eb6876a07a2e8ef34a52ec92b52dcc MD5 | raw file
1/* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.google.marvin.widget; 18 19import android.content.Context; 20import android.view.MotionEvent; 21import android.view.View; 22 23import com.googlecode.eyesfree.utils.compat.MotionEventCompatUtils; 24 25/** 26 * A transparent overlay which catches all touch events and uses a call back to 27 * return the gesture that the user performed. Includes edge events. 28 * 29 * @author clchen@google.com (Charles L. Chen), credo@google.com (Tim Credo) 30 */ 31 32public class GestureOverlay extends View { 33 public static class Gesture { 34 public static final int UPLEFT = 1; 35 36 public static final int UP = 2; 37 38 public static final int UPRIGHT = 3; 39 40 public static final int LEFT = 4; 41 42 public static final int CENTER = 5; 43 44 public static final int RIGHT = 6; 45 46 public static final int DOWNLEFT = 7; 47 48 public static final int DOWN = 8; 49 50 public static final int DOWNRIGHT = 9; 51 52 public static final int EDGELEFT = 10; 53 54 public static final int EDGERIGHT = 11; 55 } 56 57 /** 58 * The callback interface to be used when a gesture is detected. 59 */ 60 public interface GestureListener { 61 public void onGestureStart(int g); 62 63 public void onGestureChange(int g); 64 65 public void onGestureFinish(int g); 66 } 67 68 private final double left = 0; 69 70 private final double upleft = Math.PI * .25; 71 72 private final double up = Math.PI * .5; 73 74 private final double upright = Math.PI * .75; 75 76 private final double downright = -Math.PI * .75; 77 78 private final double down = -Math.PI * .5; 79 80 private final double downleft = -Math.PI * .25; 81 82 private final double right = Math.PI; 83 84 private final double rightWrap = -Math.PI; 85 86 private GestureListener cb = null; 87 88 private double downX; 89 90 private double downY; 91 92 private int currentGesture; 93 94 private int radiusThreshold = 30; 95 96 private boolean edgeGesture = false; 97 98 int leftEdge, rightEdge; 99 100 public GestureOverlay(Context context, GestureListener callback) { 101 super(context); 102 cb = callback; 103 } 104 105 public GestureOverlay(Context context) { 106 super(context); 107 } 108 109 public void setGestureListener(GestureListener callback) { 110 cb = callback; 111 } 112 113 public void setMinimumRadius(int minRadius) { 114 radiusThreshold = minRadius; 115 } 116 117 // @Override (only in API 13+) 118 public boolean onHoverEvent(MotionEvent event) { 119 return onTouchEvent(event); 120 } 121 122 @Override 123 public boolean onTouchEvent(MotionEvent event) { 124 int action = event.getAction(); 125 float x = event.getX(); 126 float y = event.getY(); 127 int prevGesture = -1; 128 switch (action) { 129 case MotionEventCompatUtils.ACTION_HOVER_ENTER: 130 case MotionEvent.ACTION_DOWN: 131 downX = x; 132 downY = y; 133 leftEdge = getWidth() / 7; 134 rightEdge = getWidth() - getWidth() / 7; 135 if (x < leftEdge) { 136 currentGesture = Gesture.EDGELEFT; 137 edgeGesture = true; 138 } else if (x > rightEdge) { 139 currentGesture = Gesture.EDGERIGHT; 140 edgeGesture = true; 141 } else { 142 currentGesture = Gesture.CENTER; 143 edgeGesture = false; 144 } 145 if (cb != null) 146 cb.onGestureStart(currentGesture); 147 break; 148 case MotionEventCompatUtils.ACTION_HOVER_EXIT: 149 case MotionEvent.ACTION_UP: 150 prevGesture = currentGesture; 151 currentGesture = evalMotion(x, y); 152 // Do some correction if the user lifts up on deadspace 153 if (currentGesture == -1) { 154 currentGesture = prevGesture; 155 } 156 if (cb != null) { 157 cb.onGestureFinish(currentGesture); 158 } 159 break; 160 default: 161 prevGesture = currentGesture; 162 currentGesture = evalMotion(x, y); 163 // Do some correction if the user lifts up on deadspace 164 if (currentGesture == -1) { 165 currentGesture = prevGesture; 166 break; 167 } 168 if (prevGesture != currentGesture) { 169 if (cb != null) { 170 cb.onGestureChange(currentGesture); 171 } 172 } 173 break; 174 } 175 return true; 176 } 177 178 public int evalMotion(double x, double y) { 179 float rTolerance = radiusThreshold; 180 double thetaTolerance = (Math.PI / 12); 181 182 if (edgeGesture) { 183 if (x < leftEdge && downX < leftEdge) { 184 return Gesture.EDGELEFT; 185 } else if (x > rightEdge && downX > rightEdge) { 186 return Gesture.EDGERIGHT; 187 } else { 188 edgeGesture = false; 189 } 190 } 191 192 double r = Math.sqrt(((downX - x) * (downX - x)) + ((downY - y) * (downY - y))); 193 194 if (r < rTolerance) { 195 return Gesture.CENTER; 196 } 197 198 double theta = Math.atan2(downY - y, downX - x); 199 if (Math.abs(theta - left) < thetaTolerance) { 200 return Gesture.LEFT; 201 } else if (Math.abs(theta - upleft) < thetaTolerance) { 202 return Gesture.UPLEFT; 203 } else if (Math.abs(theta - up) < thetaTolerance) { 204 return Gesture.UP; 205 } else if (Math.abs(theta - upright) < thetaTolerance) { 206 return Gesture.UPRIGHT; 207 } else if (Math.abs(theta - downright) < thetaTolerance) { 208 return Gesture.DOWNRIGHT; 209 } else if (Math.abs(theta - down) < thetaTolerance) { 210 return Gesture.DOWN; 211 } else if (Math.abs(theta - downleft) < thetaTolerance) { 212 return Gesture.DOWNLEFT; 213 } else if ((theta > right - thetaTolerance) || (theta < rightWrap + thetaTolerance)) { 214 return Gesture.RIGHT; 215 } 216 // Off by more than the threshold, so it doesn't count 217 return -1; 218 } 219 220}