PageRenderTime 36ms CodeModel.GetById 2ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

/slidetext/src/com/google/marvin/slidetext/SlideTextView.java

http://eyes-free.googlecode.com/
Java | 512 lines | 456 code | 39 blank | 17 comment | 67 complexity | 305a4de7eed471bf6416f159fbac4cdb MD5 | raw file
  1package com.google.marvin.slidetext;
  2
  3import com.google.tts.TTSParams;
  4
  5import android.content.Context;
  6import android.graphics.Canvas;
  7import android.graphics.Color;
  8import android.graphics.Paint;
  9import android.graphics.Typeface;
 10import android.hardware.SensorListener;
 11import android.hardware.SensorManager;
 12import android.os.Vibrator;
 13import android.view.MotionEvent;
 14import android.view.View;
 15
 16/**
 17 * Implements the user interface for doing slide texting.
 18 * 
 19 * @author clchen@google.com (Charles L. Chen)
 20 */
 21public class SlideTextView extends View {
 22  private static final int AE = 0;
 23  private static final int IM = 1;
 24  private static final int QU = 2;
 25  private static final int Y = 4;
 26  private static final int NONE = 5;
 27  private static final double deletionForce = 2.5;
 28  private static final int deletionCount = 1;
 29  private static final long[] PATTERN = {0, 1, 40, 41};
 30
 31  private final double left = 0;
 32  private final double upleft = Math.PI * .25;
 33  private final double up = Math.PI * .5;
 34  private final double upright = Math.PI * .75;
 35  private final double downright = -Math.PI * .75;
 36  private final double down = -Math.PI * .5;
 37  private final double downleft = -Math.PI * .25;
 38  private final double right = Math.PI;
 39  private final double rightWrap = -Math.PI;
 40
 41  private SlideText parent;
 42
 43  private double downX;
 44  private double downY;
 45  private double lastX;
 46  private double lastY;
 47  private int currentValue;
 48  private boolean screenIsBeingTouched;
 49  private Vibrator vibe;
 50
 51  private int currentWheel;
 52  private String currentCharacter;
 53  private String currentString;
 54
 55  private SensorManager sensorManager;
 56  private int shakeCount = 0;
 57  private boolean lastDeletionShakePositive = false;
 58
 59  private boolean screenVisible = true;
 60  private boolean justConfirmed;
 61
 62  /**
 63   * Handles the sensor events for changes to readings and accuracy
 64   */
 65  private final SensorListener mListener = new SensorListener() {
 66    public void onSensorChanged(int sensor, float[] values) {
 67      if ((values[0] > deletionForce) && !lastDeletionShakePositive) {
 68        shakeCount++;
 69        lastDeletionShakePositive = true;
 70      } else if ((values[0] < -deletionForce) && lastDeletionShakePositive) {
 71        shakeCount++;
 72        lastDeletionShakePositive = false;
 73      }
 74      if (shakeCount > deletionCount) {
 75        backspace();
 76        shakeCount = 0;
 77      }
 78    }
 79
 80    public void onAccuracyChanged(int arg0, int arg1) {
 81      // Ignore accelerometer accuracy for now
 82    }
 83  };
 84
 85  public SlideTextView(Context context) {
 86    super(context);
 87    parent = (SlideText) context;
 88    downX = 0;
 89    downY = 0;
 90    lastX = 0;
 91    lastY = 0;
 92    currentValue = -1;
 93    screenIsBeingTouched = false;
 94    justConfirmed = false;
 95    currentWheel = NONE;
 96    currentCharacter = "";
 97    currentString = "";
 98    vibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
 99    screenVisible = true;
100    shakeCount = 0;
101    lastDeletionShakePositive = false;
102    sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
103    sensorManager.registerListener(mListener, SensorManager.SENSOR_ACCELEROMETER,
104        SensorManager.SENSOR_DELAY_FASTEST);
105  }
106
107  private void confirmEntry() {
108    screenIsBeingTouched = false;
109    int prevVal = currentValue;
110    currentValue = evalMotion(lastX, lastY);
111    // Do some correction if the user lifts up on deadspace
112    if (currentValue == -1) {
113      currentValue = prevVal;
114    }
115    // The user never got a number that wasn't deadspace,
116    // so assume 5.
117    if (currentValue == -1) {
118      currentValue = 5;
119    }
120    currentCharacter = getCharacter(currentWheel, currentValue);
121    if (currentCharacter.equals("SPACE")) {
122      currentString = currentString + " ";
123    } else if (currentCharacter.equals("MODE")) {
124      // Do nothing
125    } else {
126      currentString = currentString + currentCharacter;
127    }
128    speak(currentCharacter, 0, null);
129    justConfirmed = true;
130    invalidate();
131    initiateMotion(lastX, lastY);
132  }
133
134  private void initiateMotion(double x, double y) {
135    downX = x;
136    downY = y;
137    lastX = x;
138    lastY = y;
139    currentValue = -1;
140    currentWheel = NONE;
141    currentCharacter = "";
142  }
143
144
145  @Override
146  public boolean onTouchEvent(MotionEvent event) {
147    int action = event.getAction();
148    float x = event.getX();
149    float y = event.getY();
150    if (action == MotionEvent.ACTION_DOWN) {
151      initiateMotion(x, y);
152      return true;
153    } else if (action == MotionEvent.ACTION_UP) {
154      confirmEntry();
155      return true;
156    } else {
157      screenIsBeingTouched = true;
158      lastX = x;
159      lastY = y;
160      int prevVal = currentValue;
161      currentValue = evalMotion(x, y);
162      // Do nothing since we want a deadzone here;
163      // restore the state to the previous value.
164      if (currentValue == -1) {
165        currentValue = prevVal;
166        return true;
167      }
168      // There is a wheel that is active
169      if (currentValue != 5) {
170        if (currentWheel == NONE) {
171          currentWheel = getWheel(currentValue);
172        }
173        currentCharacter = getCharacter(currentWheel, currentValue);
174      } else {
175        currentCharacter = "";
176      }
177      invalidate();
178      if (prevVal != currentValue) {
179        if (currentCharacter.equals("")) {
180          speak("[tock]", 0, null);
181        } else {
182          String[] params = new String[1];
183          params[0] = TTSParams.VOICE_FEMALE.toString();
184          speak(currentCharacter, 0, params);
185        }
186        vibe.vibrate(PATTERN, -1);
187      }
188      return true;
189    }
190  }
191
192
193  public int getWheel(int value) {
194    switch (value) {
195      case 1:
196        return AE;
197      case 2:
198        return IM;
199      case 3:
200        return QU;
201      case 4:
202        return Y;
203      case 5:
204        return NONE;
205      case 6:
206        return Y;
207      case 7:
208        return QU;
209      case 8:
210        return IM;
211      case 9:
212        return AE;
213      default:
214        return NONE;
215    }
216  }
217
218  public String getCharacter(int wheel, int value) {
219    switch (wheel) {
220      case AE:
221        switch (value) {
222          case 1:
223            return "A";
224          case 2:
225            return "B";
226          case 3:
227            return "C";
228          case 4:
229            return "H";
230          case 5:
231            return "";
232          case 6:
233            return "D";
234          case 7:
235            return "G";
236          case 8:
237            return "F";
238          case 9:
239            return "E";
240          default:
241            return "";
242        }
243      case IM:
244        switch (value) {
245          case 1:
246            return "P";
247          case 2:
248            return "I";
249          case 3:
250            return "J";
251          case 4:
252            return "O";
253          case 5:
254            return "";
255          case 6:
256            return "K";
257          case 7:
258            return "N";
259          case 8:
260            return "M";
261          case 9:
262            return "L";
263          default:
264            return "";
265        }
266      case QU:
267        switch (value) {
268          case 1:
269            return "W";
270          case 2:
271            return "X";
272          case 3:
273            return "Q";
274          case 4:
275            return "V";
276          case 5:
277            return "";
278          case 6:
279            return "R";
280          case 7:
281            return "U";
282          case 8:
283            return "T";
284          case 9:
285            return "S";
286          default:
287            return "";
288        }
289      case Y:
290        switch (value) {
291          case 1:
292            return ",";
293          case 2:
294            return "!";
295          case 3:
296            return "MODE";
297          case 4:
298            return "SPACE";
299          case 5:
300            return "";
301          case 6:
302            return "Y";
303          case 7:
304            return ".";
305          case 8:
306            return "?";
307          case 9:
308            return "Z";
309          default:
310            return "";
311        }
312      default:
313        return "";
314    }
315  }
316
317
318  public int evalMotion(double x, double y) {
319    float rTolerance = 25;
320    double thetaTolerance = (Math.PI / 16);
321
322    boolean movedFar = false;
323
324    double r = Math.sqrt(((downX - x) * (downX - x)) + ((downY - y) * (downY - y)));
325
326    if (r < rTolerance) {
327      return 5;
328    }
329    if (r > 10 * rTolerance) {
330      movedFar = true;
331    }
332    double theta = Math.atan2(downY - y, downX - x);
333
334    if (Math.abs(theta - left) < thetaTolerance) {
335      return 4;
336    } else if (Math.abs(theta - upleft) < thetaTolerance) {
337      return 1;
338    } else if (Math.abs(theta - up) < thetaTolerance) {
339      return 2;
340    } else if (Math.abs(theta - upright) < thetaTolerance) {
341      return 3;
342    } else if (Math.abs(theta - downright) < thetaTolerance) {
343      return 9;
344    } else if (Math.abs(theta - down) < thetaTolerance) {
345      return 8;
346    } else if (Math.abs(theta - downleft) < thetaTolerance) {
347      return 7;
348    } else if ((theta > right - thetaTolerance) || (theta < rightWrap + thetaTolerance)) {
349      return 6;
350    }
351
352    // Off by more than the threshold, so it doesn't count
353    return -1;
354  }
355
356  @Override
357  public void onDraw(Canvas canvas) {
358    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
359    paint.setColor(Color.WHITE);
360    paint.setTextAlign(Paint.Align.CENTER);
361    paint.setTypeface(Typeface.DEFAULT_BOLD);
362
363    int x = 5;
364    int y = 50;
365    paint.setTextSize(50);
366    paint.setTextAlign(Paint.Align.LEFT);
367    y -= paint.ascent() / 2;
368    canvas.drawText(currentString, x, y, paint);
369
370    if (screenIsBeingTouched) {
371      int offset = 90;
372      int regSize = 50;
373      int selectedSize = regSize * 2;
374
375      int x1 = (int) downX - offset;
376      int y1 = (int) downY - offset;
377      int x2 = (int) downX;
378      int y2 = (int) downY - offset;
379      int x3 = (int) downX + offset;
380      int y3 = (int) downY - offset;
381      int x4 = (int) downX - offset;
382      int y4 = (int) downY;
383      int x6 = (int) downX + offset;
384      int y6 = (int) downY;
385      int x7 = (int) downX - offset;
386      int y7 = (int) downY + offset;
387      int x8 = (int) downX;
388      int y8 = (int) downY + offset;
389      int x9 = (int) downX + offset;
390      int y9 = (int) downY + offset;
391
392
393      y1 -= paint.ascent() / 2;
394      y2 -= paint.ascent() / 2;
395      y3 -= paint.ascent() / 2;
396      y4 -= paint.ascent() / 2;
397      y6 -= paint.ascent() / 2;
398      y7 -= paint.ascent() / 2;
399      y8 -= paint.ascent() / 2;
400      y9 -= paint.ascent() / 2;
401
402      switch (currentWheel) {
403        case AE:
404          paint.setColor(Color.RED);
405          drawCharacter("A", x1, y1, canvas, paint, currentCharacter.equals("A"));
406          drawCharacter("B", x2, y2, canvas, paint, currentCharacter.equals("B"));
407          drawCharacter("C", x3, y3, canvas, paint, currentCharacter.equals("C"));
408          drawCharacter("H", x4, y4, canvas, paint, currentCharacter.equals("H"));
409          drawCharacter("D", x6, y6, canvas, paint, currentCharacter.equals("D"));
410          drawCharacter("G", x7, y7, canvas, paint, currentCharacter.equals("G"));
411          drawCharacter("F", x8, y8, canvas, paint, currentCharacter.equals("F"));
412          drawCharacter("E", x9, y9, canvas, paint, currentCharacter.equals("E"));
413          break;
414        case IM:
415          paint.setColor(Color.BLUE);
416          drawCharacter("P", x1, y1, canvas, paint, currentCharacter.equals("P"));
417          drawCharacter("I", x2, y2, canvas, paint, currentCharacter.equals("I"));
418          drawCharacter("J", x3, y3, canvas, paint, currentCharacter.equals("J"));
419          drawCharacter("O", x4, y4, canvas, paint, currentCharacter.equals("O"));
420          drawCharacter("K", x6, y6, canvas, paint, currentCharacter.equals("K"));
421          drawCharacter("N", x7, y7, canvas, paint, currentCharacter.equals("N"));
422          drawCharacter("M", x8, y8, canvas, paint, currentCharacter.equals("M"));
423          drawCharacter("L", x9, y9, canvas, paint, currentCharacter.equals("L"));
424          break;
425        case QU:
426          paint.setColor(Color.GREEN);
427          drawCharacter("W", x1, y1, canvas, paint, currentCharacter.equals("W"));
428          drawCharacter("X", x2, y2, canvas, paint, currentCharacter.equals("X"));
429          drawCharacter("Q", x3, y3, canvas, paint, currentCharacter.equals("Q"));
430          drawCharacter("V", x4, y4, canvas, paint, currentCharacter.equals("V"));
431          drawCharacter("R", x6, y6, canvas, paint, currentCharacter.equals("R"));
432          drawCharacter("U", x7, y7, canvas, paint, currentCharacter.equals("U"));
433          drawCharacter("T", x8, y8, canvas, paint, currentCharacter.equals("T"));
434          drawCharacter("S", x9, y9, canvas, paint, currentCharacter.equals("S"));
435          break;
436        case Y:
437          paint.setColor(Color.YELLOW);
438          drawCharacter(",", x1, y1, canvas, paint, currentCharacter.equals(","));
439          drawCharacter("!", x2, y2, canvas, paint, currentCharacter.equals("!"));
440          drawCharacter("MODE", x3, y3, canvas, paint, currentCharacter.equals("MODE"));
441          drawCharacter("SPACE", x4, y4, canvas, paint, currentCharacter.equals("SPACE"));
442          drawCharacter("Y", x6, y6, canvas, paint, currentCharacter.equals("Y"));
443          drawCharacter(".", x7, y7, canvas, paint, currentCharacter.equals("."));
444          drawCharacter("?", x8, y8, canvas, paint, currentCharacter.equals("?"));
445          drawCharacter("Z", x9, y9, canvas, paint, currentCharacter.equals("Z"));
446          break;
447        default:
448          paint.setColor(Color.RED);
449          canvas.drawText("A", x1, y1, paint);
450          canvas.drawText("E", x9, y9, paint);
451          paint.setColor(Color.BLUE);
452          canvas.drawText("I", x2, y2, paint);
453          canvas.drawText("M", x8, y8, paint);
454          paint.setColor(Color.GREEN);
455          canvas.drawText("Q", x3, y3, paint);
456          canvas.drawText("U", x7, y7, paint);
457          paint.setColor(Color.YELLOW);
458          canvas.drawText("Y", x6, y6, paint);
459          canvas.drawText("SPACE", x4, y4, paint);
460          break;
461      }
462    }
463  }
464
465  private void drawCharacter(String character, int x, int y, Canvas canvas, Paint paint,
466      boolean isSelected) {
467    int regSize = 50;
468    int selectedSize = regSize * 2;
469    if (isSelected) {
470      paint.setTextSize(selectedSize);
471    } else {
472      paint.setTextSize(regSize);
473    }
474    canvas.drawText(character, x, y, paint);
475  }
476
477  public void backspace() {
478    String deletedCharacter = "";
479    if (currentString.length() > 0) {
480      deletedCharacter = "" + currentString.charAt(currentString.length() - 1);
481      currentString = currentString.substring(0, currentString.length() - 1);
482    }
483    if (!deletedCharacter.equals("")) {
484      speak(deletedCharacter, 0, new String[] {TTSParams.VOICE_ROBOT.toString()});
485    } else {
486      speak("Nothing to delete", 0, null);
487    }
488    invalidate();
489  }
490
491
492  @Override
493  protected void onWindowVisibilityChanged(int visibility) {
494    if (visibility == View.VISIBLE) {
495      screenVisible = true;
496    } else {
497      screenVisible = false;
498    }
499  }
500
501  private void speak(String text, int queueMode, String[] params) {
502    if (!screenVisible) {
503      return;
504    }
505    if (justConfirmed) {
506      queueMode = 1;
507      justConfirmed = false;
508    }
509    parent.tts.speak(text, queueMode, params);
510  }
511
512}