mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Updated ZXing Test with all of the CameraManager fixes from Barcode Scanner, as well as making it high-dpi capable, and bumped the version to 1.12.
git-svn-id: https://zxing.googlecode.com/svn/trunk@1102 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
c47cf9752e
commit
ac2ea432c0
|
@ -16,9 +16,16 @@
|
||||||
-->
|
-->
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.google.zxing.client.androidtest"
|
package="com.google.zxing.client.androidtest"
|
||||||
android:versionName="1.11"
|
android:versionName="1.12"
|
||||||
android:versionCode="3">
|
android:versionCode="4">
|
||||||
|
<!-- We require Cupcake (Android 1.5) or later. -->
|
||||||
<uses-sdk android:minSdkVersion="3"/>
|
<uses-sdk android:minSdkVersion="3"/>
|
||||||
|
<!-- Donut-specific flags which allow us to run on large and high dpi screens. -->
|
||||||
|
<supports-screens
|
||||||
|
android:largeScreens="true"
|
||||||
|
android:normalScreens="true"
|
||||||
|
android:smallScreens="true"
|
||||||
|
android:anyDensity="true"/>
|
||||||
<application android:label="@string/app_name"
|
<application android:label="@string/app_name"
|
||||||
android:icon="@drawable/icon"
|
android:icon="@drawable/icon"
|
||||||
android:debuggable="true">
|
android:debuggable="true">
|
||||||
|
|
|
@ -20,8 +20,10 @@ import android.content.Context;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
@ -32,45 +34,119 @@ import java.io.IOException;
|
||||||
* This object wraps the Camera service object and expects to be the only one talking to it. The
|
* This object wraps the Camera service object and expects to be the only one talking to it. The
|
||||||
* implementation encapsulates the steps needed to take preview-sized images, which are used for
|
* implementation encapsulates the steps needed to take preview-sized images, which are used for
|
||||||
* both preview and decoding.
|
* both preview and decoding.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
final class CameraManager {
|
final class CameraManager {
|
||||||
|
|
||||||
private static final String TAG = "CameraManager";
|
private static final String TAG = "CameraManager";
|
||||||
|
private static final int MIN_FRAME_WIDTH = 240;
|
||||||
|
private static final int MIN_FRAME_HEIGHT = 240;
|
||||||
|
private static final int MAX_FRAME_WIDTH = 480;
|
||||||
|
private static final int MAX_FRAME_HEIGHT = 360;
|
||||||
|
|
||||||
private static CameraManager mCameraManager;
|
private static CameraManager cameraManager;
|
||||||
private Camera mCamera;
|
private Camera camera;
|
||||||
private final Context mContext;
|
private final Context context;
|
||||||
private Point mScreenResolution;
|
private Point screenResolution;
|
||||||
private Rect mFramingRect;
|
private Point cameraResolution;
|
||||||
private Handler mPreviewHandler;
|
private Rect framingRect;
|
||||||
private int mPreviewMessage;
|
private Handler previewHandler;
|
||||||
private Handler mAutoFocusHandler;
|
private int previewMessage;
|
||||||
private int mAutoFocusMessage;
|
private Handler autoFocusHandler;
|
||||||
private boolean mPreviewing;
|
private int autoFocusMessage;
|
||||||
|
private boolean initialized;
|
||||||
|
private boolean previewing;
|
||||||
|
private int previewFormat;
|
||||||
|
private String previewFormatString;
|
||||||
|
private boolean useOneShotPreviewCallback;
|
||||||
|
|
||||||
public static synchronized void init(Context context) {
|
/**
|
||||||
if (mCameraManager == null) {
|
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
|
||||||
mCameraManager = new CameraManager(context);
|
* clear the handler so it will only receive one message.
|
||||||
mCameraManager.getScreenResolution();
|
*/
|
||||||
|
private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
|
||||||
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||||
|
if (!useOneShotPreviewCallback) {
|
||||||
|
camera.setPreviewCallback(null);
|
||||||
|
}
|
||||||
|
if (previewHandler != null) {
|
||||||
|
Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
|
||||||
|
cameraResolution.y, data);
|
||||||
|
message.sendToTarget();
|
||||||
|
previewHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
|
||||||
|
*/
|
||||||
|
private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
|
||||||
|
public void onAutoFocus(boolean success, Camera camera) {
|
||||||
|
if (autoFocusHandler != null) {
|
||||||
|
Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
|
||||||
|
// Barcode Scanner needs to insert a delay here because it does continuous focus,
|
||||||
|
// but this test app does not, so send the message immediately.
|
||||||
|
message.sendToTarget();
|
||||||
|
autoFocusHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes this static object with the Context of the calling Activity.
|
||||||
|
*
|
||||||
|
* @param context The Activity which wants to use the camera.
|
||||||
|
*/
|
||||||
|
public static void init(Context context) {
|
||||||
|
if (cameraManager == null) {
|
||||||
|
cameraManager = new CameraManager(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the CameraManager singleton instance.
|
||||||
|
*
|
||||||
|
* @return A reference to the CameraManager singleton.
|
||||||
|
*/
|
||||||
public static CameraManager get() {
|
public static CameraManager get() {
|
||||||
return mCameraManager;
|
return cameraManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CameraManager(Context context) {
|
private CameraManager(Context context) {
|
||||||
mContext = context;
|
this.context = context;
|
||||||
mCamera = null;
|
camera = null;
|
||||||
mPreviewing = false;
|
initialized = false;
|
||||||
|
previewing = false;
|
||||||
|
|
||||||
|
// Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
|
||||||
|
// Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
|
||||||
|
// the more efficient one shot callback, as the older one can swamp the system and cause it
|
||||||
|
// to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
|
||||||
|
if (Integer.parseInt(Build.VERSION.SDK) <= Build.VERSION_CODES.CUPCAKE) {
|
||||||
|
useOneShotPreviewCallback = false;
|
||||||
|
} else {
|
||||||
|
useOneShotPreviewCallback = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws IOException added to accommodate Android 1.5.
|
/**
|
||||||
|
* Opens the camera driver and initializes the hardware parameters.
|
||||||
|
*
|
||||||
|
* @param holder The surface object which the camera will draw preview frames into.
|
||||||
|
* @throws IOException Indicates the camera driver failed to open.
|
||||||
|
*/
|
||||||
public String openDriver(SurfaceHolder holder, boolean getParameters) throws IOException {
|
public String openDriver(SurfaceHolder holder, boolean getParameters) throws IOException {
|
||||||
String result = null;
|
String result = null;
|
||||||
if (mCamera == null) {
|
if (camera == null) {
|
||||||
mCamera = Camera.open();
|
camera = Camera.open();
|
||||||
mCamera.setPreviewDisplay(holder);
|
camera.setPreviewDisplay(holder);
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
getScreenResolution();
|
||||||
|
}
|
||||||
|
|
||||||
if (getParameters) {
|
if (getParameters) {
|
||||||
result = collectCameraParameters();
|
result = collectCameraParameters();
|
||||||
}
|
}
|
||||||
|
@ -79,114 +155,141 @@ final class CameraManager {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the camera driver if still in use.
|
||||||
|
*/
|
||||||
public void closeDriver() {
|
public void closeDriver() {
|
||||||
if (mCamera != null) {
|
if (camera != null) {
|
||||||
mCamera.release();
|
camera.release();
|
||||||
mCamera = null;
|
camera = null;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startPreview() {
|
|
||||||
if (mCamera != null && !mPreviewing) {
|
|
||||||
mCamera.startPreview();
|
|
||||||
mPreviewing = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stopPreview() {
|
|
||||||
if (mCamera != null && mPreviewing) {
|
|
||||||
mCamera.setPreviewCallback(null);
|
|
||||||
mCamera.stopPreview();
|
|
||||||
mPreviewHandler = null;
|
|
||||||
mAutoFocusHandler = null;
|
|
||||||
mPreviewing = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A single preview frame will be returned to the handler supplied. The data will arrive as
|
* Asks the camera hardware to begin drawing preview frames to the screen.
|
||||||
* byte[] in the message.obj field, with width and height encoded as message.arg1 and
|
*/
|
||||||
* message.arg2, respectively.
|
public void startPreview() {
|
||||||
|
if (camera != null && !previewing) {
|
||||||
|
camera.startPreview();
|
||||||
|
previewing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tells the camera to stop drawing preview frames.
|
||||||
|
*/
|
||||||
|
public void stopPreview() {
|
||||||
|
if (camera != null && previewing) {
|
||||||
|
if (!useOneShotPreviewCallback) {
|
||||||
|
camera.setPreviewCallback(null);
|
||||||
|
}
|
||||||
|
camera.stopPreview();
|
||||||
|
previewHandler = null;
|
||||||
|
autoFocusHandler = null;
|
||||||
|
previewing = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
|
||||||
|
* in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
|
||||||
|
* respectively.
|
||||||
*
|
*
|
||||||
* @param handler The handler to send the message to.
|
* @param handler The handler to send the message to.
|
||||||
* @param message The what field of the message to be sent.
|
* @param message The what field of the message to be sent.
|
||||||
*/
|
*/
|
||||||
public void requestPreviewFrame(Handler handler, int message) {
|
public void requestPreviewFrame(Handler handler, int message) {
|
||||||
if (mCamera != null && mPreviewing) {
|
if (camera != null && previewing) {
|
||||||
mPreviewHandler = handler;
|
previewHandler = handler;
|
||||||
mPreviewMessage = message;
|
previewMessage = message;
|
||||||
mCamera.setPreviewCallback(previewCallback);
|
if (useOneShotPreviewCallback) {
|
||||||
|
camera.setOneShotPreviewCallback(previewCallback);
|
||||||
|
} else {
|
||||||
|
camera.setPreviewCallback(previewCallback);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the camera hardware to perform an autofocus.
|
||||||
|
*
|
||||||
|
* @param handler The Handler to notify when the autofocus completes.
|
||||||
|
* @param message The message to deliver.
|
||||||
|
*/
|
||||||
public void requestAutoFocus(Handler handler, int message) {
|
public void requestAutoFocus(Handler handler, int message) {
|
||||||
if (mCamera != null && mPreviewing) {
|
if (camera != null && previewing) {
|
||||||
mAutoFocusHandler = handler;
|
autoFocusHandler = handler;
|
||||||
mAutoFocusMessage = message;
|
autoFocusMessage = message;
|
||||||
mCamera.autoFocus(autoFocusCallback);
|
camera.autoFocus(autoFocusCallback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the framing rect which the UI should draw to show the user where to place the
|
* 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
|
* barcode. This target helps with alignment as well as forces the user to hold the device
|
||||||
* frame the shot too tightly. This target helps with alignment as well as forces the user to
|
* far enough away to ensure the image will be in focus.
|
||||||
* hold the device far enough away to ensure the image will be in focus.
|
|
||||||
*
|
*
|
||||||
* @return The rectangle to draw on screen in window coordinates.
|
* @return The rectangle to draw on screen in window coordinates.
|
||||||
*/
|
*/
|
||||||
public Rect getFramingRect() {
|
public Rect getFramingRect() {
|
||||||
if (mFramingRect == null) {
|
if (framingRect == null) {
|
||||||
int width = mScreenResolution.x * 3 / 4;
|
if (camera == null) {
|
||||||
int height = mScreenResolution.y * 3 / 4;
|
return null;
|
||||||
int leftOffset = (mScreenResolution.x - width) / 2;
|
}
|
||||||
int topOffset = (mScreenResolution.y - height) / 2;
|
int width = cameraResolution.x * 3 / 4;
|
||||||
mFramingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
|
if (width < MIN_FRAME_WIDTH) {
|
||||||
|
width = MIN_FRAME_WIDTH;
|
||||||
|
} else if (width > MAX_FRAME_WIDTH) {
|
||||||
|
width = MAX_FRAME_WIDTH;
|
||||||
|
}
|
||||||
|
int height = cameraResolution.y * 3 / 4;
|
||||||
|
if (height < MIN_FRAME_HEIGHT) {
|
||||||
|
height = MIN_FRAME_HEIGHT;
|
||||||
|
} else if (height > MAX_FRAME_HEIGHT) {
|
||||||
|
height = MAX_FRAME_HEIGHT;
|
||||||
|
}
|
||||||
|
int leftOffset = (cameraResolution.x - width) / 2;
|
||||||
|
int topOffset = (cameraResolution.y - height) / 2;
|
||||||
|
framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
|
||||||
|
Log.v(TAG, "Calculated framing rect: " + framingRect);
|
||||||
}
|
}
|
||||||
return mFramingRect;
|
return framingRect;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
|
* Sets the camera up to take preview images which are used for both preview and decoding.
|
||||||
* clear the handler so it will only receive one message.
|
* We detect the preview format here so that buildLuminanceSource() can build an appropriate
|
||||||
*/
|
* LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
|
||||||
private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
|
* and the planar Y can be used for barcode scanning without a copy in some cases.
|
||||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
|
||||||
if (mPreviewHandler != null) {
|
|
||||||
mCamera.setPreviewCallback(null);
|
|
||||||
Message message = mPreviewHandler.obtainMessage(mPreviewMessage,
|
|
||||||
mScreenResolution.x, mScreenResolution.y, data);
|
|
||||||
message.sendToTarget();
|
|
||||||
mPreviewHandler = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
|
|
||||||
public void onAutoFocus(boolean success, Camera camera) {
|
|
||||||
if (mAutoFocusHandler != null) {
|
|
||||||
Message message = mAutoFocusHandler.obtainMessage(mAutoFocusMessage, success);
|
|
||||||
// The Barcodes app needs to insert a delay here because it does continuous focus,
|
|
||||||
// but this test app does not, so send the message immediately.
|
|
||||||
message.sendToTarget();
|
|
||||||
mAutoFocusHandler = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the camera up to take preview images which are used for both preview and decoding. We're
|
|
||||||
* counting on the default YUV420 semi-planar data. If that changes in the future, we'll need to
|
|
||||||
* specify it explicitly with setPreviewFormat().
|
|
||||||
*/
|
*/
|
||||||
private void setCameraParameters() {
|
private void setCameraParameters() {
|
||||||
Camera.Parameters parameters = mCamera.getParameters();
|
Camera.Parameters parameters = camera.getParameters();
|
||||||
parameters.setPreviewSize(mScreenResolution.x, mScreenResolution.y);
|
Camera.Size size = parameters.getPreviewSize();
|
||||||
mCamera.setParameters(parameters);
|
Log.v(TAG, "Default preview size: " + size.width + ", " + size.height);
|
||||||
|
previewFormat = parameters.getPreviewFormat();
|
||||||
|
previewFormatString = parameters.get("preview-format");
|
||||||
|
Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
|
||||||
|
|
||||||
|
// Ensure that the camera resolution is a multiple of 8, as the screen may not be.
|
||||||
|
// TODO: A better solution would be to request the supported preview resolutions
|
||||||
|
// and pick the best match, but this parameter is not standardized in Cupcake.
|
||||||
|
cameraResolution = new Point();
|
||||||
|
cameraResolution.x = (screenResolution.x >> 3) << 3;
|
||||||
|
cameraResolution.y = (screenResolution.y >> 3) << 3;
|
||||||
|
Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
|
||||||
|
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
|
||||||
|
|
||||||
|
// FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
|
||||||
|
parameters.set("flash-value", 2);
|
||||||
|
|
||||||
|
// This is the standard setting to turn the flash off that all devices should honor.
|
||||||
|
parameters.set("flash-mode", "off");
|
||||||
|
|
||||||
|
camera.setParameters(parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String collectCameraParameters() {
|
private String collectCameraParameters() {
|
||||||
Camera.Parameters parameters = mCamera.getParameters();
|
Camera.Parameters parameters = camera.getParameters();
|
||||||
String[] params = parameters.flatten().split(";");
|
String[] params = parameters.flatten().split(";");
|
||||||
StringBuffer result = new StringBuffer();
|
StringBuffer result = new StringBuffer();
|
||||||
result.append("Default camera parameters:");
|
result.append("Default camera parameters:");
|
||||||
|
@ -199,12 +302,11 @@ final class CameraManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point getScreenResolution() {
|
private Point getScreenResolution() {
|
||||||
if (mScreenResolution == null) {
|
if (screenResolution == null) {
|
||||||
WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
Display display = wm.getDefaultDisplay();
|
Display display = manager.getDefaultDisplay();
|
||||||
mScreenResolution = new Point(display.getWidth(), display.getHeight());
|
screenResolution = new Point(display.getWidth(), display.getHeight());
|
||||||
}
|
}
|
||||||
return mScreenResolution;
|
return screenResolution;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ public final class CameraTestActivity extends Activity implements SurfaceHolder.
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
if (mSaveThread == null && !mGetCameraParameters) {
|
if (mSaveThread == null && !mGetCameraParameters) {
|
||||||
mSaveThread = new SaveThread(this, CameraManager.get().getFramingRect());
|
mSaveThread = new SaveThread(this);
|
||||||
mSaveThread.start();
|
mSaveThread.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,11 +37,9 @@ final class SaveThread extends Thread {
|
||||||
public Handler mHandler;
|
public Handler mHandler;
|
||||||
|
|
||||||
private final CameraTestActivity mActivity;
|
private final CameraTestActivity mActivity;
|
||||||
private final Rect mFramingRect;
|
|
||||||
|
|
||||||
SaveThread(CameraTestActivity activity, Rect framingRect) {
|
SaveThread(CameraTestActivity activity) {
|
||||||
mActivity = activity;
|
mActivity = activity;
|
||||||
mFramingRect = framingRect;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -65,14 +63,15 @@ final class SaveThread extends Thread {
|
||||||
|
|
||||||
// Save the center rectangle of the Y channel as a greyscale PNG to the SD card.
|
// Save the center rectangle of the Y channel as a greyscale PNG to the SD card.
|
||||||
private void save(byte[] data, int width, int height) {
|
private void save(byte[] data, int width, int height) {
|
||||||
int framingWidth = mFramingRect.width();
|
final Rect framingRect = CameraManager.get().getFramingRect();
|
||||||
int framingHeight = mFramingRect.height();
|
int framingWidth = framingRect.width();
|
||||||
|
int framingHeight = framingRect.height();
|
||||||
if (framingWidth > width || framingHeight > height) {
|
if (framingWidth > width || framingHeight > height) {
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
|
|
||||||
int leftOffset = mFramingRect.left;
|
int leftOffset = framingRect.left;
|
||||||
int topOffset = mFramingRect.top;
|
int topOffset = framingRect.top;
|
||||||
int[] colors = new int[framingWidth * framingHeight];
|
int[] colors = new int[framingWidth * framingHeight];
|
||||||
|
|
||||||
for (int y = 0; y < framingHeight; y++) {
|
for (int y = 0; y < framingHeight; y++) {
|
||||||
|
|
Loading…
Reference in a new issue