diff --git a/android-m3/res/values/ids.xml b/android-m3/res/values/ids.xml
index 776e72925..be39f8eaa 100644
--- a/android-m3/res/values/ids.xml
+++ b/android-m3/res/values/ids.xml
@@ -15,6 +15,17 @@
limitations under the License.
-->
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android-m3/src/com/google/zxing/client/android/BarcodeReaderCaptureActivity.java b/android-m3/src/com/google/zxing/client/android/BarcodeReaderCaptureActivity.java
index d950630e9..78e578f20 100644
--- a/android-m3/src/com/google/zxing/client/android/BarcodeReaderCaptureActivity.java
+++ b/android-m3/src/com/google/zxing/client/android/BarcodeReaderCaptureActivity.java
@@ -32,8 +32,6 @@ import com.google.zxing.ResultPoint;
import com.google.zxing.client.result.ParsedReaderResult;
import com.google.zxing.client.result.ParsedReaderResultType;
-import java.util.Date;
-
/**
* The barcode reader activity itself. This is loosely based on the CameraPreview
* example included in the Android SDK.
@@ -45,10 +43,10 @@ public final class BarcodeReaderCaptureActivity extends Activity {
private CameraManager cameraManager;
private CameraSurfaceView surfaceView;
- private WorkerThread workerThread;
- private Date decodeStart;
+ private CameraThread cameraThread;
private static final int ABOUT_ID = Menu.FIRST;
+ private static final int HELP_ID = Menu.FIRST + 1;
@Override
public void onCreate(Bundle icicle) {
@@ -64,9 +62,8 @@ public final class BarcodeReaderCaptureActivity extends Activity {
cameraManager = new CameraManager(getApplication());
surfaceView = new CameraSurfaceView(getApplication(), cameraManager);
setContentView(surfaceView);
- workerThread = new WorkerThread(this, surfaceView, cameraManager, messageHandler);
- workerThread.requestPreviewLoop();
- workerThread.start();
+ cameraThread = new CameraThread(this, surfaceView, cameraManager, messageHandler);
+ cameraThread.start();
// TODO re-enable this when issues with Matrix.setPolyToPoly() are resolved
//GridSampler.setGridSampler(new AndroidGraphicsGridSampler());
@@ -84,36 +81,38 @@ public final class BarcodeReaderCaptureActivity extends Activity {
protected void onResume() {
super.onResume();
cameraManager.openDriver();
- if (workerThread == null) {
- workerThread = new WorkerThread(this, 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) {
+ Message quit = Message.obtain(cameraThread.handler, R.id.quit);
+ quit.sendToTarget();
+ cameraThread = null;
}
cameraManager.closeDriver();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
- decodeStart = new Date();
- workerThread.requestStillAndDecode();
- } else if (keyCode == KeyEvent.KEYCODE_Q) {
- decodeStart = new Date();
- workerThread.requestStillAndDecodeQR();
- } else if (keyCode == KeyEvent.KEYCODE_U) {
- decodeStart = new Date();
- workerThread.requestStillAndDecode1D();
+ if (keyCode == KeyEvent.KEYCODE_A) {
+ cameraThread.setDecodeAllMode();
} else if (keyCode == KeyEvent.KEYCODE_C) {
- workerThread.requestStillAndSave();
+ 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_U) {
+ cameraThread.setDecode1DMode();
} else {
return super.onKeyDown(keyCode, event);
}
@@ -124,18 +123,24 @@ public final class BarcodeReaderCaptureActivity extends Activity {
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),
context.getString(R.string.msg_about),
context.getString(R.string.button_ok), null, true, null);
break;
+ case HELP_ID:
+ showAlert(context.getString(R.string.title_help),
+ context.getString(R.string.msg_help),
+ context.getString(R.string.button_ok), null, true, null);
+ break;
}
return super.onOptionsItemSelected(item);
}
@@ -143,29 +148,22 @@ public final class BarcodeReaderCaptureActivity extends Activity {
private final Handler messageHandler = new Handler() {
@Override
public void handleMessage(Message message) {
- Date now = new Date();
- long duration = now.getTime() - decodeStart.getTime();
switch (message.what) {
- case R.id.decoding_succeeded_message:
+ case R.id.decode_succeeded:
+ int duration = message.arg1;
handleDecode((Result) message.obj, duration);
break;
- case R.id.decoding_failed_message:
- Context context = getApplication();
- String title = context.getString(R.string.title_no_barcode_detected) +
- " (" + duration + " ms)";
- showAlert(title, context.getString(R.string.msg_no_barcode_detected),
- context.getString(R.string.button_ok), null, true, null);
- break;
}
}
};
public void restartPreview() {
- workerThread.requestPreviewLoop();
+ Message restart = Message.obtain(cameraThread.handler, R.id.restart_preview);
+ restart.sendToTarget();
}
// TODO(dswitkin): These deprecated showAlert calls need to be updated.
- private void handleDecode(Result rawResult, long duration) {
+ private void handleDecode(Result rawResult, int duration) {
ResultPoint[] points = rawResult.getResultPoints();
if (points != null && points.length > 0) {
surfaceView.drawResultPoints(points);
diff --git a/android-m3/src/com/google/zxing/client/android/CameraManager.java b/android-m3/src/com/google/zxing/client/android/CameraManager.java
index fb40bd291..51cbb869d 100644
--- a/android-m3/src/com/google/zxing/client/android/CameraManager.java
+++ b/android-m3/src/com/google/zxing/client/android/CameraManager.java
@@ -45,17 +45,21 @@ final class CameraManager {
private int stillMultiplier;
private Point screenResolution;
private Rect framingRect;
- private final Bitmap bitmap;
+ private Bitmap bitmap;
private CameraDevice camera;
private final CameraDevice.CaptureParams params;
private boolean previewMode;
+ private boolean usePreviewForDecode;
CameraManager(Context context) {
this.context = context;
getScreenResolution();
calculateStillResolution();
calculatePreviewResolution();
- bitmap = Bitmap.createBitmap(stillResolution.x, stillResolution.y, false);
+
+ usePreviewForDecode = true;
+ setUsePreviewForDecode(false);
+
camera = CameraDevice.open();
params = new CameraDevice.CaptureParams();
previewMode = false;
@@ -84,12 +88,31 @@ final class CameraManager {
}
public Bitmap captureStill() {
- setPreviewMode(false);
+ setPreviewMode(usePreviewForDecode);
Canvas canvas = new Canvas(bitmap);
camera.capture(canvas);
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: " + screenResolution.x + "," +
+ screenResolution.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
@@ -104,6 +127,7 @@ final class CameraManager {
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;
}
@@ -122,8 +146,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;
}
diff --git a/android-m3/src/com/google/zxing/client/android/CameraThread.java b/android-m3/src/com/google/zxing/client/android/CameraThread.java
new file mode 100644
index 000000000..fa2a2acf4
--- /dev/null
+++ b/android-m3/src/com/google/zxing/client/android/CameraThread.java
@@ -0,0 +1,144 @@
+/*
+ * 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 boolean requestDecode;
+ private boolean suspendPreview;
+
+ CameraThread(BarcodeReaderCaptureActivity activity, CameraSurfaceView surfaceView,
+ CameraManager cameraManager, Handler activityHandler) {
+ this.surfaceView = surfaceView;
+ this.activityHandler = activityHandler;
+
+ decodeThread = new DecodeThread(activity, cameraManager);
+ decodeThread.start();
+ requestDecode = true;
+ suspendPreview = false;
+ }
+
+ @Override
+ public void run() {
+ Looper.prepare();
+ handler = new Handler() {
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case R.id.preview:
+ if (!suspendPreview) {
+ surfaceView.capturePreviewAndDraw();
+ }
+ break;
+ case R.id.save:
+ suspendPreview = true;
+ Message save = Message.obtain(decodeThread.handler, R.id.save);
+ save.sendToTarget();
+ break;
+ case R.id.restart_preview:
+ restartPreviewAndDecode();
+ return;
+ case R.id.quit:
+ Message quit = Message.obtain(decodeThread.handler, R.id.quit);
+ quit.sendToTarget();
+ Looper.myLooper().quit();
+ break;
+ case R.id.decode_started:
+ // Since the decoder is done with the camera, continue fetching preview frames.
+ suspendPreview = false;
+ break;
+ case R.id.decode_succeeded:
+ // 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();
+ suspendPreview = true;
+ break;
+ case R.id.decode_failed:
+ // We're decoding as fast as possible, so when one fails, start another.
+ requestDecode = true;
+ 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();
+ return;
+ }
+
+ if (requestDecode) {
+ requestDecode = false;
+ suspendPreview = true;
+ Message decode = Message.obtain(decodeThread.handler, R.id.decode);
+ decode.sendToTarget();
+ } else if (!suspendPreview) {
+ Message preview = Message.obtain(handler, R.id.preview);
+ preview.sendToTarget();
+ }
+ }
+ };
+ decodeThread.setCameraThreadHandler(handler);
+
+ // Start ourselves capturing previews
+ Message preview = Message.obtain(handler, R.id.preview);
+ preview.sendToTarget();
+ Looper.loop();
+ }
+
+ 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();
+ }
+
+ /**
+ * Take one preview to update the screen, then do a decode and continue previews.
+ */
+ private void restartPreviewAndDecode() {
+ requestDecode = true;
+ suspendPreview = false;
+ Message preview = Message.obtain(handler, R.id.preview);
+ preview.sendToTarget();
+ }
+
+}
diff --git a/android-m3/src/com/google/zxing/client/android/DecodeThread.java b/android-m3/src/com/google/zxing/client/android/DecodeThread.java
new file mode 100644
index 000000000..efdcfef28
--- /dev/null
+++ b/android-m3/src/com/google/zxing/client/android/DecodeThread.java
@@ -0,0 +1,200 @@
+/*
+ * 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.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 hints;
+ private Handler cameraThreadHandler;
+
+ DecodeThread(BarcodeReaderCaptureActivity activity, CameraManager cameraManager) {
+ this.activity = activity;
+ this.cameraManager = cameraManager;
+ }
+
+ @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;
+ }
+ }
+ };
+ 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(3);
+ Vector vector = new Vector();
+ 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(3);
+ Vector vector = new Vector();
+ 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();
+
+ Result rawResult;
+ try {
+ MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
+ rawResult = new MultiFormatReader().decode(source, hints);
+ } catch (ReaderException e) {
+ Message failure = Message.obtain(cameraThreadHandler, R.id.decode_failed);
+ failure.sendToTarget();
+ return;
+ }
+ Date endDate = new Date();
+ Message success = Message.obtain(cameraThreadHandler, R.id.decode_succeeded, rawResult);
+ success.arg1 = (int) (endDate.getTime() - startDate.getTime());
+ success.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";
+ }
+
+}
diff --git a/android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java b/android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
index 100a9d466..5e6faa057 100755
--- a/android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
+++ b/android-m3/src/com/google/zxing/client/android/RGBMonochromeBitmapSource.java
@@ -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,7 +58,7 @@ final class RGBMonochromeBitmapSource implements MonochromeBitmapSource {
row.clear();
}
int[] pixelRow = new int[getWidth];
- image.getPixels(pixelRow, 0, getWidth, startX, y, getWidth, 1);
+ image.getPixels(pixelRow, 0, getWidth, startX, y, getWidth, 1);
for (int i = 0; i < getWidth; i++) {
if (computeRGBLuminance(pixelRow[i]) < blackPoint) {
row.set(i);
@@ -124,6 +121,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
diff --git a/android-m3/src/com/google/zxing/client/android/WorkerThread.java b/android-m3/src/com/google/zxing/client/android/WorkerThread.java
deleted file mode 100644
index 012bd1c4f..000000000
--- a/android-m3/src/com/google/zxing/client/android/WorkerThread.java
+++ /dev/null
@@ -1,238 +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.app.Application;
-import android.graphics.Bitmap;
-import android.os.Handler;
-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.Hashtable;
-import java.util.Vector;
-
-/**
- * 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 BarcodeReaderCaptureActivity activity;
- 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,
- STILL_AND_DECODE_1D,
- STILL_AND_DECODE_QR,
- STILL_AND_SAVE,
- DONE
- }
-
- WorkerThread(BarcodeReaderCaptureActivity activity, CameraSurfaceView surfaceView,
- CameraManager cameraManager, Handler handler) {
- this.activity = activity;
- 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:
- takeStillAndDecode(null);
- break;
- case STILL_AND_DECODE_1D: {
- Hashtable hints = new Hashtable(3);
- // 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.
- Vector vector = new Vector();
- 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);
- takeStillAndDecode(hints);
- break;
- }
- case STILL_AND_DECODE_QR: {
- Hashtable hints = new Hashtable(3);
- Vector vector = new Vector();
- vector.addElement(BarcodeFormat.QR_CODE);
- hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
- takeStillAndDecode(hints);
- break;
- }
- case STILL_AND_SAVE:
- takeStillAndSave();
- break;
- case DONE:
- return;
- }
- }
- }
-
- public void requestPreviewLoop() {
- state = State.PREVIEW_LOOP;
- wakeFromIdle();
- }
-
- public void requestStillAndDecode() {
- state = State.STILL_AND_DECODE;
- wakeFromIdle();
- }
-
- public void requestStillAndDecode1D() {
- state = State.STILL_AND_DECODE_1D;
- wakeFromIdle();
- }
-
- public void requestStillAndDecodeQR() {
- state = State.STILL_AND_DECODE_QR;
- wakeFromIdle();
- }
-
- public void requestStillAndSave() {
- state = State.STILL_AND_SAVE;
- wakeFromIdle();
- }
-
- public void requestExitAndWait() {
- state = State.DONE;
- wakeFromIdle();
- try {
- join();
- } catch (InterruptedException e) {
- }
- }
-
- private void idle() {
- try {
- synchronized (idleLock) {
- idleLock.wait();
- }
- } catch (InterruptedException e) {
- // Continue
- }
- }
-
- private void wakeFromIdle() {
- synchronized (idleLock) {
- idleLock.notifyAll();
- }
- }
-
- private void takeStillAndDecode(Hashtable hints) {
- Bitmap bitmap = cameraManager.captureStill();
- Result rawResult;
- try {
- MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap);
- rawResult = new MultiFormatReader().decode(source, hints);
- } catch (ReaderException e) {
- Message message = Message.obtain(handler, R.id.decoding_failed_message);
- message.sendToTarget();
- state = State.PREVIEW_LOOP;
- return;
- }
- Message message = Message.obtain(handler, R.id.decoding_succeeded_message, rawResult);
- message.sendToTarget();
- state = State.IDLE;
- }
-
- /**
- * 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 takeStillAndSave() {
- Bitmap bitmap = cameraManager.captureStill();
- OutputStream outStream = getNewPhotoOutputStream();
- bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outStream);
- try {
- outStream.close();
- } catch (IOException e) {
- }
- state = State.PREVIEW_LOOP;
- }
-
- /**
- * 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(barcodes.list());
- try {
- return new FileOutputStream(new File(barcodes, fileName));
- } catch (FileNotFoundException e) {
- }
- } else {
- Application application = activity.getApplication();
- String fileName = getNewPhotoName(application.fileList());
- try {
- return application.openFileOutput(fileName, 0);
- } catch (FileNotFoundException e) {
- }
- }
- return null;
- }
-
- private String getNewPhotoName(String[] listOfFiles) {
- int existingFileCount = (listOfFiles != null) ? listOfFiles.length : 0;
- return "capture" + existingFileCount + ".jpg";
- }
-
-}
diff --git a/android-m3/strings.xml.template b/android-m3/strings.xml.template
index e9af99e21..3b5d67278 100644
--- a/android-m3/strings.xml.template
+++ b/android-m3/strings.xml.template
@@ -20,16 +20,17 @@
OK
Yes
About...
+ Help...
ZXing Barcode Reader v@VERSION@\nhttp://code.google.com/p/zxing
- Sorry, no barcode was found.
+ 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\nU: Decode only UPC/1D barcodes
About
- Barcode Detected
- No Barcode Detected
- Error
- Open Web Page?
Add Contact?
+ Barcode Detected
Compose E-mail?
- Look Up Barcode Online?
Dial Number?
- View In Google Maps?
+ Error
+ Keyboard Shortcut Help
+ Look Up Barcode Online?
+ Open Web Page?
+ View In Google Maps?