Final changes for v2.7 of Barcode Scanner, including sending product lookups to the...
authordswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 1 May 2009 14:36:57 +0000 (14:36 +0000)
committerdswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Fri, 1 May 2009 14:36:57 +0000 (14:36 +0000)
git-svn-id: http://zxing.googlecode.com/svn/trunk@923 59b500cc-1b3d-0410-9834-0bbf25fbcc57

android/AndroidManifest.xml
android/assets/html/whatsnew.html
android/res/values/ids.xml
android/res/values/strings.xml
android/src/com/google/zxing/client/android/CaptureActivity.java
android/src/com/google/zxing/client/android/CaptureActivityHandler.java
android/src/com/google/zxing/client/android/LocaleManager.java
android/src/com/google/zxing/client/android/result/ResultHandler.java

index 18449af..ca5106f 100755 (executable)
@@ -20,8 +20,9 @@ version to be published. The next versionCode will be 7, regardless of whether t
 versionName is 2.31, 2.4, or 3.0. -->
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.google.zxing.client.android"
-          android:versionName="2.6"
-          android:versionCode="13">
+          android:versionName="2.7"
+          android:versionCode="18">
+  <uses-sdk android:minSdkVersion="1"/>
   <application android:icon="@drawable/launcher_icon"
                android:label="@string/app_name">
     <activity android:name=".CaptureActivity"
@@ -48,6 +49,20 @@ versionName is 2.31, 2.4, or 3.0. -->
         <category android:name="android.intent.category.BROWSABLE"/>
         <data android:scheme="http" android:host="zxing.appspot.com" android:path="/scan" />
       </intent-filter>
+      <!-- We also support a Google Product Search URL. -->
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <data android:scheme="http" android:host="www.google.com" android:path="/m/products/scan" />
+      </intent-filter>
+      <!-- And the UK version. -->
+      <intent-filter>
+        <action android:name="android.intent.action.VIEW"/>
+        <category android:name="android.intent.category.DEFAULT" />
+        <category android:name="android.intent.category.BROWSABLE"/>
+        <data android:scheme="http" android:host="www.google.co.uk" android:path="/m/products/scan" />
+      </intent-filter>
     </activity>
     <activity android:name="PreferencesActivity"
               android:label="@string/preferences_name">
index 0b39b2f..9b55eae 100644 (file)
@@ -3,14 +3,14 @@
 <body>
 <link rel="StyleSheet" href="style.css" type="text/css">
 <h3><center>What's new in this version</center></h3>
-<p>New in version 2.6:</p>
+<p>New in version 2.7:</p>
 <ul>
-  <li>Added support for ITF format barcodes.</li>
-  <li>Fixed a bug where some URLs would be misinterpreted as email addresses.</li>
-  <li>Added support for uppercase URIs like TEL: and GEO:.</li>
-  <li>Prevented Barcode Scanner from crashing when launching an unhandled intent.</li>
-  <li>Web apps can now launch Barcode Scanner by creating a hyperlink to <i>http://zxing.appspot.com/scan</i>.</li>
-  <li>Added the version to the about box.</li>
+  <li>Searching for a product online now uses the new mobile version of Google Product Search, which
+  is formatted for small screens and loads quicker.</li>
+  <li>Two fixes in QR Code version detection.</li>
+  <li>Fixed encoding QR Codes of geo locations.</li>
+  <li>You can now encode a contact as a QR Code even if it doesn't have a name, i.e. just a phone
+  number.</li>
 </ul>
 </body>
 </html>
\ No newline at end of file
index 434d93e..ea373de 100755 (executable)
@@ -22,6 +22,7 @@
   <item type="id" name="decode_succeeded"/>
   <item type="id" name="encode_failed"/>
   <item type="id" name="encode_succeeded"/>
+  <item type="id" name="launch_product_query"/>
   <item type="id" name="quit"/>
   <item type="id" name="restart_preview"/>
   <item type="id" name="return_scan_result"/>
index ac193b5..c6d06f8 100755 (executable)
@@ -79,7 +79,7 @@
   <string name="msg_share_subject_line">Here\'s the contents of a barcode I scanned</string>
 
   <string name="preferences_actions_title">When a barcode is found\u2026</string>
-  <string name="preferences_copy_to_clipboard_title">Copy contents to clipboard</string>
+  <string name="preferences_copy_to_clipboard_title">Copy to clipboard</string>
   <string name="preferences_decode_1D_title">Decode 1D barcodes</string>
   <string name="preferences_decode_QR_title">Decode QR Codes</string>
   <string name="preferences_general_title">General settings</string>
index 34d8d2d..dc220e8 100755 (executable)
@@ -37,9 +37,10 @@ import android.os.Bundle;
 import android.os.Message;
 import android.os.Vibrator;
 import android.preference.PreferenceManager;
