PageRenderTime 38ms CodeModel.GetById 17ms app.highlight 18ms RepoModel.GetById 1ms app.codeStats 0ms

/utils/src/com/google/marvin/widget/GestureOverlay.java

http://eyes-free.googlecode.com/
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}