Merged revisions 321,327,330,332,334,342-343,352-353,355-358,361-363,365,372 via...
authordswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Mon, 21 Apr 2008 22:44:40 +0000 (22:44 +0000)
committerdswitkin <dswitkin@59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Mon, 21 Apr 2008 22:44:40 +0000 (22:44 +0000)
https://zxing.googlecode.com/svn/trunk/android-m3

........
  r321 | srowen | 2008-03-28 09:57:55 -0700 (Fri, 28 Mar 2008) | 1 line

  Updates for 0.6; set aside a different android-m3-home variable for android-m3 build
........
  r327 | srowen | 2008-03-31 12:04:38 -0700 (Mon, 31 Mar 2008) | 1 line

  Made the RGB to luminance approximation/optimization a little faster -- one less shift
........
  r330 | dswitkin | 2008-03-31 13:55:38 -0700 (Mon, 31 Mar 2008) | 1 line

  Changed the project names for the two Android clients to disambiguate them.
........
  r332 | dswitkin | 2008-04-01 09:11:36 -0700 (Tue, 01 Apr 2008) | 1 line

  Added some debugging features to the M3 Android client. You can now press 'C' to capture a photo, 'U' to decode only UPC/1D barcodes, and 'Q' to decode only QR Codes.
........
  r334 | dswitkin | 2008-04-01 12:23:05 -0700 (Tue, 01 Apr 2008) | 1 line

  Added latency measurement in milliseconds to the Android client.
........
  r342 | dswitkin | 2008-04-03 14:13:50 -0700 (Thu, 03 Apr 2008) | 1 line

  Improved the Android M3 viewfinder display, by making the framing box larger and zooming in. Also fixed a bug when restarting the activity where the camera params were stale. Fixed handling of geo and tel URIs.
........
  r343 | srowen | 2008-04-03 15:18:16 -0700 (Thu, 03 Apr 2008) | 1 line

  Moved the "geo:" fix to the 'right' place
........
  r352 | dswitkin | 2008-04-08 08:25:13 -0700 (Tue, 08 Apr 2008) | 1 line

  Rewrote the Android M3 client to do continuous decoding, which means you no longer have to push the shutter button. Now you can just place the barcode in the viewfinder and it will display the contents as soon as it decodes them. That also means you no longer get "barcode not found" error dialogs which is a big improvement. Also made sure that capturing debug JPEGs uses unique filenames.
........
  r353 | dswitkin | 2008-04-08 09:06:13 -0700 (Tue, 08 Apr 2008) | 1 line

  Improved the CameraThread state machine, and fixed a bug where preview would not continue after capture.
........
  r355 | dswitkin | 2008-04-10 13:57:37 -0700 (Thu, 10 Apr 2008) | 1 line

  Made sure the BitmapSource subclasses do not reuse a BitArray which is too small.
........
  r356 | dswitkin | 2008-04-11 12:30:54 -0700 (Fri, 11 Apr 2008) | 1 line

  Changed ProGuard settings so it will inline methods which use private members. This eliminates tens of thousands of function calls to BitArray.get() on a typical decode, and lots of other work as well. Overall we're roughly 20% faster by using ProGuard than turning it off.
........
  r357 | dswitkin | 2008-04-11 13:25:09 -0700 (Fri, 11 Apr 2008) | 1 line

  Made the worker threads shut down synchronously to fix a race condition where the CameraManager could close the camera driver too soon.
........
  r358 | dswitkin | 2008-04-11 14:16:08 -0700 (Fri, 11 Apr 2008) | 1 line

  Added 'T' as a shortcut to toggle debug method tracing using android.os.Debug and traceview for analysis. The traces are written to /sdcard, which means the SD Card must not be mounted as a volume by USB at the time. Tracing can slow down decoding by a factor of 5 or more but gives very useful insights into where the time is going.
........
  r361 | dswitkin | 2008-04-15 15:34:49 -0700 (Tue, 15 Apr 2008) | 1 line

  A couple small improvements to the CameraManager.
........
  r362 | srowen | 2008-04-15 17:27:36 -0700 (Tue, 15 Apr 2008) | 1 line

  Small simplification of build file condition properties using else attribute
........
  r363 | srowen | 2008-04-15 17:34:13 -0700 (Tue, 15 Apr 2008) | 1 line

  Fix Issue 50, not building on Windows, by adding some small workarounds for Windows paths in build files for Android
........
  r365 | dswitkin | 2008-04-17 13:09:17 -0700 (Thu, 17 Apr 2008) | 1 line

  Converted the Android client to use a status bar on the bottom of the screen instead of dialogs for a much better, less interrupting user experience.
........
  r372 | dswitkin | 2008-04-21 11:11:02 -0700 (Mon, 21 Apr 2008) | 1 line

  Added some simple sharpening for 1D decoding which allowed a couple more blackbox images to pass. There were a few cases where the format or content is now misdetected, but since the net gain was positive I decided to make those non-fatal errors. In real world use the sharpening seems to help, and I think we can do even better with a better algorithm.
........

git-svn-id: http://zxing.googlecode.com/svn/trunk@374 59b500cc-1b3d-0410-9834-0bbf25fbcc57

12 files changed:
android/AndroidManifest.xml
android/build.xml
android/res/layout/main.xml
android/res/values/ids.xml
android/src/com/google/zxing/client/android/BarcodeReaderCaptureActivity.java
android/src/com/google/zxing/client/android/CameraManager.java
android/src/com/google/zxing/client/android/CameraThread.java [new file with mode: 0644]
android/src/com/google/zxing/client/android/DecodeThread.java [new file with mode: 0644]
android/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
android/src/com/google/zxing/client/android/ResultHandler.java
android/src/com/google/zxing/client/android/WorkerThread.java [deleted file]
android/strings.xml.template

index 3d614d8..89bc0d2 100644 (file)
@@ -17,7 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="com.google.zxing.client.android">
   <application android:icon="@drawable/icon">
-    <activity android:name=".BarcodeReaderCaptureActivity" android:label="@string/app_name">
+    <activity android:name="BarcodeReaderCaptureActivity" android:label="@string/app_name">
       <intent-filter>
         <action android:name="android.intent.action.MAIN"/>
         <category android:name="android.intent.category.LAUNCHER"/>
