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