-import android.text.SpannableStringBuilder;
 import android.text.ClipboardManager;
+import android.text.SpannableStringBuilder;
 import android.text.style.UnderlineSpan;
+import android.util.Log;
 import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.Menu;
@@ -53,7 +54,6 @@ import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.util.Log;
 import com.google.zxing.Result;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.client.android.result.ResultButtonListener;
@@ -81,6 +81,16 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
   private static final long VIBRATE_DURATION = 200;
 
   private static final String PACKAGE_NAME = "com.google.zxing.client.android";
+  private static final String PRODUCT_SEARCH_URL_PREFIX = "http://www.google";
+  private static final String PRODUCT_SEARCH_URL_SUFFIX = "/m/products/scan";
+  private static final String ZXING_URL = "http://zxing.appspot.com/scan";
+
+  private enum Source {
+    NATIVE_APP_INTENT,
+    PRODUCT_SEARCH_LINK,
+    ZXING_LINK,
+    NONE
+  }
 
   public CaptureActivityHandler mHandler;
 
@@ -93,9 +103,10 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
   private boolean mPlayBeep;
   private boolean mVibrate;
   private boolean mCopyToClipboard;
-  private boolean mScanIntent;
+  private Source mSource;
+  private String mSourceUrl;
   private String mDecodeMode;
-  private String versionName;
+  private String mVersionName;
 
   private final OnCompletionListener mBeepListener = new BeepListener();
 
@@ -137,13 +148,35 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
 
     Intent intent = getIntent();
     String action = intent.getAction();
