PageRenderTime 43ms CodeModel.GetById 9ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 0ms

/remindme/src/com/google/marvin/remindme/NumberEntryView.java

http://eyes-free.googlecode.com/
Java | 427 lines | 341 code | 47 blank | 39 comment | 56 complexity | c29a92e57cad8d2f40c7d28d7bd3a331 MD5 | raw file
  1/*
  2 * Copyright (C) 2008 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.remindme;
 18
 19
 20import com.google.marvin.remindme.ShakeDetector.ShakeListener;
 21import com.google.tts.TTSParams;
 22
 23import android.content.Context;
 24import android.graphics.Canvas;
 25import android.graphics.Color;
 26import android.graphics.Paint;
 27import android.graphics.Typeface;
 28import android.os.Vibrator;
 29import android.view.KeyCharacterMap;
 30import android.view.KeyEvent;
 31import android.view.MotionEvent;
 32import android.view.View;
 33import android.widget.TextView;
 34
 35/**
 36 * Implements the user interface for doing slide dialing.
 37 * 
 38 * @author clchen@google.com (Charles L. Chen) Created 8-2-2008
 39 */
 40public class NumberEntryView extends TextView {
 41  private final double left = 0;
 42  private final double upleft = Math.PI * .25;
 43  private final double up = Math.PI * .5;
 44  private final double upright = Math.PI * .75;
 45  private final double downright = -Math.PI * .75;
 46  private final double down = -Math.PI * .5;
 47  private final double downleft = -Math.PI * .25;
 48  private final double right = Math.PI;
 49  private final double rightWrap = -Math.PI;
 50
 51  public RemindMe parent;
 52  private double downX;
 53  private double downY;
 54  private String currentValue;
 55  private Vibrator vibe;
 56
 57  private String dialedNumber;
 58
 59  boolean screenIsBeingTouched = false;
 60  boolean screenVisible = true;
 61
 62  private ShakeDetector shakeDetector;
 63
 64  public NumberEntryView(Context context) {
 65    super(context);
 66    parent = (RemindMe) context;
 67    // android.os.Debug.waitForDebugger();
 68    downX = 0;
 69    downY = 0;
 70    dialedNumber = "";
 71    currentValue = "";
 72    vibe = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
 73    setClickable(true);
 74    setFocusable(true);
 75    setFocusableInTouchMode(true);
 76    requestFocus();
 77    screenVisible = true;
 78    screenIsBeingTouched = false;
 79    shakeDetector = new ShakeDetector(context, new ShakeListener() {
 80      public void onShakeDetected() {
 81        deleteNumber();
 82      }
 83    });
 84  }
 85
 86  public void shutdown() {
 87    shakeDetector.shutdown();
 88  }
 89
 90
 91  @Override
 92  public boolean onTouchEvent(MotionEvent event) {
 93    int action = event.getAction();
 94    float x = event.getX();
 95    float y = event.getY();
 96    String prevVal = "";
 97    switch (action) {
 98      case MotionEvent.ACTION_DOWN:
 99        screenIsBeingTouched = true;
100        downX = x;
101        downY = y;
102        currentValue = "";
103        break;
104      case MotionEvent.ACTION_UP:
105        screenIsBeingTouched = false;
106        prevVal = currentValue;
107        currentValue = evalMotion(x, y);
108        // Do some correction if the user lifts up on deadspace
109        if (currentValue.length() == 0) {
110          currentValue = prevVal;
111        }
112        parent.tts.speak(currentValue, 0, null);
113        dialedNumber = dialedNumber + currentValue;
114        break;
115      default:
116        screenIsBeingTouched = true;
117        prevVal = currentValue;
118        currentValue = evalMotion(x, y);
119        // Do nothing since we want a deadzone here;
120        // restore the state to the previous value.
121        if (currentValue.length() == 0) {
122          currentValue = prevVal;
123          break;
124        }
125        if (prevVal != currentValue) {
126          String[] params = new String[1];
127          params[0] = TTSParams.VOICE_FEMALE.toString();
128          parent.tts.speak(currentValue, 0, params);
129          long[] pattern = {0, 1, 40, 41};
130          vibe.vibrate(pattern, -1);
131        }
132        break;
133    }
134    invalidate();
135    return true;
136  }
137
138  public String evalMotion(double x, double y) {
139    float rTolerance = 25;
140    double thetaTolerance = (Math.PI / 12);
141
142    boolean movedFar = false;
143
144    double r = Math.sqrt(((downX - x) * (downX - x)) + ((downY - y) * (downY - y)));
145
146    if (r < rTolerance) {
147      return "5";
148    }
149    if (r > 6 * rTolerance) {
150      movedFar = true;
151    }
152
153
154    double theta = Math.atan2(downY - y, downX - x);
155
156    if (Math.abs(theta - left) < thetaTolerance) {
157      return "4";
158    } else if (Math.abs(theta - upleft) < thetaTolerance) {
159      return "1";
160    } else if (Math.abs(theta - up) < thetaTolerance) {
161      return "2";
162    } else if (Math.abs(theta - upright) < thetaTolerance) {
163      return "3";
164    } else if (Math.abs(theta - downright) < thetaTolerance) {
165      return "9";
166    } else if (Math.abs(theta - down) < thetaTolerance) {
167      return movedFar ? "0" : "8";
168    } else if (Math.abs(theta - downleft) < thetaTolerance) {
169      return "7";
170    } else if ((theta > right - thetaTolerance) || (theta < rightWrap + thetaTolerance)) {
171      return "6";
172    }
173
174    // Off by more than the threshold, so it doesn't count
175    return "";
176  }
177
178  @Override
179  public void onDraw(Canvas canvas) {
180    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
181    paint.setColor(Color.WHITE);
182    paint.setTextAlign(Paint.Align.CENTER);
183    paint.setTypeface(Typeface.DEFAULT_BOLD);
184
185    if (!screenIsBeingTouched) {
186      int x = getWidth() / 2;
187      int y = (getHeight() / 2) - 35;
188      paint.setTextSize(400);
189      y -= paint.ascent() / 2;
190      canvas.drawText(currentValue, x, y, paint);
191
192      x = 5;
193      y = 30;
194      paint.setTextSize(50);
195      paint.setTextAlign(Paint.Align.LEFT);
196      y -= paint.ascent() / 2;
197      canvas.drawText(dialedNumber, x, y, paint);
198
199      x = 5;
200      y = getHeight() - 40;
201      paint.setTextSize(20);
202      paint.setTextAlign(Paint.Align.LEFT);
203      y -= paint.ascent() / 2;
204      canvas.drawText("Stroke screen to set time.", x, y, paint);
205      x = 5;
206      y = getHeight() - 20;
207      paint.setTextSize(20);
208      paint.setTextAlign(Paint.Align.LEFT);
209      y -= paint.ascent() / 2;
210      canvas.drawText("Press CALL to confirm.", x, y, paint);
211
212    } else {
213
214      int offset = 130;
215      int regSize = 100;
216      int selectedSize = regSize * 2;
217
218
219      int x1 = (int) downX - offset;
220      int y1 = (int) downY - offset;
221      int x2 = (int) downX;
222      int y2 = (int) downY - offset;
223      int x3 = (int) downX + offset;
224      int y3 = (int) downY - offset;
225      int x4 = (int) downX - offset;
226      int y4 = (int) downY;
227      int x5 = (int) downX;
228      int y5 = (int) downY;
229      int x6 = (int) downX + offset;
230      int y6 = (int) downY;
231      int x7 = (int) downX - offset;
232      int y7 = (int) downY + offset;
233      int x8 = (int) downX;
234      int y8 = (int) downY + offset;
235      int x9 = (int) downX + offset;
236      int y9 = (int) downY + offset;
237
238
239      int x0 = (int) downX;
240      int y0 = (int) downY + offset + offset;
241
242
243      if (currentValue.equals("1")) {
244        paint.setTextSize(selectedSize);
245      } else {
246        paint.setTextSize(regSize);
247      }
248      y1 -= paint.ascent() / 2;
249      canvas.drawText("1", x1, y1, paint);
250
251      if (currentValue.equals("2")) {
252        paint.setTextSize(selectedSize);
253      } else {
254        paint.setTextSize(regSize);
255      }
256      y2 -= paint.ascent() / 2;
257      canvas.drawText("2", x2, y2, paint);
258
259      if (currentValue.equals("3")) {
260        paint.setTextSize(selectedSize);
261      } else {
262        paint.setTextSize(regSize);
263      }
264      y3 -= paint.ascent() / 2;
265      canvas.drawText("3", x3, y3, paint);
266
267      if (currentValue.equals("4")) {
268        paint.setTextSize(selectedSize);
269      } else {
270        paint.setTextSize(regSize);
271      }
272      y4 -= paint.ascent() / 2;
273      canvas.drawText("4", x4, y4, paint);
274
275      if (currentValue.equals("5")) {
276        paint.setTextSize(selectedSize);
277      } else {
278        paint.setTextSize(regSize);
279      }
280      y5 -= paint.ascent() / 2;
281      canvas.drawText("5", x5, y5, paint);
282
283      if (currentValue.equals("6")) {
284        paint.setTextSize(selectedSize);
285      } else {
286        paint.setTextSize(regSize);
287      }
288      y6 -= paint.ascent() / 2;
289      canvas.drawText("6", x6, y6, paint);
290
291      if (currentValue.equals("7")) {
292        paint.setTextSize(selectedSize);
293      } else {
294        paint.setTextSize(regSize);
295      }
296      y7 -= paint.ascent() / 2;
297      canvas.drawText("7", x7, y7, paint);
298
299      if (currentValue.equals("8")) {
300        paint.setTextSize(selectedSize);
301      } else {
302        paint.setTextSize(regSize);
303      }
304      y8 -= paint.ascent() / 2;
305      canvas.drawText("8", x8, y8, paint);
306
307      if (currentValue.equals("9")) {
308        paint.setTextSize(selectedSize);
309      } else {
310        paint.setTextSize(regSize);
311      }
312      y9 -= paint.ascent() / 2;
313      canvas.drawText("9", x9, y9, paint);
314
315      if (currentValue.equals("0")) {
316        paint.setTextSize(selectedSize);
317      } else {
318        paint.setTextSize(regSize);
319      }
320      y0 -= paint.ascent() / 2;
321      canvas.drawText("0", x0, y0, paint);
322    }
323  }
324
325  @Override
326  public boolean onKeyDown(int keyCode, KeyEvent event) {
327    boolean newNumberEntered = false;
328    switch (keyCode) {
329      case KeyEvent.KEYCODE_CALL:
330        parent.setTime(dialedNumber);
331        return true;
332        /*
333        if (!confirmed) {
334          parent.tts.speak("Reminder at ", 1, null);
335          for (int i = 0; i < dialedNumber.length(); i++) {
336            String digit = dialedNumber.charAt(i) + "";
337            parent.tts.speak(digit, 1, null);
338          }
339          confirmed = true;
340          return true;
341        } else {
342          parent.setTime(dialedNumber);
343          return true;
344        }
345        */
346
347      case KeyEvent.KEYCODE_0:
348        newNumberEntered = true;
349        break;
350      case KeyEvent.KEYCODE_1:
351        newNumberEntered = true;
352        break;
353      case KeyEvent.KEYCODE_2:
354        newNumberEntered = true;
355        break;
356      case KeyEvent.KEYCODE_3:
357        newNumberEntered = true;
358        break;
359      case KeyEvent.KEYCODE_4:
360        newNumberEntered = true;
361        break;
362      case KeyEvent.KEYCODE_5:
363        newNumberEntered = true;
364        break;
365      case KeyEvent.KEYCODE_6:
366        newNumberEntered = true;
367        break;
368      case KeyEvent.KEYCODE_7:
369        newNumberEntered = true;
370        break;
371      case KeyEvent.KEYCODE_8:
372        newNumberEntered = true;
373        break;
374      case KeyEvent.KEYCODE_9:
375        newNumberEntered = true;
376        break;
377      case KeyEvent.KEYCODE_DEL:
378        deleteNumber();
379        return true;
380    }
381    if (newNumberEntered) {
382      KeyCharacterMap kmap = KeyCharacterMap.load(event.getDeviceId());
383      currentValue = kmap.getNumber(keyCode) + "";
384      parent.tts.speak(currentValue, 0, null);
385      dialedNumber = dialedNumber + currentValue;
386      invalidate();
387      return true;
388    }
389    return false;
390  }
391
392  private void deleteNumber() {
393    String deletedNum;
394    if (dialedNumber.length() > 0) {
395      deletedNum = "" + dialedNumber.charAt(dialedNumber.length() - 1);
396      dialedNumber = dialedNumber.substring(0, dialedNumber.length() - 1);
397    } else {
398      deletedNum = "";
399    }
400    if (!deletedNum.equals("")) {
401      String[] params = new String[1];
402      params[0] = TTSParams.VOICE_ROBOT.toString();
403      parent.tts.speak(deletedNum, 0, params);
404    } else {
405      parent.tts.speak("[tock]", 0, null);
406      parent.tts.speak("[tock]", 1, null);
407    }
408    currentValue = "";
409    invalidate();
410  }
411
412  public void reset() {
413    dialedNumber = "";
414    parent.tts.speak("Please enter a valid time.", 0, null);
415    currentValue = "";
416    invalidate();
417  }
418
419  @Override
420  protected void onWindowVisibilityChanged(int visibility) {
421    if (visibility == View.VISIBLE) {
422      screenVisible = true;
423    } else {
424      screenVisible = false;
425    }
426  }
427}