/ocr/cardscanner/src/com/google/marvin/cardscanner/ScannerActivity.java

http://eyes-free.googlecode.com/ · Java · 273 lines · 175 code · 36 blank · 62 comment · 34 complexity · 7d49a139da5e84da5920f6cf3e8534e7 MD5 · raw file

  1. /*
  2. * Copyright (C) 2009 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.cardscanner;
  17. import android.app.Activity;
  18. import android.content.Intent;
  19. import android.os.Bundle;
  20. import android.os.Parcelable;
  21. import android.provider.Contacts.ContactMethodsColumns;
  22. import android.provider.Contacts.People;
  23. import android.provider.Contacts.PhonesColumns;
  24. import android.provider.Contacts.Intents.Insert;
  25. import android.util.Log;
  26. import android.view.KeyEvent;
  27. import android.widget.Toast;
  28. import com.android.ocr.client.Config;
  29. import com.android.ocr.client.Intents;
  30. import com.android.ocr.client.Result;
  31. import java.util.regex.Matcher;
  32. import java.util.regex.Pattern;
  33. /**
  34. * Demonstration application for using Intents. Can load text from the camera
  35. * and copy it to the clipboard.
  36. *
  37. * @author alanv@google.com (Alan Viverette)
  38. */
  39. public class ScannerActivity extends Activity {
  40. private static final String TAG = "ScannerActivity";
  41. private static final int REQUEST_CAPTURE = 0;
  42. private static final int REQUEST_RECOGNIZE = 1;
  43. private static final int REQUEST_REVIEW = 2;
  44. private static final int DEFAULT_WIDTH = 1024;
  45. private static final int DEFAULT_HEIGHT = 768;
  46. @Override
  47. public void onCreate(Bundle savedInstanceState) {
  48. Log.i(TAG, "Creating ScannerActivity...");
  49. super.onCreate(savedInstanceState);
  50. requestCapture();
  51. }
  52. @Override
  53. public boolean onKeyDown(int keyCode, KeyEvent event) {
  54. switch (keyCode) {
  55. case KeyEvent.KEYCODE_BACK: {
  56. requestCapture();
  57. return true;
  58. }
  59. }
  60. return super.onKeyDown(keyCode, event);
  61. }
  62. public void requestCapture() {
  63. Intent capture = new Intent(Intents.Capture.ACTION);
  64. capture.putExtra(Intents.Capture.WIDTH, DEFAULT_WIDTH);
  65. capture.putExtra(Intents.Capture.HEIGHT, DEFAULT_HEIGHT);
  66. startActivityForResult(capture, REQUEST_CAPTURE);
  67. }
  68. public void requestRecognize(Intent data) {
  69. Parcelable extra = data.getParcelableExtra(Intents.Capture.CONFIG);
  70. if (!(extra instanceof Config)) {
  71. Log.e(TAG, "requestRecognize received wrong parcelable type (was " + extra + ")");
  72. return;
  73. }
  74. Config config = (Config) extra;
  75. config.pageSegMode = Config.PSM_AUTO;
  76. config.options |= Config.OPT_NORMALIZE_BG;
  77. if (config.image != null) {
  78. Intent recognize = new Intent(Intents.Recognize.ACTION);
  79. recognize.putExtra(Intents.Recognize.CONFIG, config);
  80. startActivityForResult(recognize, REQUEST_RECOGNIZE);
  81. } else {
  82. Log.e(TAG, "requestRecognize received null image");
  83. }
  84. }
  85. @Override
  86. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  87. switch (requestCode) {
  88. case REQUEST_CAPTURE: {
  89. if (resultCode == RESULT_OK) {
  90. requestRecognize(data);
  91. } else if (resultCode == RESULT_CANCELED) {
  92. // Maintain the illusion that Capture is the main activity
  93. finish();
  94. } else {
  95. Toast.makeText(this, R.string.capture_failed, 5).show();
  96. Log.e(TAG, "REQUEST_CAPTURE received unexpected resultCode (" + resultCode + ")");
  97. requestCapture();
  98. }
  99. break;
  100. }
  101. case REQUEST_RECOGNIZE: {
  102. if (resultCode == RESULT_OK) {
  103. handleCompleted(data);
  104. } else if (resultCode == RESULT_CANCELED) {
  105. Toast.makeText(this, R.string.recognize_canceled, 3).show();
  106. Log.i(TAG, "REQUEST_RECOGNIZED received RESULT_CANCELED");
  107. requestCapture();
  108. } else {
  109. Toast.makeText(this, R.string.recognize_failed, 5).show();
  110. Log.e(TAG, "REQUEST_RECOGNIZE received unexpected resultCode (" + resultCode + ")");
  111. requestCapture();
  112. }
  113. break;
  114. }
  115. case REQUEST_REVIEW: {
  116. if (resultCode == RESULT_OK) {
  117. Toast.makeText(this, R.string.contact_added, 3).show();
  118. }
  119. requestCapture();
  120. break;
  121. }
  122. default: {
  123. Log.i(TAG, "Received unknown activity request code (" + requestCode + ")");
  124. super.onActivityResult(requestCode, resultCode, data);
  125. }
  126. }
  127. }
  128. /**
  129. * Pipes output from OcrActivity into the EditContactActivity of the built-in
  130. * Contact manager application.
  131. *
  132. * @param data Result data returned as an Intent from OcrActivity.
  133. */
  134. private void handleCompleted(Intent data) {
  135. Parcelable[] results = data.getParcelableArrayExtra(Intents.Recognize.RESULTS);
  136. Bundle extras = extractInformation(results);
  137. if (extras.isEmpty()) {
  138. Toast.makeText(this, "Sorry, could not recognize card.", 5).show();
  139. requestCapture();
  140. } else {
  141. Intent intent = new Intent();
  142. intent.setAction(Intent.ACTION_INSERT);
  143. intent.addCategory(Intent.CATEGORY_DEFAULT);
  144. intent.setData(People.CONTENT_URI);
  145. intent.putExtras(extras);
  146. // TODO: Figure out how to keep EditContactActivity from saving the
  147. // contact if the user presses KEYCODE_BACK.
  148. startActivityForResult(intent, REQUEST_REVIEW);
  149. }
  150. }
  151. /**
  152. * Extracts contact information from a string using regular expressions. This
  153. * function is relatively tolerant of poor OCR, as long as it maintains
  154. * letters and characters.
  155. *
  156. * @param results The result array from which to extract contact information.
  157. * @return Returns a Bundle containing extracted Contact.Insert fields.
  158. */
  159. private Bundle extractInformation(Parcelable[] results) {
  160. Bundle extras = new Bundle();
  161. if (results == null || results.length == 0) {
  162. return extras;
  163. }
  164. String str = "";
  165. for (Parcelable result : results) {
  166. str += ((Result) result).getString() + "\n";
  167. }
  168. Pattern p;
  169. Matcher m;
  170. /*
  171. * Name-matching Expression - Matches: T.V. Raman Alan Viverette Charles L.
  172. * Chen Julie Lythcott-Haimes - Does not match: Google Google User
  173. * Experience Team 650-720-5555 cell
  174. */
  175. p = Pattern.compile("^([A-Z]([a-z]*|\\.) *){1,2}([A-Z][a-z]+-?)+$", Pattern.MULTILINE);
  176. m = p.matcher(str);
  177. if (m.find()) {
  178. extras.putCharSequence(Insert.NAME, m.group());
  179. }
  180. /*
  181. * Address-matching Expression - Matches: 2600 Amphitheatre Pkwy. P.O. Box
  182. * 26000 1600 Pennsylvania Avenue 1 Geary - Does not match: Google T.V.
  183. * Raman 650-720-5555 cell
  184. */
  185. p =
  186. Pattern.compile("^(\\d+ ([A-Z][a-z]+.? +)*[A-Z][a-z]+.?|P.?O.? *Box +\\d+)$",
  187. Pattern.MULTILINE);
  188. m = p.matcher(str);
  189. if (m.find()) {
  190. extras.putCharSequence(Insert.POSTAL, m.group());
  191. extras.putInt(Insert.POSTAL_TYPE, ContactMethodsColumns.TYPE_WORK);
  192. extras.putBoolean(Insert.POSTAL_ISPRIMARY, true);
  193. }
  194. /*
  195. * Address-matching Expression 2 - Matches: Mountain View, CA 94304 Houston
  196. * TX 77069 Stanford, CA 94309-2901 Salt Lake City, UT 12345 - Does not
  197. * match: Cell 650-720-5555 Ext. 54085 Google 12345
  198. */
  199. p = Pattern.compile("^([A-Z][a-z]+.? *)+ *.? *[A-Z]{2}? *\\d{5}(-\\d[4])?", Pattern.MULTILINE);
  200. m = p.matcher(str);
  201. if (m.find()) {
  202. CharSequence address;
  203. if ((address = extras.getCharSequence(Insert.POSTAL)) == null)
  204. address = m.group();
  205. else
  206. address = address + ", " + m.group();
  207. extras.putCharSequence(Insert.POSTAL, address);
  208. extras.putInt(Insert.POSTAL_TYPE, ContactMethodsColumns.TYPE_WORK);
  209. extras.putBoolean(Insert.POSTAL_ISPRIMARY, true);
  210. }
  211. /*
  212. * Email-matching Expression - Matches: email: raman@google.com
  213. * spam@google.co.uk v0nn3gu7@ice9.org name @ host.com - Does not match:
  214. * #@/.cJX Google c@t
  215. */
  216. p = Pattern.compile("([A-Za-z0-9]+ *@ *[A-Za-z0-9]+(\\.[A-Za-z]{2,4})+)$", Pattern.MULTILINE);
  217. m = p.matcher(str);
  218. if (m.find()) {
  219. extras.putCharSequence(Insert.EMAIL, m.group(1));
  220. extras.putInt(Insert.EMAIL_TYPE, ContactMethodsColumns.TYPE_WORK);
  221. extras.putBoolean(Insert.EMAIL_ISPRIMARY, true);
  222. }
  223. /*
  224. * Phone-matching Expression - Matches: 1234567890 (650) 720-5678
  225. * 650-720-5678 650.720.5678 - Does not match: 12345 12345678901 720-5678
  226. */
  227. p = Pattern.compile("(?:^|\\D)(\\d{3})[)\\-. ]*?(\\d{3})[\\-. ]*?(\\d{4})(?:$|\\D)");
  228. m = p.matcher(str);
  229. if (m.find()) {
  230. String phone = "(" + m.group(1) + ") " + m.group(2) + "-" + m.group(3);
  231. extras.putCharSequence(Insert.PHONE, phone);
  232. extras.putInt(Insert.PHONE_TYPE, PhonesColumns.TYPE_WORK);
  233. extras.putBoolean(Insert.PHONE_ISPRIMARY, true);
  234. }
  235. return extras;
  236. }
  237. }