PageRenderTime 39ms CodeModel.GetById 5ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/CircleIME/src/com/marvin/circleime/CircleGestureView.java

http://eyes-free.googlecode.com/
Java | 594 lines | 496 code | 68 blank | 30 comment | 64 complexity | 6a7baa3a1fd9ff414bb481b4779d1884 MD5 | raw file
  1
  2package com.marvin.circleime;
  3
  4import com.google.tts.TTSEarcon;
  5
  6import android.content.Context;
  7import android.graphics.Canvas;
  8import android.graphics.Color;
  9import android.graphics.Paint;
 10import android.graphics.Typeface;
 11import android.inputmethodservice.KeyboardView;
 12import android.os.Vibrator;
 13import android.util.AttributeSet;
 14import android.util.Log;
 15import android.view.KeyCharacterMap;
 16import android.view.KeyEvent;
 17import android.view.MotionEvent;
 18import android.view.View;
 19
 20/**
 21 * A transparent overlay which catches all touch events and uses a call back to
 22 * return the gesture that the user performed.
 23 * 
 24 * @author clchen@google.com (Charles L. Chen)
 25 */
 26
 27public class CircleGestureView extends KeyboardView {
 28    private static final char alphabet[] = {
 29            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
 30            'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
 31    };
 32
 33    private static final long[] PATTERN = {
 34            0, 1, 40, 41
 35    };
 36
 37    private static final int AE = 0;
 38
 39    private static final int IM = 1;
 40
 41    private static final int QU = 2;
 42
 43    private static final int Y = 4;
 44
 45    private static final int NONE = 5;
 46
 47    private static final int NUM0 = 6;
 48    
 49    private static final int NUM1 = 7;
 50    
 51
 52    private final double left = 0;
 53
 54    private final double upleft = Math.PI * .25;
 55
 56    private final double up = Math.PI * .5;
 57
 58    private final double upright = Math.PI * .75;
 59
 60    private final double downright = -Math.PI * .75;
 61
 62    private final double down = -Math.PI * .5;
 63
 64    private final double downleft = -Math.PI * .25;
 65
 66    private final double right = Math.PI;
 67
 68    private final double rightWrap = -Math.PI;
 69
 70    private double downX;
 71
 72    private double downY;
 73
 74    private int currentWheel = 5;
 75
 76    private String currentCharacter = "";
 77
 78    private String currentString = "";
 79
 80    private double lastX;
 81
 82    private double lastY;
 83
 84    private int currentValue;
 85
 86    private boolean screenIsBeingTouched;
 87
 88    private Vibrator vibe;
 89
 90    private SoftKeyboard parent;
 91
 92    public CircleGestureView(Context context, AttributeSet attrs) {
 93        super(context, attrs);
 94        parent = (SoftKeyboard) context;
 95        vibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
 96    }
 97
 98    @Override
 99    public boolean onTouchEvent(MotionEvent event) {
100        int action = event.getAction();
101        float x = event.getX();
102        float y = event.getY();
103        if (action == MotionEvent.ACTION_DOWN) {
104            initiateMotion(x, y);
105            return true;
106        } else if (action == MotionEvent.ACTION_UP) {
107            confirmEntry();
108            return true;
109        } else {
110            screenIsBeingTouched = true;
111            lastX = x;
112            lastY = y;
113            int prevVal = currentValue;
114            currentValue = evalMotion(x, y);
115            // Do nothing since we want a deadzone here;
116            // restore the state to the previous value.
117            if (currentValue == -1) {
118                currentValue = prevVal;
119                return true;
120            }
121            // There is a wheel that is active
122            if (currentValue != 5) {
123                if (currentWheel == NONE) {
124                    currentWheel = getWheel(currentValue);
125                }
126                currentCharacter = getCharacter(currentWheel, currentValue);
127            } else {
128                currentCharacter = "";
129            }
130            invalidate();
131            if (prevVal != currentValue) {
132                parent.mTts.playEarcon("[tock]", 2, null);
133                if (currentCharacter.equals("")) {
134                    // parent.tts.playEarcon(TTSEarcon.TOCK.toString(), 0,
135                    // null);
136                } else {
137                    String[] params = new String[1];
138                    // params[0] = TTSParams.VOICE_FEMALE.toString();
139                    // parent.tts.speak(currentCharacter, 0, null); //TODO: Fix
140                    // me!
141                }
142            }
143            vibe.vibrate(PATTERN, -1);
144        }
145        return true;
146    }
147
148    public int getWheel(int value) {
149        switch (value) {
150            case 1:
151                return AE;
152            case 2:
153                return IM;
154            case 3:
155                return QU;
156            case 4:
157                return Y;
158            case 5:
159                return NONE;
160            case 6:
161                return Y;
162            case 7:
163                return QU;
164            case 8:
165                return IM;
166            case 9:
167                return AE;
168            default:
169                return NONE;
170        }
171    }
172
173    public String getCharacter(int wheel, int value) {
174        switch (wheel) {
175            case AE:
176                switch (value) {
177                    case 1:
178                        return "A";
179                    case 2:
180                        return "B";
181                    case 3:
182                        return "C";
183                    case 4:
184                        return "H";
185                    case 5:
186                        return "";
187                    case 6:
188                        return "D";
189                    case 7:
190                        return "G";
191                    case 8:
192                        return "F";
193                    case 9:
194                        return "E";
195                    default:
196                        return "";
197                }
198            case IM:
199                switch (value) {
200                    case 1:
201                        return "P";
202                    case 2:
203                        return "I";
204                    case 3:
205                        return "J";
206                    case 4:
207                        return "O";
208                    case 5:
209                        return "";
210                    case 6:
211                        return "K";
212                    case 7:
213                        return "N";
214                    case 8:
215                        return "M";
216                    case 9:
217                        return "L";
218                    default:
219                        return "";
220                }
221            case QU:
222                switch (value) {
223                    case 1:
224                        return "W";
225                    case 2:
226                        return "X";
227                    case 3:
228                        return "Q";
229                    case 4:
230                        return "V";
231                    case 5:
232                        return "";
233                    case 6:
234                        return "R";
235                    case 7:
236                        return "U";
237                    case 8:
238                        return "T";
239                    case 9:
240                        return "S";
241                    default:
242                        return "";
243                }
244            case Y:
245                switch (value) {
246                    case 1:
247                        return ",";
248                    case 2:
249                        return "!";
250                    case 3:
251                        return "SPACE"; // return "MODE";
252                    case 4:
253                        return "<-";
254                    case 5:
255                        return "";
256                    case 6:
257                        return "Y";
258                    case 7:
259                        return ".";
260                    case 8:
261                        return "?";
262                    case 9:
263                        return "Z";
264                    default:
265                        return "";
266                }
267            case NUM0:
268                switch (value) {
269                    case 1:
270                        return "0";
271                    case 2:
272                        return "1";
273                    case 3:
274                        return "2"; // return "MODE";
275                    case 4:
276                        return "3";
277                    case 5:
278                        return "";
279                    case 6:
280                        return "4";
281                    case 7:
282                        return "5";
283                    case 8:
284                        return "6";
285                    case 9:
286                        return "7";
287                    default:
288                        return "";
289                } 
290            case NUM1:
291                switch (value) {
292                    case 1:
293                        return "8";
294                    case 2:
295                        return "9";
296                    case 3:
297                        return "<-";
298                    case 4:
299                        return "*";
300                    case 5:
301                        return "";
302                    case 6:
303                        return "@";
304                    case 7:
305                        return "&";
306                    case 8:
307                        return "#";
308                    case 9:
309                        return "$";
310                    default:
311                        return "%";
312                }                 
313            default:
314                return "";
315        }
316    }
317
318    private void confirmEntry() {
319        screenIsBeingTouched = false;
320        int prevVal = currentValue;
321        currentValue = evalMotion(lastX, lastY);
322        // Do some correction if the user lifts up on deadspace
323        if (currentValue == -1) {
324            currentValue = prevVal;
325        }
326        // The user never got a number that wasn't deadspace,
327        // so assume 5.
328        if (currentValue == -1) {
329            currentValue = 5;
330        }
331        currentCharacter = getCharacter(currentWheel, currentValue);
332        if (currentCharacter.equals("<-")) {
333            currentCharacter = "";
334            backspace();
335        } else {
336            if (currentCharacter.equals("SPACE")) {
337                currentString = currentString + " ";
338            }
339            /*
340             * else if (currentCharacter.equals("MODE")) { // Do nothing }
341             */
342            else {
343                currentString = currentString + currentCharacter;
344            }
345            // parent.tts.speak(currentCharacter, 0, null);
346            if (currentCharacter.equals("SPACE")) {
347                currentCharacter = " ";
348            }
349            if (currentCharacter.length() > 0) {
350                parent.sendKeyChar(currentCharacter.toLowerCase().charAt(0));
351            }
352        }
353        invalidate();
354        initiateMotion(lastX, lastY);
355    }
356
357    private void initiateMotion(double x, double y) {
358        downX = x;
359        downY = y;
360        lastX = x;
361        lastY = y;
362        currentValue = -1;
363        currentWheel = NONE;
364        currentCharacter = "";
365    }
366
367    @Override
368    public void onDraw(Canvas canvas) {
369        // super.onDraw(canvas);
370
371        setBackgroundColor(Color.TRANSPARENT);
372
373        // Draw an indication that the IME is up - doing a border for now
374        Paint imeStatusPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
375        imeStatusPaint.setColor(Color.RED);
376        imeStatusPaint.setTextSize(14);
377        imeStatusPaint.setTypeface(Typeface.DEFAULT_BOLD);
378        Paint imeBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
379        imeBgPaint.setColor(Color.BLACK);
380        int fudgeFactor = 15; 
381        int startY = 0;
382
383        canvas.drawRect(0, startY, getWidth(), startY + fudgeFactor, imeBgPaint);
384        canvas.drawText("IME Active", 10, startY + 13, imeStatusPaint);
385
386        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
387        paint.setColor(Color.WHITE);
388        paint.setTextAlign(Paint.Align.CENTER);
389        paint.setTypeface(Typeface.DEFAULT_BOLD);
390
391        int x = 5;
392        int y = 50;
393        paint.setTextSize(50);
394        paint.setTextAlign(Paint.Align.LEFT);
395        y -= paint.ascent() / 2;
396
397        // canvas.drawText(currentString, x, y, paint);
398
399        if (!screenIsBeingTouched) {
400            x = 5;
401            y = getHeight() - 40;
402            paint.setTextSize(20);
403            paint.setTextAlign(Paint.Align.LEFT);
404            y -= paint.ascent() / 2;
405            // canvas.drawText("Scroll apps with trackball.", x, y, paint);
406
407            x = 5;
408            y = getHeight() - 20;
409            paint.setTextSize(20);
410            paint.setTextAlign(Paint.Align.LEFT);
411            y -= paint.ascent() / 2;
412            // canvas.drawText("Press CALL to launch app.", x, y, paint);
413        } else {
414            int offset = 90;
415
416            int x1 = (int) downX - offset;
417            int y1 = (int) downY - offset;
418            int x2 = (int) downX;
419            int y2 = (int) downY - offset;
420            int x3 = (int) downX + offset;
421            int y3 = (int) downY - offset;
422            int x4 = (int) downX - offset;
423            int y4 = (int) downY;
424            int x6 = (int) downX + offset;
425            int y6 = (int) downY;
426            int x7 = (int) downX - offset;
427            int y7 = (int) downY + offset;
428            int x8 = (int) downX;
429            int y8 = (int) downY + offset;
430            int x9 = (int) downX + offset;
431            int y9 = (int) downY + offset;
432
433            y1 -= paint.ascent() / 2;
434            y2 -= paint.ascent() / 2;
435            y3 -= paint.ascent() / 2;
436            y4 -= paint.ascent() / 2;
437            y6 -= paint.ascent() / 2;
438            y7 -= paint.ascent() / 2;
439            y8 -= paint.ascent() / 2;
440            y9 -= paint.ascent() / 2;
441
442            switch (currentWheel) {
443                case AE:
444                    paint.setColor(Color.RED);
445                    drawCharacter("A", x1, y1, canvas, paint, currentCharacter.equals("A"));
446                    drawCharacter("B", x2, y2, canvas, paint, currentCharacter.equals("B"));
447                    drawCharacter("C", x3, y3, canvas, paint, currentCharacter.equals("C"));
448                    drawCharacter("H", x4, y4, canvas, paint, currentCharacter.equals("H"));
449                    drawCharacter("D", x6, y6, canvas, paint, currentCharacter.equals("D"));
450                    drawCharacter("G", x7, y7, canvas, paint, currentCharacter.equals("G"));
451                    drawCharacter("F", x8, y8, canvas, paint, currentCharacter.equals("F"));
452                    drawCharacter("E", x9, y9, canvas, paint, currentCharacter.equals("E"));
453                    break;
454                case IM:
455                    paint.setColor(Color.BLUE);
456                    drawCharacter("P", x1, y1, canvas, paint, currentCharacter.equals("P"));
457                    drawCharacter("I", x2, y2, canvas, paint, currentCharacter.equals("I"));
458                    drawCharacter("J", x3, y3, canvas, paint, currentCharacter.equals("J"));
459                    drawCharacter("O", x4, y4, canvas, paint, currentCharacter.equals("O"));
460                    drawCharacter("K", x6, y6, canvas, paint, currentCharacter.equals("K"));
461                    drawCharacter("N", x7, y7, canvas, paint, currentCharacter.equals("N"));
462                    drawCharacter("M", x8, y8, canvas, paint, currentCharacter.equals("M"));
463                    drawCharacter("L", x9, y9, canvas, paint, currentCharacter.equals("L"));
464                    break;
465                case QU:
466                    paint.setColor(Color.GREEN);
467                    drawCharacter("W", x1, y1, canvas, paint, currentCharacter.equals("W"));
468                    drawCharacter("X", x2, y2, canvas, paint, currentCharacter.equals("X"));
469                    drawCharacter("Q", x3, y3, canvas, paint, currentCharacter.equals("Q"));
470                    drawCharacter("V", x4, y4, canvas, paint, currentCharacter.equals("V"));
471                    drawCharacter("R", x6, y6, canvas, paint, currentCharacter.equals("R"));
472                    drawCharacter("U", x7, y7, canvas, paint, currentCharacter.equals("U"));
473                    drawCharacter("T", x8, y8, canvas, paint, currentCharacter.equals("T"));
474                    drawCharacter("S", x9, y9, canvas, paint, currentCharacter.equals("S"));
475                    break;
476                case Y:
477                    paint.setColor(Color.YELLOW);
478                    drawCharacter(",", x1, y1, canvas, paint, currentCharacter.equals(","));
479                    drawCharacter("!", x2, y2, canvas, paint, currentCharacter.equals("!"));
480                    drawCharacter("SPACE", x3, y3, canvas, paint, currentCharacter.equals("SPACE"));
481                    drawCharacter("<-", x4, y4, canvas, paint, currentCharacter.equals("<-"));
482                    drawCharacter("Y", x6, y6, canvas, paint, currentCharacter.equals("Y"));
483                    drawCharacter(".", x7, y7, canvas, paint, currentCharacter.equals("."));
484                    drawCharacter("?", x8, y8, canvas, paint, currentCharacter.equals("?"));
485                    drawCharacter("Z", x9, y9, canvas, paint, currentCharacter.equals("Z"));
486                    break;
487                default:
488                    paint.setColor(Color.RED);
489                    canvas.drawText("A", x1, y1, paint);
490                    canvas.drawText("E", x9, y9, paint);
491                    paint.setColor(Color.BLUE);
492                    canvas.drawText("I", x2, y2, paint);
493                    canvas.drawText("M", x8, y8, paint);
494                    paint.setColor(Color.GREEN);
495                    canvas.drawText("Q", x3, y3, paint);
496                    canvas.drawText("U", x7, y7, paint);
497                    paint.setColor(Color.YELLOW);
498                    canvas.drawText("Y", x6, y6, paint);
499                    canvas.drawText("<-", x4, y4, paint);
500                    break;
501            }
502        }
503
504    }
505
506    public void backspace() {
507        parent.handleBackspace();
508        String deletedCharacter = "";
509        if (currentString.length() > 0) {
510            deletedCharacter = "" + currentString.charAt(currentString.length() - 1);
511            currentString = currentString.substring(0, currentString.length() - 1);
512        }
513        if (!deletedCharacter.equals("")) {
514            // parent.tts.speak(deletedCharacter + " deleted.", 0, null);
515        } else {
516            // parent.tts.playEarcon(TTSEarcon.TOCK.toString(), 0, null);
517            // parent.tts.playEarcon(TTSEarcon.TOCK.toString(), 1, null);
518        }
519        invalidate();
520    }
521
522    private void drawCharacter(String character, int x, int y, Canvas canvas, Paint paint,
523            boolean isSelected) {
524        int regSize = 50;
525        int selectedSize = regSize * 2;
526        if (isSelected) {
527            paint.setTextSize(selectedSize);
528        } else {
529            paint.setTextSize(regSize);
530        }
531        canvas.drawText(character, x, y, paint);
532    }
533
534    public int evalMotion(double x, double y) {
535        float rTolerance = 25;
536        double thetaTolerance = (Math.PI / 16);
537
538        double r = Math.sqrt(((downX - x) * (downX - x)) + ((downY - y) * (downY - y)));
539
540        if (r < rTolerance) {
541            return 5;
542        }
543
544        double theta = Math.atan2(downY - y, downX - x);
545
546        if (Math.abs(theta - left) < thetaTolerance) {
547            return 4;
548        } else if (Math.abs(theta - upleft) < thetaTolerance) {
549            return 1;
550        } else if (Math.abs(theta - up) < thetaTolerance) {
551            return 2;
552        } else if (Math.abs(theta - upright) < thetaTolerance) {
553            return 3;
554        } else if (Math.abs(theta - downright) < thetaTolerance) {
555            return 9;
556        } else if (Math.abs(theta - down) < thetaTolerance) {
557            return 8;
558        } else if (Math.abs(theta - downleft) < thetaTolerance) {
559            return 7;
560        } else if ((theta > right - thetaTolerance) || (theta < rightWrap + thetaTolerance)) {
561            return 6;
562        }
563
564        // Off by more than the threshold, so it doesn't count
565        return -1;
566    }
567    
568    
569    private int keyboardMode = 0;
570    
571    private void toggleKeyboardMode(){
572        if (keyboardMode == 0){
573            keyboardMode = 1;
574            parent.mTts.speak("Numbers", 2, null);
575        } else {
576            keyboardMode = 0;
577            parent.mTts.speak("Alpha", 2, null);
578        }
579    }
580    
581
582    @Override
583    public boolean onKeyDown(int keyCode, KeyEvent event) {
584      String input = "";
585      switch (keyCode) {        
586        case KeyEvent.KEYCODE_MENU:
587          toggleKeyboardMode();
588          return true;
589      }
590      return false;
591    }
592    
593
594}