Final changes for v2.7 of Barcode Scanner, including sending product lookups to the...
[zxing.git] / android / src / com / google / zxing / client / android / result / ResultHandler.java
index bc28521..5c150fe 100644 (file)
 package com.google.zxing.client.android.result;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Contacts;
 import com.google.zxing.client.android.Intents;
 import com.google.zxing.client.android.R;
 import com.google.zxing.client.android.SearchBookContentsActivity;
+import com.google.zxing.client.android.LocaleManager;
+import com.google.zxing.client.android.Contents;
 import com.google.zxing.client.result.ParsedResult;
 import com.google.zxing.client.result.ParsedResultType;
 
 import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.text.DateFormat;
 import java.util.Date;
 import java.util.GregorianCalendar;
 
 public abstract class ResultHandler {
 
+  private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");
+  private static final DateFormat DATE_TIME_FORMAT = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
+
   public static final int MAX_BUTTON_COUNT = 4;
 
-  protected ParsedResult mResult;
-  private Activity mActivity;
+  protected final ParsedResult mResult;
+  private final Activity mActivity;
 
-  public ResultHandler(Activity activity, ParsedResult result) {
+  protected ResultHandler(Activity activity, ParsedResult result) {
     mResult = result;
     mActivity = activity;
   }
@@ -100,7 +109,7 @@ public abstract class ResultHandler {
    * @param start   The start time as yyyyMMdd or yyyyMMdd'T'HHmmss or yyyyMMdd'T'HHmmss'Z'
    * @param end     The end time as yyyyMMdd or yyyyMMdd'T'HHmmss or yyyyMMdd'T'HHmmss'Z'
    */
-  public void addCalendarEvent(String summary, String start, String end) {
+  public final void addCalendarEvent(String summary, String start, String end) {
     Intent intent = new Intent(Intent.ACTION_EDIT);
     intent.setType("vnd.android.cursor.item/event");
     intent.putExtra("beginTime", calculateMilliseconds(start));
@@ -112,19 +121,23 @@ public abstract class ResultHandler {
     launchIntent(intent);
   }
 
-  private long calculateMilliseconds(String when) {
+  private static long calculateMilliseconds(String when) {
     if (when.length() == 8) {
       // Only contains year/month/day
-      SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
-      Date date = format.parse(when, new ParsePosition(0));
+      Date date;
+      synchronized (DATE_FORMAT) {
+        date = DATE_FORMAT.parse(when, new ParsePosition(0));
+      }
       return date.getTime();
     } else {
       // The when string can be local time, or UTC if it ends with a Z
-      SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd'T'HHmmss");
-      Date date = format.parse(when.substring(0, 15), new ParsePosition(0));
+      Date date;
+      synchronized (DATE_TIME_FORMAT) {
+       date = DATE_TIME_FORMAT.parse(when.substring(0, 15), new ParsePosition(0));
+      }
       long milliseconds = date.getTime();
       if (when.length() == 16 && when.charAt(15) == 'Z') {
-        GregorianCalendar calendar = new GregorianCalendar();
+        Calendar calendar = new GregorianCalendar();
         int offset = (calendar.get(java.util.Calendar.ZONE_OFFSET) +
             calendar.get(java.util.Calendar.DST_OFFSET));
         milliseconds += offset;
@@ -133,13 +146,24 @@ public abstract class ResultHandler {
     }
   }
 
-  public void addContact(String[] names, String[] phoneNumbers, String[] emails, String note,
+  public final void addContact(String[] names, String[] phoneNumbers, String[] emails, String note,
                          String address, String org, String title) {
 
+    // Only use the first name in the array, if present.
     Intent intent = new Intent(Contacts.Intents.Insert.ACTION, Contacts.People.CONTENT_URI);
-    putExtra(intent, Contacts.Intents.Insert.NAME, names);
-    putExtra(intent, Contacts.Intents.Insert.PHONE, phoneNumbers);
-    putExtra(intent, Contacts.Intents.Insert.EMAIL, emails);
+    putExtra(intent, Contacts.Intents.Insert.NAME, names != null ? names[0] : null);
+
+    int phoneCount = Math.min((phoneNumbers != null) ? phoneNumbers.length : 0,
+        Contents.PHONE_KEYS.length);
+    for (int x = 0; x < phoneCount; x++) {
+      putExtra(intent, Contents.PHONE_KEYS[x], phoneNumbers[x]);
+    }
+
+    int emailCount = Math.min((emails != null) ? emails.length : 0, Contents.EMAIL_KEYS.length);
+    for (int x = 0; x < emailCount; x++) {
+      putExtra(intent, Contents.EMAIL_KEYS[x], emails[x]);
+    }
+
     putExtra(intent, Contacts.Intents.Insert.NOTES, note);
     putExtra(intent, Contacts.Intents.Insert.POSTAL, address);
     putExtra(intent, Contacts.Intents.Insert.COMPANY, org);
@@ -147,30 +171,33 @@ public abstract class ResultHandler {
     launchIntent(intent);
   }
 
-  public void shareByEmail(String contents) {
-    sendEmailFromUri("mailto:", mActivity.getString(R.string.msg_share_barcode), contents);
+  public final void shareByEmail(String contents) {
+    sendEmailFromUri("mailto:", mActivity.getString(R.string.msg_share_subject_line), contents);
   }
 
-  public void sendEmail(String address, String subject, String body) {
+  public final void sendEmail(String address, String subject, String body) {
     sendEmailFromUri("mailto:" + address, subject, body);
   }
 
-  public void sendEmailFromUri(String uri, String subject, String body) {
-    Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
-    putExtra(intent, "subject", subject);
-    putExtra(intent, "body", body);
+  // Use public Intent fields rather than private GMail app fields to specify subject and body.
+  public final void sendEmailFromUri(String uri, String subject, String body) {
+    Intent intent = new Intent(Intent.ACTION_SEND, Uri.parse(uri));
+    putExtra(intent, Intent.EXTRA_SUBJECT, subject);
+    putExtra(intent, Intent.EXTRA_TEXT, body);
+    intent.setType("text/plain");
     launchIntent(intent);
   }
 
-  public void shareBySMS(String contents) {
-    sendSMSFromUri("smsto:", mActivity.getString(R.string.msg_share_barcode) + ":\n" + contents);
+  public final void shareBySMS(String contents) {
+    sendSMSFromUri("smsto:", mActivity.getString(R.string.msg_share_subject_line) + ":\n" +
+        contents);
   }
 
-  public void sendSMS(String phoneNumber, String body) {
+  public final void sendSMS(String phoneNumber, String body) {
     sendSMSFromUri("smsto:" + phoneNumber, body);
   }
 
-  public void sendSMSFromUri(String uri, String body) {
+  public final void sendSMSFromUri(String uri, String body) {
     Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
     putExtra(intent, "sms_body", body);
     // Exit the app once the SMS is sent
@@ -178,11 +205,11 @@ public abstract class ResultHandler {
     launchIntent(intent);
   }
 
-  public void sendMMS(String phoneNumber, String subject, String body) {
+  public final void sendMMS(String phoneNumber, String subject, String body) {
     sendMMSFromUri("mmsto:" + phoneNumber, subject, body);
   }
 
-  public void sendMMSFromUri(String uri, String subject, String body) {
+  public final void sendMMSFromUri(String uri, String subject, String body) {
     Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
     // The Messaging app needs to see a valid subject or else it will treat this an an SMS.
     if (subject == null || subject.length() == 0) {
@@ -195,15 +222,15 @@ public abstract class ResultHandler {
     launchIntent(intent);
   }
 
-  public void dialPhone(String phoneNumber) {
+  public final void dialPhone(String phoneNumber) {
     launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)));
   }
 
-  public void dialPhoneFromUri(String uri) {
+  public final void dialPhoneFromUri(String uri) {
     launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse(uri)));
   }
 
-  public void openMap(String geoURI) {
+  public final void openMap(String geoURI) {
     launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(geoURI)));
   }
 
@@ -213,41 +240,44 @@ public abstract class ResultHandler {
    * @param address The address to find
    * @param title An optional title, e.g. the name of the business at this address
    */
-  public void searchMap(String address, String title) {
+  public final void searchMap(String address, String title) {
     String query = address;
     if (title != null && title.length() > 0) {
-      query = query + " (" + title + ")";
+      query = query + " (" + title + ')';
     }
     launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" + Uri.encode(query))));
   }
 
-  public void getDirections(float latitude, float longitude) {
-    launchIntent(new Intent(Intent.ACTION_VIEW,
-        Uri.parse("http://maps.google.com/maps?f=d&daddr=" + latitude + "," + longitude)));
+  public final void getDirections(double latitude, double longitude) {
+    launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google." +
+        LocaleManager.getCountryTLD() + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
   }
 
-  public void openProductSearch(String upc) {
-    Uri uri = Uri.parse("http://www.google.com/products?q=" + upc);
+  // Uses the mobile-specific version of Product Search, which is formatted for small screens.
+  public final void openProductSearch(String upc) {
+    Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD() +
+        "/m/products?q=" + upc + "&source=zxing");
     launchIntent(new Intent(Intent.ACTION_VIEW, uri));
   }
 
-  public void openBookSearch(String isbn) {
-    Uri uri = Uri.parse("http://books.google.com/books?vid=isbn" + isbn);
+  public final void openBookSearch(String isbn) {
+    Uri uri = Uri.parse("http://books.google." + LocaleManager.getCountryTLD() + "/books?vid=isbn" +
+        isbn);
     launchIntent(new Intent(Intent.ACTION_VIEW, uri));
   }
 
-  public void searchBookContents(String isbn) {
+  public final void searchBookContents(String isbn) {
     Intent intent = new Intent(Intents.SearchBookContents.ACTION);
     intent.setClassName(mActivity, SearchBookContentsActivity.class.getName());
     putExtra(intent, Intents.SearchBookContents.ISBN, isbn);
     launchIntent(intent);
   }
 
-  public void openURL(String url) {
+  public final void openURL(String url) {
     launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
   }
 
-  public void webSearch(String query) {
+  public final void webSearch(String query) {
     Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
     intent.putExtra("query", query);
     launchIntent(intent);
@@ -255,7 +285,15 @@ public abstract class ResultHandler {
 
   private void launchIntent(Intent intent) {
     if (intent != null) {
-      mActivity.startActivity(intent);
+      try {
+        mActivity.startActivity(intent);
+      } catch (ActivityNotFoundException e) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
+        builder.setTitle(mActivity.getString(R.string.app_name));
+        builder.setMessage(mActivity.getString(R.string.msg_intent_failed));
+        builder.setPositiveButton(R.string.button_ok, null);
+        builder.show();
+      }
     }
   }
 
@@ -265,12 +303,4 @@ public abstract class ResultHandler {
     }
   }
 
-  // TODO: The current Contacts Intent API can only accept one value for each field, so we pick the
-  // first element in the array for names, phone numbers, and emails. It would be great to fix this.
-  private static void putExtra(Intent intent, String key, String[] value) {
-    if (value != null && value.length > 0) {
-      putExtra(intent, key, value[0]);
-    }
-  }
-
 }