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.

git-svn-id: https://zxing.googlecode.com/svn/trunk@332 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
dswitkin 2008-04-01 16:11:36 +00:00
parent 4b0927626f
commit 14a200b546
2 changed files with 141 additions and 20 deletions

View file

@ -61,7 +61,7 @@ public final class BarcodeReaderCaptureActivity extends Activity {
cameraManager = new CameraManager(getApplication()); cameraManager = new CameraManager(getApplication());
surfaceView = new CameraSurfaceView(getApplication(), cameraManager); surfaceView = new CameraSurfaceView(getApplication(), cameraManager);
setContentView(surfaceView); setContentView(surfaceView);
workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler); workerThread = new WorkerThread(this, surfaceView, cameraManager, messageHandler);
workerThread.requestPreviewLoop(); workerThread.requestPreviewLoop();
workerThread.start(); workerThread.start();
@ -82,7 +82,7 @@ public final class BarcodeReaderCaptureActivity extends Activity {
super.onResume(); super.onResume();
cameraManager.openDriver(); cameraManager.openDriver();
if (workerThread == null) { if (workerThread == null) {
workerThread = new WorkerThread(surfaceView, cameraManager, messageHandler); workerThread = new WorkerThread(this, surfaceView, cameraManager, messageHandler);
workerThread.requestPreviewLoop(); workerThread.requestPreviewLoop();
workerThread.start(); workerThread.start();
} }
@ -102,10 +102,16 @@ public final class BarcodeReaderCaptureActivity extends Activity {
public boolean onKeyDown(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) { if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
workerThread.requestStillAndDecode(); workerThread.requestStillAndDecode();
return true; } else if (keyCode == KeyEvent.KEYCODE_Q) {
workerThread.requestStillAndDecodeQR();
} else if (keyCode == KeyEvent.KEYCODE_U) {
workerThread.requestStillAndDecode1D();
} else if (keyCode == KeyEvent.KEYCODE_C) {
workerThread.requestStillAndSave();
} else { } else {
return super.onKeyDown(keyCode, event); return super.onKeyDown(keyCode, event);
} }
return true;
} }
@Override @Override

View file

@ -16,14 +16,25 @@
package com.google.zxing.client.android; package com.google.zxing.client.android;
import android.app.Application;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Message;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.MonochromeBitmapSource; import com.google.zxing.MonochromeBitmapSource;
import com.google.zxing.MultiFormatReader; import com.google.zxing.MultiFormatReader;
import com.google.zxing.ReaderException; import com.google.zxing.ReaderException;
import com.google.zxing.Result; 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 * 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. * decoding. That leaves the main thread free to handle UI tasks.
@ -32,6 +43,7 @@ import com.google.zxing.Result;
*/ */
final class WorkerThread extends Thread { final class WorkerThread extends Thread {
private final BarcodeReaderCaptureActivity activity;
private final CameraSurfaceView surfaceView; private final CameraSurfaceView surfaceView;
private final CameraManager cameraManager; private final CameraManager cameraManager;
private final Handler handler; private final Handler handler;
@ -42,10 +54,15 @@ final class WorkerThread extends Thread {
IDLE, IDLE,
PREVIEW_LOOP, PREVIEW_LOOP,
STILL_AND_DECODE, STILL_AND_DECODE,
STILL_AND_DECODE_1D,
STILL_AND_DECODE_QR,
STILL_AND_SAVE,
DONE DONE
} }
WorkerThread(CameraSurfaceView surfaceView, CameraManager cameraManager, Handler handler) { WorkerThread(BarcodeReaderCaptureActivity activity, CameraSurfaceView surfaceView,
CameraManager cameraManager, Handler handler) {
this.activity = activity;
this.surfaceView = surfaceView; this.surfaceView = surfaceView;
this.cameraManager = cameraManager; this.cameraManager = cameraManager;
this.handler = handler; this.handler = handler;
@ -64,20 +81,33 @@ final class WorkerThread extends Thread {
surfaceView.capturePreviewAndDraw(); surfaceView.capturePreviewAndDraw();
break; break;
case STILL_AND_DECODE: case STILL_AND_DECODE:
Bitmap bitmap = cameraManager.captureStill(); takeStillAndDecode(null);
Result rawResult; break;
try { case STILL_AND_DECODE_1D: {
MonochromeBitmapSource source = new RGBMonochromeBitmapSource(bitmap); Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(3);
rawResult = new MultiFormatReader().decode(source); // TODO: This is fragile in case we add new formats. It would be better to have a new enum
} catch (ReaderException e) { // value which represented all 1D formats.
Message message = Message.obtain(handler, R.id.decoding_failed_message); Vector vector = new Vector();
message.sendToTarget(); vector.addElement(BarcodeFormat.UPC_A);
state = State.PREVIEW_LOOP; vector.addElement(BarcodeFormat.UPC_E);
break; vector.addElement(BarcodeFormat.EAN_13);
} vector.addElement(BarcodeFormat.EAN_8);
Message message = Message.obtain(handler, R.id.decoding_succeeded_message, rawResult); vector.addElement(BarcodeFormat.CODE_39);
message.sendToTarget(); vector.addElement(BarcodeFormat.CODE_128);
state = State.IDLE; hints.put(DecodeHintType.POSSIBLE_FORMATS, vector);
takeStillAndDecode(hints);
break;
}
case STILL_AND_DECODE_QR: {
Hashtable<DecodeHintType, Object> hints = new Hashtable<DecodeHintType, Object>(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; break;
case DONE: case DONE:
return; return;
@ -95,6 +125,21 @@ final class WorkerThread extends Thread {
wakeFromIdle(); 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() { public void requestExitAndWait() {
state = State.DONE; state = State.DONE;
wakeFromIdle(); wakeFromIdle();
@ -109,8 +154,8 @@ final class WorkerThread extends Thread {
synchronized (idleLock) { synchronized (idleLock) {
idleLock.wait(); idleLock.wait();
} }
} catch (InterruptedException ie) { } catch (InterruptedException e) {
// continue // Continue
} }
} }
@ -120,4 +165,74 @@ final class WorkerThread extends Thread {
} }
} }
private void takeStillAndDecode(Hashtable<DecodeHintType, Object> 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";
}
} }