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?