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