index c15813f..3f0dc2e 100644 (file)
@@ -62,7 +62,7 @@
 
   <target name="init">
     <tstamp/>
-    <fail message="Please set 'android-home' in build.properties">
+    <fail message="Please set 'android-m5-home' in build.properties">
       <condition>
         <not>
           <available file="${android-home}" type="dir"/>
   <target name="optimize" depends="compile" unless="debug">
     <jar basedir="${outdir-classes}" destfile="temp.jar"/>
     <java jar="${WTK-home}/bin/proguard.jar" fork="true" failonerror="true">
-     <jvmarg value="-Dmaximum.inlined.code.length=32"/>
-     <arg value="-injars temp.jar"/>
-     <arg value="-outjars optimized.jar"/>
-     <arg value="-libraryjars ${android-jar}"/>
-     <arg value="-dontpreverify"/>
-     <arg value="-dontobfuscate"/>
-     <arg value="-keep public class com.google.zxing.client.android.BarcodeReaderCaptureActivity"/>
-     <arg value="-optimizationpasses 7"/>
-     <arg value="-overloadaggressively"/>
-     <arg value="-verbose"/>
-   </java>
-   <delete file="temp.jar"/>
-   <delete dir="${outdir-classes}"/>
-   <mkdir dir="${outdir-classes}"/>
-   <unzip src="optimized.jar" dest="${outdir-classes}"/>
-   <delete file="optimized.jar"/>
+      <jvmarg value="-Dmaximum.inlined.code.length=32"/>
+      <arg value="-injars temp.jar"/>
+      <arg value="-outjars optimized.jar"/>
+      <arg value="-libraryjars ${android-jar}"/>
+      <arg value="-dontpreverify"/>
+      <arg value="-dontobfuscate"/>
+      <!-- Temporary workaround to keep important stuff in the client, while inlining core. -->
+      <arg value="-keep class com.google.zxing.client.android.BarcodeReaderCaptureActivity { *; }"/>
+      <arg value="-keep class com.google.zxing.client.android.CameraManager { *; }"/>
+      <arg value="-keep class com.google.zxing.client.android.CameraThread { *; }"/>
+      <arg value="-optimizationpasses 7"/>
+      <arg value="-overloadaggressively"/>
+      <arg value="-verbose"/>
+      <!-- Needed to allow getters which refer to private members to be inlined. -->
+      <arg value="-allowaccessmodification"/>
+    </java>
+    <delete file="temp.jar"/>
+    <delete dir="${outdir-classes}"/>
+    <mkdir dir="${outdir-classes}"/>
+    <unzip src="optimized.jar" dest="${outdir-classes}"/>
+    <delete file="optimized.jar"/>
   </target>
 
   <!-- Convert this project's .class files into .dex files. -->
index a841f28..115a8d2 100644 (file)
  See the License for the specific language governing permissions and
  limitations under the License.
  -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
               android:layout_width="fill_parent"
-              android:layout_height="fill_parent"/>
+              android:layout_height="fill_parent">
 
+  <FrameLayout android:id="@+id/preview_view"
+             android:layout_width="fill_parent"
+             android:layout_height="fill_parent"/>
+
+  <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="fill_parent"
+                android:layout_height="fill_parent"
+                android:background="#00000000">
+
+    <FrameLayout
+                 android:layout_width="fill_parent"
+                 android:layout_height="fill_parent"
+                 android:layout_weight="1"
+                 android:background="#00000000"/>
+
+    <LinearLayout android:id="@+id/status_view"
+                  android:orientation="horizontal"
+                  android:layout_width="fill_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_weight="0"
+                  android:background="#55000000"
+                  android:baselineAligned="false"
+                  android:padding="4px">
+
+      <TextView android:id="@+id/status_text_view"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="left|center_vertical"
+                android:layout_weight="1"
+                android:text="@string/msg_default_status"
+                android:textColor="#ffffff"/>
+
+      <Button android:id="@+id/status_action_button"
+              android:layout_width="wrap_content"
+              android:layout_height="wrap_content"
+              android:layout_gravity="center|fill_horizontal"
+              android:visibility="gone"/>
+
+    </LinearLayout>
+
+  </LinearLayout>
+
+</FrameLayout>
index 776e729..8f10506 100644 (file)
  limitations under the License.
  -->
 <resources>
-  <item type="id" name="decoding_succeeded_message"/>
-  <item type="id" name="decoding_failed_message"/>
+  <!-- Messages IDs -->
+  <item type="id" name="preview"/>
+  <item type="id" name="decode"/>
+  <item type="id" name="save"/>
+  <item type="id" name="restart_preview"/>
+  <item type="id" name="quit"/>
+  <item type="id" name="set_decode_all_mode"/>
+  <item type="id" name="set_decode_1D_mode"/>
+  <item type="id" name="set_decode_QR_mode"/>
+  <item type="id" name="toggle_tracing"/>
+  <item type="id" name="decode_started"/>
+  <item type="id" name="decode_succeeded"/>
+  <item type="id" name="decode_failed"/>
+  <item type="id" name="save_succeeded"/>
+  <item type="id" name="save_failed"/>
+
+  <!-- Objects in the view hierarchy -->
+  <item type="id" name="preview_view"/>
+  <item type="id" name="status_action_button"/>
+  <item type="id" name="status_text_view"/>
+  <item type="id" name="status_view"/>
 </resources>
index a682c43..85b9bfd 100644 (file)
@@ -24,7 +24,11 @@ import android.os.Handler;
 import android.os.Message;
 import android.view.KeyEvent;
 import android.view.Menu;
+import android.view.View;
+import android.view.ViewGroup;
 import android.view.Window;
+import android.widget.Button;
+import android.widget.TextView;
 import com.google.zxing.Result;
 import com.google.zxing.ResultPoint;
 import com.google.zxing.client.result.ParsedReaderResult;