-    if (intent != null && action != null && (action.equals(Intents.Scan.ACTION) ||
-        action.equals(Intents.Scan.DEPRECATED_ACTION))) {
-      mScanIntent = true;
-      mDecodeMode = intent.getStringExtra(Intents.Scan.MODE);
-      resetStatusView();
+    String dataString = intent.getDataString();
+    if (intent != null && action != null) {
+      if (action.equals(Intents.Scan.ACTION) || action.equals(Intents.Scan.DEPRECATED_ACTION)) {
+        // Scan the formats the intent requested, and return the result to the calling activity.
+        mSource = Source.NATIVE_APP_INTENT;
+        mDecodeMode = intent.getStringExtra(Intents.Scan.MODE);
+        resetStatusView();
+      } else if (dataString != null && dataString.contains(PRODUCT_SEARCH_URL_PREFIX) &&
+          dataString.contains(PRODUCT_SEARCH_URL_PREFIX)) {
+        // Scan only products and send the result to mobile Product Search.
+        mSource = Source.PRODUCT_SEARCH_LINK;
+        mSourceUrl = dataString;
+        mDecodeMode = Intents.Scan.PRODUCT_MODE;
+        resetStatusView();
+      } else if (dataString != null && dataString.equals(ZXING_URL)) {
+        // Scan all formats and handle the results ourselves.
+        // TODO: In the future we could allow the hyperlink to include a URL to send the results to.
+        mSource = Source.ZXING_LINK;
+        mSourceUrl = dataString;
+        mDecodeMode = null;
+        resetStatusView();
+      } else {
+        // Scan all formats and handle the results ourselves (launched from Home).
+        mSource = Source.NONE;
+        mDecodeMode = null;
+        resetStatusView();
+      }
     } else {
-      mScanIntent = false;
+      mSource = Source.NONE;
       mDecodeMode = null;
       if (mLastResult == null) {
         resetStatusView();
@@ -170,11 +203,11 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
   @Override
   public boolean onKeyDown(int keyCode, KeyEvent event) {
     if (keyCode == KeyEvent.KEYCODE_BACK) {
-      if (mScanIntent) {
+      if (mSource == Source.NATIVE_APP_INTENT) {
         setResult(RESULT_CANCELED);
         finish();
         return true;
-      } else if (mLastResult != null) {
+      } else if ((mSource == Source.NONE || mSource == Source.ZXING_LINK) && mLastResult != null) {
         resetStatusView();
         mHandler.sendEmptyMessage(R.id.restart_preview);
         return true;
@@ -230,7 +263,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
       }
       case ABOUT_ID:
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
-        builder.setTitle(getString(R.string.title_about) + versionName);
+        builder.setTitle(getString(R.string.title_about) + mVersionName);
         builder.setMessage(getString(R.string.msg_about) + "\n\n" + getString(R.string.zxing_url));
         builder.setIcon(R.drawable.zxing_icon);
         builder.setPositiveButton(R.string.button_open_browser, mAboutListener);
@@ -274,60 +307,21 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
    *
    * @param rawResult The contents of the barcode.
    * @param barcode   A greyscale bitmap of the camera data which was decoded.
-   * @param duration  How long the decoding took in milliseconds.
    */
-  public void handleDecode(Result rawResult, Bitmap barcode, int duration) {
+  public void handleDecode(Result rawResult, Bitmap barcode) {
     mLastResult = rawResult;
     playBeepSoundAndVibrate();
     drawResultPoints(barcode, rawResult);
 
-    if (mScanIntent) {
-      handleDecodeForScanIntent(rawResult, barcode, duration);
-    } else {
-      mStatusView.setVisibility(View.GONE);
-      mViewfinderView.setVisibility(View.GONE);
-      mResultView.setVisibility(View.VISIBLE);
-
-      ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
-      barcodeImageView.setMaxWidth(MAX_RESULT_IMAGE_SIZE);
-      barcodeImageView.setMaxHeight(MAX_RESULT_IMAGE_SIZE);
-      barcodeImageView.setImageBitmap(barcode);
-
-      TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
-      formatTextView.setText(getString(R.string.msg_default_format) + ": " +
-          rawResult.getBarcodeFormat().toString());
-
-      ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
-      TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
-      typeTextView.setText(getString(R.string.msg_default_type) + ": " +
-          resultHandler.getType().toString());
-
-      TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
-      CharSequence title = getString(resultHandler.getDisplayTitle());
-      SpannableStringBuilder styled = new SpannableStringBuilder(title + "\n\n");
-      styled.setSpan(new UnderlineSpan(), 0, title.length(), 0);
-      CharSequence displayContents = resultHandler.getDisplayContents();
-      styled.append(displayContents);
-      contentsTextView.setText(styled);
-
-      int buttonCount = resultHandler.getButtonCount();
-      ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
-      buttonView.requestFocus();
-      for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
-        Button button = (Button) buttonView.getChildAt(x);
-        if (x < buttonCount) {
-          button.setVisibility(View.VISIBLE);
-          button.setText(resultHandler.getButtonText(x));
-          button.setOnClickListener(new ResultButtonListener(resultHandler, x));
-        } else {
-          button.setVisibility(View.GONE);
-        }
-      }
-
-      if (mCopyToClipboard) {
-        ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
-        clipboard.setText(displayContents);
-      }
+    switch (mSource) {
+      case NATIVE_APP_INTENT:
+      case PRODUCT_SEARCH_LINK:
+        handleDecodeExternally(rawResult, barcode);
+        break;
+      case ZXING_LINK:
+      case NONE:
+        handleDecodeInternally(rawResult, barcode);
+        break;
     }
   }
 
@@ -362,7 +356,56 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
     }
   }
 
-  private void handleDecodeForScanIntent(Result rawResult, Bitmap barcode, int duration) {
+  // Put up our own UI for how to handle the decoded contents.
+  private void handleDecodeInternally(Result rawResult, Bitmap barcode) {
+    mStatusView.setVisibility(View.GONE);
+    mViewfinderView.setVisibility(View.GONE);
+    mResultView.setVisibility(View.VISIBLE);
+
+    ImageView barcodeImageView = (ImageView) findViewById(R.id.barcode_image_view);
+    barcodeImageView.setMaxWidth(MAX_RESULT_IMAGE_SIZE);
+    barcodeImageView.setMaxHeight(MAX_RESULT_IMAGE_SIZE);
+    barcodeImageView.setImageBitmap(barcode);
+
+    TextView formatTextView = (TextView) findViewById(R.id.format_text_view);
+    formatTextView.setText(getString(R.string.msg_default_format) + ": " +
+        rawResult.getBarcodeFormat().toString());
+
+    ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(this, rawResult);
+    TextView typeTextView = (TextView) findViewById(R.id.type_text_view);
+    typeTextView.setText(getString(R.string.msg_default_type) + ": " +
+        resultHandler.getType().toString());
+
+    TextView contentsTextView = (TextView) findViewById(R.id.contents_text_view);
+    CharSequence title = getString(resultHandler.getDisplayTitle());
+    SpannableStringBuilder styled = new SpannableStringBuilder(title + "\n\n");
+    styled.setSpan(new UnderlineSpan(), 0, title.length(), 0);
+    CharSequence displayContents = resultHandler.getDisplayContents();
+    styled.append(displayContents);
+    contentsTextView.setText(styled);
+
+    int buttonCount = resultHandler.getButtonCount();
+    ViewGroup buttonView = (ViewGroup) findViewById(R.id.result_button_view);
+    buttonView.requestFocus();
+    for (int x = 0; x < ResultHandler.MAX_BUTTON_COUNT; x++) {
+      Button button = (Button) buttonView.getChildAt(x);
+      if (x < buttonCount) {
+        button.setVisibility(View.VISIBLE);
+        button.setText(resultHandler.getButtonText(x));
+        button.setOnClickListener(new ResultButtonListener(resultHandler, x));
+      } else {
+        button.setVisibility(View.GONE);
+      }
+    }
+
+    if (mCopyToClipboard) {
+      ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+      clipboard.setText(displayContents);
+    }
+  }
+
+  // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
+  private void handleDecodeExternally(Result rawResult, Bitmap barcode) {
     mViewfinderView.drawResultBitmap(barcode);
 
     // Since this message will only be shown for a second, just tell the user what kind of
@@ -381,14 +424,24 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
       clipboard.setText(resultHandler.getDisplayContents());
     }
 
-    // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
-    // the deprecated intent is retired.
-    Intent intent = new Intent(getIntent().getAction());
-    intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
-    intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
-    Message message = Message.obtain(mHandler, R.id.return_scan_result);
-    message.obj = intent;
-    mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
+    if (mSource == Source.NATIVE_APP_INTENT) {
+      // Hand back whatever action they requested - this can be changed to Intents.Scan.ACTION when
+      // the deprecated intent is retired.
+      Intent intent = new Intent(getIntent().getAction());
+      intent.putExtra(Intents.Scan.RESULT, rawResult.toString());
+      intent.putExtra(Intents.Scan.RESULT_FORMAT, rawResult.getBarcodeFormat().toString());
+      Message message = Message.obtain(mHandler, R.id.return_scan_result);
+      message.obj = intent;
+      mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
+    } else if (mSource == Source.PRODUCT_SEARCH_LINK) {
+      // Reformulate the URL which triggered us into a query, so that the request goes to the same
+      // TLD as the scan URL.
+      Message message = Message.obtain(mHandler, R.id.launch_product_query);
+      int end = mSourceUrl.lastIndexOf("/scan");
+      message.obj = mSourceUrl.substring(0, end) + "?q=" +
+          resultHandler.getDisplayContents().toString() + "&source=zxing";
+      mHandler.sendMessageDelayed(message, INTENT_RESULT_DURATION);
+    }
   }
 
   /**
@@ -402,7 +455,7 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal
       int currentVersion = info.versionCode;
       // Since we're paying to talk to the PackageManager anyway, it makes sense to cache the app
       // version name here for display in the about box later.
-      this.versionName = info.versionName;
+      this.mVersionName = info.versionName;
       SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
       int lastVersion = prefs.getInt(PreferencesActivity.KEY_HELP_VERSION_SHOWN, 0);
       if (currentVersion > lastVersion) {
index 34780f2..0736842 100755 (executable)
@@ -19,6 +19,7 @@ package com.google.zxing.client.android;
 import android.app.Activity;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
@@ -70,8 +71,7 @@ public final class CaptureActivityHandler extends Handler {
         mState = State.SUCCESS;
         Bundle bundle = message.getData();
         Bitmap barcode = bundle.getParcelable(DecodeThread.BARCODE_BITMAP);
-        int duration = message.arg1;
-        mActivity.handleDecode((Result) message.obj, barcode, duration);
+        mActivity.handleDecode((Result) message.obj, barcode);
         break;
       case R.id.decode_failed:
         // We're decoding as fast as possible, so when one decode fails, start another.
@@ -82,6 +82,10 @@ public final class CaptureActivityHandler extends Handler {
         mActivity.setResult(Activity.RESULT_OK, (Intent) message.obj);
         mActivity.finish();
         break;
+      case R.id.launch_product_query:
+        String url = (String) message.obj;
+        mActivity.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
+        break;
     }
   }
 
index c504be7..d30701b 100644 (file)
@@ -40,6 +40,13 @@ public final class LocaleManager {
     GOOGLE_COUNTRY_TLD.put(Locale.UK, "co.uk");
   }
 
+  // Google Product Search for mobile is available in fewer countries than web search.
+  private static final Map<Locale,String> GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD;
+  static {
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD = new HashMap<Locale,String>();
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.UK, "co.uk");
+  }
+
   private LocaleManager() {}
 
   /**
@@ -58,4 +65,20 @@ public final class LocaleManager {
     return tld;
   }
 
+  /**
+   * The same as above, but specifically for Google Product Search.
+   * @return The top-level domain to use.
+   */
+  public static String getProductSearchCountryTLD() {
+    Locale locale = Locale.getDefault();
+    if (locale == null) {
+      return DEFAULT_TLD;
+    }
+    String tld = GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.get(locale);
+    if (tld == null) {
+      return DEFAULT_TLD;
+    }
+    return tld;
+  }
+
 }
index 63f380c..5c150fe 100644 (file)
@@ -253,9 +253,10 @@ public abstract class ResultHandler {
         LocaleManager.getCountryTLD() + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
   }
 
+  // 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.getCountryTLD() + "/products?q=" +
-        upc);
+    Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD() +
+        "/m/products?q=" + upc + "&source=zxing");
     launchIntent(new Intent(Intent.ACTION_VIEW, uri));
   }