PageRenderTime 65ms CodeModel.GetById 50ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/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 */
 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}