@@ -41,21 +45,27 @@ public final class BarcodeReaderCaptureActivity extends Activity {
 
   private CameraManager cameraManager;
   private CameraSurfaceView surfaceView;
-  private WorkerThread workerThread;
+  private CameraThread cameraThread;
+  private String lastResult;
 
   private static final int ABOUT_ID = Menu.FIRST;
+  private static final int HELP_ID = Menu.FIRST + 1;
 
   @Override
   public void onCreate(Bundle icicle) {
     super.onCreate(icicle);
     requestWindowFeature(Window.FEATURE_NO_TITLE);
 
+    setContentView(R.layout.main);
+
     cameraManager = new CameraManager(getApplication());
     surfaceView = new CameraSurfaceView(getApplication(), cameraManager);
-    setContentView(surfaceView);
-    workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler);
-    workerThread.requestPreviewLoop();
-    workerThread.start();
+    surfaceView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+        ViewGroup.LayoutParams.FILL_PARENT));
+
+    ViewGroup previewView = (ViewGroup) findViewById(R.id.preview_view);
+    previewView.addView(surfaceView);
+    cameraThread = null;
 
     // TODO re-enable this when issues with Matrix.setPolyToPoly() are resolved
     //GridSampler.setGridSampler(new AndroidGraphicsGridSampler());
