/remindme/src/com/google/marvin/remindme/NumberEntryView.java
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}