@@ -64,53 +74,70 @@ public final class BarcodeReaderCaptureActivity extends Activity {
   @Override
   protected void onResume() {
     super.onResume();
+    resetStatusView();
     cameraManager.openDriver();
-    if (workerThread == null) {
-      workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler);
-      workerThread.requestPreviewLoop();
-      workerThread.start();
+    if (cameraThread == null) {
+      cameraThread = new CameraThread(this, surfaceView, cameraManager, messageHandler);
+      cameraThread.start();
     }
   }
 
   @Override
   protected void onPause() {
     super.onPause();
-    if (workerThread != null) {
-      workerThread.requestExitAndWait();
-      workerThread = null;
+    if (cameraThread != null) {
+      cameraThread.quitSynchronously();
+      cameraThread = null;
     }
     cameraManager.closeDriver();
   }
 
   @Override
   public boolean onKeyDown(int keyCode, KeyEvent event) {
-    if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
-      workerThread.requestStillAndDecode();
-      return true;
+    if (keyCode == KeyEvent.KEYCODE_A) {
+      cameraThread.setDecodeAllMode();
+    } else if (keyCode == KeyEvent.KEYCODE_C) {
+      Message save = Message.obtain(cameraThread.handler, R.id.save);
+      save.sendToTarget();
+    } else if (keyCode == KeyEvent.KEYCODE_P) {
+      cameraManager.setUsePreviewForDecode(true);
+    } else if (keyCode == KeyEvent.KEYCODE_Q) {
+      cameraThread.setDecodeQRMode();
+    } else if (keyCode == KeyEvent.KEYCODE_S) {
+      cameraManager.setUsePreviewForDecode(false);
+    } else if (keyCode == KeyEvent.KEYCODE_T) {
+      cameraThread.toggleTracing();
+    } else if (keyCode == KeyEvent.KEYCODE_U) {
+      cameraThread.setDecode1DMode();
     } else {
       return super.onKeyDown(keyCode, event);
     }
+    return true;
   }
 
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
     super.onCreateOptionsMenu(menu);
     menu.add(0, ABOUT_ID, R.string.menu_about);
+    menu.add(0, HELP_ID, R.string.menu_help);
     return true;
   }
 
   @Override
   public boolean onOptionsItemSelected(Menu.Item item) {
+    Context context = getApplication();
     switch (item.getId()) {
       case ABOUT_ID:
-        Context context = getApplication();
-        showAlert(
-          context.getString(R.string.title_about),
-          0,
+        showAlert(context.getString(R.string.title_about), 0,
           context.getString(R.string.msg_about),
           context.getString(R.string.button_ok),
           true);
         break;
+      case HELP_ID:
+        showAlert(context.getString(R.string.title_help), 0,
+            context.getString(R.string.msg_help),
+            context.getString(R.string.button_ok), true);
+        break;
     }
     return super.onOptionsItemSelected(item);
   }
@@ -119,61 +146,63 @@ public final class BarcodeReaderCaptureActivity extends Activity {
     @Override
     public void handleMessage(Message message) {
       switch (message.what) {
-        case R.id.decoding_succeeded_message:
-          handleDecode((Result) message.obj);
+        case R.id.decode_succeeded:
+          int duration = message.arg1;
+          handleDecode((Result) message.obj, duration);
           break;
-        case R.id.decoding_failed_message:
-          Context context = getApplication();
-          showAlert(
-            context.getString(R.string.title_no_barcode_detected),
-            0,
-            context.getString(R.string.msg_no_barcode_detected),
-            context.getString(R.string.button_ok),
-            true);
+        case R.id.restart_preview:
+          restartPreview();
           break;
       }
     }
   };
 
   public void restartPreview() {
-    workerThread.requestPreviewLoop();
+    Message restart = Message.obtain(cameraThread.handler, R.id.restart_preview);
+    restart.sendToTarget();
   }
 
-  private void handleDecode(Result rawResult) {
-    ResultPoint[] points = rawResult.getResultPoints();
-    if (points != null && points.length > 0) {
-      surfaceView.drawResultPoints(points);
-    }
+  private void handleDecode(Result rawResult, int duration) {
+    if (!rawResult.toString().equals(lastResult)) {
+      lastResult = rawResult.toString();
 
-    Context context = getApplication();
-    ParsedReaderResult readerResult = parseReaderResult(rawResult);
-    ResultHandler handler = new ResultHandler(this, readerResult);
-    if (handler.getIntent() != null) {
-      // Can be handled by some external app; ask if the user wants to
-      // proceed first though
-      showAlert(
-        context.getString(getDialogTitleID(readerResult.getType())),
-        0,
-        readerResult.getDisplayResult(),
-        context.getString(R.string.button_yes),
-        handler,
-        context.getString(R.string.button_no),
-        null,
-        true,
-        null
-      );
+      ResultPoint[] points = rawResult.getResultPoints();
+      if (points != null && points.length > 0) {
+        surfaceView.drawResultPoints(points);
+      }
+
+      TextView textView = (TextView) findViewById(R.id.status_text_view);
+      ParsedReaderResult readerResult = parseReaderResult(rawResult);
+      textView.setText(readerResult.getDisplayResult() + " (" + duration + " ms)");
+
+      Button actionButton = (Button) findViewById(R.id.status_action_button);
+      int buttonText = getActionButtonText(readerResult.getType());
+      if (buttonText != 0) {
+        actionButton.setVisibility(View.VISIBLE);
+        actionButton.setText(buttonText);
+        ResultHandler handler = new ResultHandler(this, readerResult);
+        actionButton.setOnClickListener(handler);
+        actionButton.requestFocus();
+      } else {
+        actionButton.setVisibility(View.GONE);
+      }
 
+      // Show the green finder patterns for one second, then restart the preview
+      Message message = Message.obtain(messageHandler, R.id.restart_preview);
+      messageHandler.sendMessageDelayed(message, 1000);
     } else {
-      // Just show information to user
-      showAlert(
-        context.getString(R.string.title_barcode_detected),
-        0,
-        readerResult.getDisplayResult(),
-        context.getString(R.string.button_ok),
-        true);
+      restartPreview();
     }
   }
 
+  private void resetStatusView() {
+    TextView textView = (TextView) findViewById(R.id.status_text_view);
+    textView.setText(R.string.msg_default_status);
+    Button actionButton = (Button) findViewById(R.id.status_action_button);
+    actionButton.setVisibility(View.GONE);
+    lastResult = "";
+  }
+
   private static ParsedReaderResult parseReaderResult(Result rawResult) {
     ParsedReaderResult readerResult = ParsedReaderResult.parseReaderResult(rawResult);
     if (readerResult.getType().equals(ParsedReaderResultType.TEXT)) {
@@ -191,25 +220,27 @@ public final class BarcodeReaderCaptureActivity extends Activity {
     return readerResult;
   }
 
-  private static int getDialogTitleID(ParsedReaderResultType type) {
+  private static int getActionButtonText(ParsedReaderResultType type) {
+    int buttonText;
     if (type.equals(ParsedReaderResultType.ADDRESSBOOK)) {
-      return R.string.title_add_contact;
+      buttonText = R.string.button_add_contact;
     } else if (type.equals(ParsedReaderResultType.URI) ||
                type.equals(ParsedReaderResultType.BOOKMARK) ||
                type.equals(ParsedReaderResultType.URLTO)) {
-      return R.string.title_open_url;
+      buttonText = R.string.button_open_browser;
     } else if (type.equals(ParsedReaderResultType.EMAIL) ||
                type.equals(ParsedReaderResultType.EMAIL_ADDRESS)) {
-      return R.string.title_compose_email;
+      buttonText = R.string.button_email;
     } else if (type.equals(ParsedReaderResultType.UPC)) {
-      return R.string.title_lookup_barcode;
+      buttonText = R.string.button_lookup_product;
     } else if (type.equals(ParsedReaderResultType.TEL)) {
-      return R.string.title_dial;
+      buttonText = R.string.button_dial;
     } else if (type.equals(ParsedReaderResultType.GEO)) {
-      return R.string.title_view_maps;
+      buttonText = R.string.button_show_map;
     } else {
-      return R.string.title_barcode_detected;
+      buttonText = 0;
     }
+    return buttonText;
   }
 
 }
\ No newline at end of file
index 6bcf748..e751f00 100644 (file)
@@ -44,41 +44,49 @@ final class CameraManager {
   private final Context context;
   private Point cameraResolution;
   private Point stillResolution;
+  private Point previewResolution;
   private int stillMultiplier;
   private Point screenResolution;
   private Rect framingRect;
-  private final Bitmap bitmap;
+  private Bitmap bitmap;
   // TODO switch back to CameraDevice later
   // private CameraDevice camera;
   private CameraSource cameraSource;
   // end TODO
   private final CameraDevice.CaptureParams params;
   private boolean previewMode;
+  private boolean usePreviewForDecode;
 
   CameraManager(Context context) {
     this.context = context;
-    calculateStillResolution();
     getScreenResolution();
-    bitmap = Bitmap.createBitmap(stillResolution.x, stillResolution.y, false);
+    calculateStillResolution();
+    calculatePreviewResolution();
+
+    usePreviewForDecode = true;
+    setUsePreviewForDecode(false);
+
     // TODO switch back to CameraDevice later
-    // camera = CameraDevice.open();
+    // camera = null;
     Bitmap fakeBitmap = BitmapFactory.decodeFile("/tmp/barcode.jpg");
     if (fakeBitmap == null) {
       throw new RuntimeException("/tmp/barcode.jpg was not found");
     }
     cameraSource = new BitmapCamera(fakeBitmap, stillResolution.x, stillResolution.y);
     // end TODO
+
     params = new CameraDevice.CaptureParams();
-    previewMode = false;
-    setPreviewMode(true);
   }
 
   public void openDriver() {
-    // TODO switch back to CameraDevice later
-    // if (camera == null) {
-    //  camera = CameraDevice.open();
-    // }
-    // end TODO
+//    TODO switch back to CameraDevice later
+//    if (camera == null) {
+//      camera = CameraDevice.open();
+//      // If we're reopening the camera, we need to reset the capture params.
+//      previewMode = false;
+//      setPreviewMode(true);
+//    }
+//    end TODO
   }
 
   public void closeDriver() {
@@ -99,7 +107,7 @@ final class CameraManager {
   }
 
   public Bitmap captureStill() {
-    setPreviewMode(false);
+    setPreviewMode(usePreviewForDecode);
     Canvas canvas = new Canvas(bitmap);
     // TODO switch back to CameraDevice later
     // camera.capture(canvas);
@@ -108,6 +116,25 @@ final class CameraManager {
     return bitmap;
   }
 
+  /**
+   * This method exists to help us evaluate how to best set up and use the camera.
+   * @param usePreview Decode at preview resolution if true, else use still resolution.
+   */
+  public void setUsePreviewForDecode(boolean usePreview) {
+    if (usePreviewForDecode != usePreview) {
+      usePreviewForDecode = usePreview;
+      if (usePreview) {
+        Log.v(TAG, "Creating bitmap at screen resolution: " + screenResolution.x + "," +
+            screenResolution.y);
+        bitmap = Bitmap.createBitmap(screenResolution.x, screenResolution.y, false);
+      } else {
+        Log.v(TAG, "Creating bitmap at still resolution: " + stillResolution.x + "," +
+            stillResolution.y);
+        bitmap = Bitmap.createBitmap(stillResolution.x, stillResolution.y, false);
+      }
+    }
+  }
+
   /**
    * Calculates the framing rect which the UI should draw to show the user where to place the
    * barcode. The actual captured image should be a bit larger than indicated because they might
@@ -118,10 +145,11 @@ final class CameraManager {
    */
   public Rect getFramingRect() {
     if (framingRect == null) {
-      int size = stillResolution.x * screenResolution.x / cameraResolution.x;
+      int size = stillResolution.x * screenResolution.x / previewResolution.x;
       int leftOffset = (screenResolution.x - size) / 2;
       int topOffset = (screenResolution.y - size) / 2;
       framingRect = new Rect(leftOffset, topOffset, leftOffset + size, topOffset + size);
+      Log.v(TAG, "Calculated framing rect: " + framingRect);
     }
     return framingRect;
   }
@@ -140,8 +168,13 @@ final class CameraManager {
     Point[] output = new Point[count];
     for (int x = 0; x < count; x++) {
       output[x] = new Point();
-      output[x].x = frame.left + (int) (points[x].getX() * frameSize / stillResolution.x + 0.5f);
-      output[x].y = frame.top + (int) (points[x].getY() * frameSize / stillResolution.y + 0.5f);
+      if (usePreviewForDecode) {
+        output[x].x = (int) (points[x].getX() + 0.5f);
+        output[x].y = (int) (points[x].getY() + 0.5f);
+      } else {
+        output[x].x = frame.left + (int) (points[x].getX() * frameSize / stillResolution.x + 0.5f);
+        output[x].y = frame.top + (int) (points[x].getY() * frameSize / stillResolution.y + 0.5f);
+      }
     }
     return output;
   }
@@ -156,18 +189,10 @@ final class CameraManager {
     if (on != previewMode) {
       if (on) {
         params.type = 1; // preview
-        if (cameraResolution.x / (float) cameraResolution.y <
-            screenResolution.x / (float) screenResolution.y) {
-          params.srcWidth = cameraResolution.x;
-          params.srcHeight = cameraResolution.x * screenResolution.y / screenResolution.x;
-          params.leftPixel = 0;
-          params.topPixel = (cameraResolution.y - params.srcHeight) / 2;
-        } else {
-          params.srcWidth = cameraResolution.y * screenResolution.x / screenResolution.y;
-          params.srcHeight = cameraResolution.y;
-          params.leftPixel = (cameraResolution.x - params.srcWidth) / 2;
-          params.topPixel = 0;
-        }
+        params.srcWidth = previewResolution.x;
+        params.srcHeight = previewResolution.y;
+        params.leftPixel = (cameraResolution.x - params.srcWidth) / 2;
+        params.topPixel = (cameraResolution.y - params.srcHeight) / 2;
         params.outputWidth = screenResolution.x;
         params.outputHeight = screenResolution.y;
         params.dataFormat = 2; // RGB565
@@ -241,6 +266,22 @@ final class CameraManager {
         " nativeResolution " + nativeResolution + " stillMultiplier " + stillMultiplier);
   }
 
+  /**
+   * The goal of the preview resolution is to show a little context around the framing rectangle
+   * which is the actual captured area in still mode.
+   */
+  private void calculatePreviewResolution() {
+    if (previewResolution == null) {
+      int previewHeight = (int) (stillResolution.x * stillMultiplier * 1.8f);
+      int previewWidth = previewHeight * screenResolution.x / screenResolution.y;
+      previewWidth = ((previewWidth + 7) >> 3) << 3;
+      if (previewWidth > cameraResolution.x) previewWidth = cameraResolution.x;
+      previewHeight = previewWidth * screenResolution.y / screenResolution.x;
+      previewResolution = new Point(previewWidth, previewHeight);
+      Log.v(TAG, "previewWidth " + previewWidth + " previewHeight " + previewHeight);
+    }
+  }
+
   // FIXME(dswitkin): These three methods have temporary constants until the new Camera API can
   // provide the real values for the current device.
   // Temporary: the camera's maximum resolution in pixels.
diff --git a/android/src/com/google/zxing/client/android/CameraThread.java b/android/src/com/google/zxing/client/android/CameraThread.java
new file mode 100644 (file)
index 0000000..b8f27a3
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+/**
+ * This thread continuously pulls preview frames from the camera and draws them to the screen. It
+ * also asks the DecodeThread to process as many images as it can keep up with, and coordinates with
+ * the main thread to display the results.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class CameraThread extends Thread {
+
+  public Handler handler;
+
+  private final CameraSurfaceView surfaceView;
+  private final Handler activityHandler;
+  private final DecodeThread decodeThread;
+  private State state;
+
+  private enum State {
+    PREVIEW,
+    DECODE,
+    SAVE,
+    DONE
+  }
+
+  CameraThread(BarcodeReaderCaptureActivity activity, CameraSurfaceView surfaceView,
+               CameraManager cameraManager, Handler activityHandler) {
+    this.surfaceView = surfaceView;
+    this.activityHandler = activityHandler;
+
+    decodeThread = new DecodeThread(activity, cameraManager);
+    decodeThread.start();
+    state = State.DONE;
+  }
+
+  @Override
+  public void run() {
+    Looper.prepare();
+    handler = new Handler() {
+      public void handleMessage(Message message) {
+        switch (message.what) {
+          case R.id.preview:
+            if (state == State.PREVIEW) {
+              surfaceView.capturePreviewAndDraw();
+            }
+            break;
+          case R.id.save:
+            state = State.SAVE;
+            Message save = Message.obtain(decodeThread.handler, R.id.save);
+            save.sendToTarget();
+            break;
+          case R.id.restart_preview:
+            restartPreviewAndDecode();
+            break;
+          case R.id.quit:
+            state = State.DONE;
+            Message quit = Message.obtain(decodeThread.handler, R.id.quit);
+            quit.sendToTarget();
+            try {
+              decodeThread.join();
+            } catch (InterruptedException e) {
+            }
+            Looper.myLooper().quit();
+            break;
+          case R.id.decode_started:
+            // Since the decoder is done with the camera, continue fetching preview frames.
+            state = State.PREVIEW;
+            break;
+          case R.id.decode_succeeded:
+            state = State.DONE;
+            // Message.copyFrom() did not work as expected, hence this workaround.
+            Message success = Message.obtain(activityHandler, R.id.decode_succeeded, message.obj);
+            success.arg1 = message.arg1;
+            success.sendToTarget();
+            break;
+          case R.id.decode_failed:
+            // We're decoding as fast as possible, so when one fails, start another.
+            startDecode();
+            break;
+          case R.id.save_succeeded:
+            // TODO: Put up a non-blocking status message
+            restartPreviewAndDecode();
+            break;
+          case R.id.save_failed:
+            // TODO: Put up a blocking error message
+            restartPreviewAndDecode();
+            break;
+        }
+
+        if (state == State.PREVIEW) {
+          Message preview = Message.obtain(handler, R.id.preview);
+          preview.sendToTarget();
+        }
+      }
+    };
+    decodeThread.setCameraThreadHandler(handler);
+
+    // Start ourselves capturing previews
+    restartPreviewAndDecode();
+    Looper.loop();
+  }
+
+  public void quitSynchronously() {
+    Message quit = Message.obtain(handler, R.id.quit);
+    quit.sendToTarget();
+    try {
+      join();
+    } catch (InterruptedException e) {
+    }
+  }
+
+  public void setDecodeAllMode() {
+    Message message = Message.obtain(decodeThread.handler, R.id.set_decode_all_mode);
+    message.sendToTarget();
+  }
+
+  public void setDecode1DMode() {
+    Message message = Message.obtain(decodeThread.handler, R.id.set_decode_1D_mode);
+    message.sendToTarget();
+  }
+
+  public void setDecodeQRMode() {
+    Message message = Message.obtain(decodeThread.handler, R.id.set_decode_QR_mode);
+    message.sendToTarget();
+  }
+
+  public void toggleTracing() {
+    Message message = Message.obtain(decodeThread.handler, R.id.toggle_tracing);
+    message.sendToTarget();
+  }
+
+  /**
+   * Start a decode if possible, but not now if the DecodeThread is in the middle of saving.
+   */
+  private void startDecode() {
+    if (state != State.SAVE) {
+      state = State.DECODE;
+      Message decode = Message.obtain(decodeThread.handler, R.id.decode);
+      decode.sendToTarget();
+    }
+  }
+
+  /**
+   * Take one preview to update the screen, then do a decode and continue previews.
+   */
+  private void restartPreviewAndDecode() {
+    state = State.PREVIEW;
+    surfaceView.capturePreviewAndDraw();
+    startDecode();
+  }
+
+}
diff --git a/android/src/com/google/zxing/client/android/DecodeThread.java b/android/src/com/google/zxing/client/android/DecodeThread.java
new file mode 100644 (file)
index 0000000..2c4b904
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android;
+
+import android.app.Application;
+import android.graphics.Bitmap;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.MonochromeBitmapSource;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * This thread does all the heavy lifting of decoding the images. It can also save images to flash
+ * for debugging purposes.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class DecodeThread extends Thread {
+
+  public Handler handler;
+
+  private final BarcodeReaderCaptureActivity activity;
+  private final CameraManager cameraManager;
+  private Hashtable<DecodeHintType, Object> hints;
+  private Handler cameraThreadHandler;
+  private int methodTraceCount;
+  private boolean tracing;
+
+  DecodeThread(BarcodeReaderCaptureActivity activity, CameraManager cameraManager) {
+    this.activity = activity;
+    this.cameraManager = cameraManager;
+    methodTraceCount = 0;
+    tracing = false;
+  }
+
+  @Override
+  public void run() {
+    Looper.prepare();
+    handler = new Handler() {
+      public void handleMessage(Message message) {
+        switch (message.what) {
+          case R.id.decode:
+            captureAndDecode();
+            break;
+          case R.id.save:
+            captureAndSave();
+            break;
+          case R.id.quit:
+            Looper.myLooper().quit();
+            break;
+          case R.id.set_decode_all_mode:
+            setDecodeAllMode();
+            break;
+          case R.id.set_decode_1D_mode:
+            setDecode1DMode();
+            break;
+          case R.id.set_decode_QR_mode:
+            setDecodeQRMode();
+            break;
+          case R.id.toggle_tracing:
+            tracing = !tracing;
+            break;
+        }
+      }
+    };
+    Looper.loop();
+  }
+
+  public void setCameraThreadHandler(Handler cameraThreadHandler) {
+    this.cameraThreadHandler = cameraThreadHandler;
+  }
+
+  private void setDecodeAllMode() {
+    hints = null;
+  }
+
+  // TODO: This is fragile in case we add new formats. It would be better to have a new enum
+  // value which represented all 1D formats.
+  private void setDecode1DMode() {
+    hints = new Hashtable<DecodeHintType, Object>(3);
+    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
+    vector.addElement(BarcodeFormat.UPC_A);
+    vector.addElement(BarcodeFormat.UPC_E);
+    vector.addElement(BarcodeFormat.EAN_13);
+    vector.addElement(BarcodeFormat.EAN_8);
+    vector.addElement(BarcodeFormat.CODE_39);
+    vector.addElement(BarcodeFormat.CODE_128);
+    hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
+  }
+
+  private void setDecodeQRMode() {
+    hints = new Hashtable<DecodeHintType, Object>(3);
+    Vector<BarcodeFormat> vector = new Vector<BarcodeFormat>();
+    vector.addElement(BarcodeFormat.QR_CODE);
+    hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
+  }
+
+  private void captureAndDecode() {
+    Date startDate = new Date();
+    Bitmap bitmap = cameraManager.captureStill();
+    // Let the CameraThread know it can resume previews while the decoding continues in parallel.
+    Message restart = Message.obtain(cameraThreadHandler, R.id.decode_started);
+    restart.sendToTarget();
+
+    if (tracing) {
+      Debug.startMethodTracing("/sdcard/ZXingDecodeThread" + methodTraceCount);
+      methodTraceCount++;
+    }
+    boolean success;
+    Result rawResult = null;
+    try {
+      MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
+      rawResult = new MultiFormatReader().decode(source, hints);
+      success = true;
+    } catch (ReaderException e) {
+      success = false;
+    }
+    if (tracing) {
+      Debug.stopMethodTracing();
+    }
+    Date endDate = new Date();
+
+    if (success) {
+      Message message = Message.obtain(cameraThreadHandler, R.id.decode_succeeded, rawResult);
+      message.arg1 = (int) (endDate.getTime() - startDate.getTime());
+      message.sendToTarget();
+    } else {
+      Message message = Message.obtain(cameraThreadHandler, R.id.decode_failed);
+      message.sendToTarget();
+    }
+  }
+
+  /**
+   * This is a debugging feature used to take photos and save them as JPEGs using the exact camera
+   * setup as in normal decoding. This is useful for building up a library of test images.
+   */
+  private void captureAndSave() {
+    Bitmap bitmap = cameraManager.captureStill();
+    OutputStream outStream = getNewPhotoOutputStream();
+    if (outStream != null) {
+      bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
+      try {
+        outStream.close();
+      } catch (IOException e) {
+      }
+      Message success = Message.obtain(cameraThreadHandler, R.id.save_succeeded);
+      success.sendToTarget();
+    } else {
+      Message failure = Message.obtain(cameraThreadHandler, R.id.save_failed);
+      failure.sendToTarget();
+    }
+  }
+
+  /**
+   * We prefer to write to the SD Card because it has more space, and is automatically mounted as a
+   * drive over USB. If it's not present, fall back to the package's private file area here:
+   *
+   * /data/data/com.google.zxing.client.android/files
+   *
+   * @return A stream which represents the new file where the photo will be saved.
+   */
+  private OutputStream getNewPhotoOutputStream() {
+    File sdcard = new File("/sdcard");
+    if (sdcard.exists()) {
+      File barcodes = new File(sdcard, "barcodes");
+      if (!barcodes.exists()) {
+        if (!barcodes.mkdir()) {
+          return null;
+        }
+      }
+      String fileName = getNewPhotoName();
+      try {
+        return new FileOutputStream(new File(barcodes, fileName));
+      } catch (FileNotFoundException e) {
+      }
+    } else {
+      Application application = activity.getApplication();
+      String fileName = getNewPhotoName();
+      try {
+        return application.openFileOutput(fileName, 0);
+      } catch (FileNotFoundException e) {
+      }
+    }
+    return null;
+  }
+
+  private String getNewPhotoName() {
+    Date now = new Date();
+    return "capture" + now.getTime() + ".jpg";
+  }
+
+}
index 7461d0a..d84a920 100755 (executable)
@@ -24,10 +24,7 @@ import com.google.zxing.common.BitArray;
 import com.google.zxing.common.BlackPointEstimator;
 
 /**
- * This object implements MonochromeBitmapSource around an Android Bitmap. Rather than capturing an
- * RGB image and calculating the grey value at each pixel, we ask the camera driver for YUV data and
- * strip out the luminance channel directly. This should be faster but provides fewer bits, i.e.
- * fewer grey levels.
+ * This object implements MonochromeBitmapSource around an Android Bitmap.
  *
  * @author dswitkin@google.com (Daniel Switkin)
  * @author srowen@google.com (Sean Owen)
@@ -61,10 +58,29 @@ final class RGBMonochromeBitmapSource implements MonochromeBitmapSource {
       row.clear();
     }
     int[] pixelRow = new int[getWidth];
-     image.getPixels(pixelRow, 0, getWidth, startX, y, getWidth, 1);
-    for (int i = 0; i < getWidth; i++) {
-      if (computeRGBLuminance(pixelRow[i]) < blackPoint) {
-        row.set(i);
+    image.getPixels(pixelRow, 0, getWidth, startX, y, getWidth, 1);
+
+    // If the current decoder calculated the blackPoint based on one row, assume we're trying to
+    // decode a 1D barcode, and apply some sharpening.
+    // TODO: We may want to add a fifth parameter to request the amount of shapening to be done.
+    if (lastMethod == BlackPointEstimationMethod.ROW_SAMPLING) {
+      int left = computeRGBLuminance(pixelRow[0]);
+      int center = computeRGBLuminance(pixelRow[1]);
+      for (int i = 1; i < getWidth - 1; i++) {
+        int right = computeRGBLuminance(pixelRow[i + 1]);
+        // Simple -1 4 -1 box filter with a weight of 2
+        int luminance = ((center << 2) - left - right) >> 1;
+        if (luminance < blackPoint) {
+          row.set(i);
+        }
+        left = center;
+        center = right;
+      }
+    } else {
+      for (int i = 0; i < getWidth; i++) {
+        if (computeRGBLuminance(pixelRow[i]) < blackPoint) {
+          row.set(i);
+        }
       }
     }
     return row;
@@ -124,6 +140,9 @@ final class RGBMonochromeBitmapSource implements MonochromeBitmapSource {
   /**
    * An optimized approximation of a more proper conversion from RGB to luminance which
    * only uses shifts. See BufferedImageMonochromeBitmapSource for an original version.
+   *
+   * @param pixel An ARGB input pixel
+   * @return An eight bit luminance value
    */
   private static int computeRGBLuminance(int pixel) {
     // Instead of multiplying by 306, 601, 117, we multiply by 256, 512, 256, so that
index fc05b37..6f9a3ab 100755 (executable)
 
 package com.google.zxing.client.android;
 
-import android.content.DialogInterface;
 import android.content.Intent;
 import android.net.Uri;
 import android.provider.Contacts;
-import com.google.zxing.client.result.AddressBookAUParsedResult;
-import com.google.zxing.client.result.AddressBookDoCoMoParsedResult;
-import com.google.zxing.client.result.BookmarkDoCoMoParsedResult;
-import com.google.zxing.client.result.EmailAddressParsedResult;
-import com.google.zxing.client.result.EmailDoCoMoParsedResult;
-import com.google.zxing.client.result.GeoParsedResult;
-import com.google.zxing.client.result.ParsedReaderResult;
-import com.google.zxing.client.result.ParsedReaderResultType;
-import com.google.zxing.client.result.TelParsedResult;
-import com.google.zxing.client.result.UPCParsedResult;
-import com.google.zxing.client.result.URIParsedResult;
-import com.google.zxing.client.result.URLTOParsedResult;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import com.google.zxing.client.result.*;
 
 /**
  * Handles the result of barcode decoding in the context of the Android platform,
- * by dispatching the proper intents and so on.
+ * by dispatching the proper intents to open other activities like GMail, Maps, etc.
  *
  * @author srowen@google.com (Sean Owen)
  * @author dswitkin@google.com (Daniel Switkin)
  */
-final class ResultHandler implements DialogInterface.OnClickListener {
+final class ResultHandler implements Button.OnClickListener {
+
+  private static final String TAG = "ResultHandler";
 
   private final Intent intent;
   private final BarcodeReaderCaptureActivity captureActivity;
@@ -101,13 +94,9 @@ final class ResultHandler implements DialogInterface.OnClickListener {
     return intent;
   }
 
-  public void onClick(DialogInterface dialogInterface, int i) {
-    if (i == DialogInterface.BUTTON1) {
-      if (intent != null) {
-        captureActivity.startActivity(intent);
-      }
-    } else {
-      captureActivity.restartPreview();
+  public void onClick(View view) {
+    if (intent != null) {
+      captureActivity.startActivity(intent);
     }
   }
 
diff --git a/android/src/com/google/zxing/client/android/WorkerThread.java b/android/src/com/google/zxing/client/android/WorkerThread.java
deleted file mode 100644 (file)
index c0f6b35..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Copyright (C) 2008 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.zxing.client.android;
-
-import android.graphics.Bitmap;
-import android.os.Handler;
-import android.os.Message;
-import com.google.zxing.MonochromeBitmapSource;
-import com.google.zxing.MultiFormatReader;
-import com.google.zxing.ReaderException;
-import com.google.zxing.Result;
-
-/**
- * This thread does all the heavy lifting, both during preview and for the final capture and
- * decoding. That leaves the main thread free to handle UI tasks.
- *
- * @author dswitkin@google.com (Daniel Switkin)
- */
-final class WorkerThread extends Thread {
-
-  private final CameraSurfaceView surfaceView;
-  private final CameraManager cameraManager;
-  private final Handler handler;
-  private final Object idleLock;
-  private State state;
-
-  private enum State {
-    IDLE,
-    PREVIEW_LOOP,
-    STILL_AND_DECODE,
-    DONE
-  }
-
-  WorkerThread(CameraSurfaceView surfaceView, CameraManager cameraManager, Handler handler) {
-    this.surfaceView = surfaceView;
-    this.cameraManager = cameraManager;
-    this.handler = handler;
-    this.idleLock = new Object();
-    state = State.IDLE;
-  }
-
-  @Override
-  public void run() {
-    while (true) {
-      switch (state) {
-        case IDLE:
-          idle();
-          break;
-        case PREVIEW_LOOP:
-          surfaceView.capturePreviewAndDraw();
-          break;
-        case STILL_AND_DECODE:
-          Bitmap bitmap = cameraManager.captureStill();
-          Result rawResult;
-          try {
-            MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
-            rawResult = new MultiFormatReader().decode(source);
-          } catch (ReaderException e) {
-            Message message = Message.obtain(handler, R.id.decoding_failed_message);
-            message.sendToTarget();
-            state = State.PREVIEW_LOOP;
-            break;
-          }
-          Message message = Message.obtain(handler, R.id.decoding_succeeded_message, rawResult);
-          message.sendToTarget();
-          state = State.IDLE;
-          break;
-        case DONE:
-          return;
-      }
-    }
-  }
-
-  public void requestPreviewLoop() {
-    state = State.PREVIEW_LOOP;
-    wakeFromIdle();
-  }
-
-  public void requestStillAndDecode() {
-    state = State.STILL_AND_DECODE;
-    wakeFromIdle();
-  }
-
-  public void requestExitAndWait() {
-    state = State.DONE;
-    wakeFromIdle();
-    try {
-      join();
-    } catch (InterruptedException e) {
-    }
-  }
-
-  private void idle() {
-    try {
-      synchronized (idleLock) {
-        idleLock.wait();
-      }
-    } catch (InterruptedException ie) {
-      // continue
-    }
-  }
-
-  private void wakeFromIdle() {
-    synchronized (idleLock) {
-      idleLock.notifyAll();
-    }
-  }
-
-}
index e9af99e..5fd6ca5 100644 (file)
  -->
 <resources>
   <string name="app_name">Barcode Reader</string>
-  <string name="button_no">No</string>
+  <string name="button_add_contact">Add Contact</string>
+  <string name="button_dial">Dial</string>
+  <string name="button_email">Email</string>
+  <string name="button_lookup_product">Lookup Product</string>
   <string name="button_ok">OK</string>
-  <string name="button_yes">Yes</string>
+  <string name="button_open_browser">Open Browser</string>
+  <string name="button_show_map">Show Map</string>
   <string name="menu_about">About...</string>
+  <string name="menu_help">Help...</string>
   <string name="msg_about">ZXing Barcode Reader v@VERSION@\nhttp://code.google.com/p/zxing</string>
-  <string name="msg_no_barcode_detected">Sorry, no barcode was found.</string>
+  <string name="msg_help">A: Decode all barcodes\nC: Capture and save a JPEG\nP: Use the preview image for decoding\nQ: Decode only QR Codes\nS: Use a still image for decoding\nT: Toggle debug method tracing\nU: Decode only UPC/1D barcodes</string>
+  <string name="msg_default_status">Place a barcode inside the viewfinder rectangle to read it.</string>
   <string name="title_about">About</string>
-  <string name="title_barcode_detected">Barcode Detected</string>
-  <string name="title_no_barcode_detected">No Barcode Detected</string>
-  <string name="title_error">Error</string>
-  <string name="title_open_url">Open Web Page?</string>
-  <string name="title_add_contact">Add Contact?</string>
-  <string name="title_compose_email">Compose E-mail?</string>
-  <string name="title_lookup_barcode">Look Up Barcode Online?</string>
-  <string name="title_dial">Dial Number?</string>
-  <string name="title_view_maps">View In Google Maps?</string>    
+  <string name="title_help">Keyboard Shortcut Help</string>
 </resources>