mirror of
https://github.com/zxing/zxing.git
synced 2025-03-05 20:48:51 -08:00
Issue 376: re-set camera params after first auto-focus callback to make it work on Droid. Along the way, did a very big reorganization of CameraManager since it was becoming spaghetti code
git-svn-id: https://zxing.googlecode.com/svn/trunk@1292 59b500cc-1b3d-0410-9834-0bbf25fbcc57
This commit is contained in:
parent
2568fc26e2
commit
ad1e585a23
|
@ -1,530 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2008 ZXing authors
|
|
||||||
*
|
|
||||||
* 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 com.google.zxing.ResultPoint;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.graphics.PixelFormat;
|
|
||||||
import android.graphics.Point;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.hardware.Camera;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.os.Message;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.Display;
|
|
||||||
import android.view.SurfaceHolder;
|
|
||||||
import android.view.WindowManager;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* both preview and decoding.
|
|
||||||
*
|
|
||||||
* @author dswitkin@google.com (Daniel Switkin)
|
|
||||||
*/
|
|
||||||
final class CameraManager {
|
|
||||||
|
|
||||||
private static final String TAG = CameraManager.class.getSimpleName();
|
|
||||||
|
|
||||||
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 final int TEN_DESIRED_ZOOM = 27;
|
|
||||||
private static final int DESIRED_SHARPNESS = 30;
|
|
||||||
|
|
||||||
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
|
|
||||||
|
|
||||||
private static CameraManager cameraManager;
|
|
||||||
|
|
||||||
private Camera camera;
|
|
||||||
private final Context context;
|
|
||||||
private Point screenResolution;
|
|
||||||
private Point cameraResolution;
|
|
||||||
private Rect framingRect;
|
|
||||||
private Handler previewHandler;
|
|
||||||
private int previewMessage;
|
|
||||||
private Handler autoFocusHandler;
|
|
||||||
private int autoFocusMessage;
|
|
||||||
private boolean initialized;
|
|
||||||
private boolean previewing;
|
|
||||||
private int previewFormat;
|
|
||||||
private String previewFormatString;
|
|
||||||
private final boolean useOneShotPreviewCallback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
|
|
||||||
* clear the handler so it will only receive one message.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
// Simulate continuous autofocus by sending a focus request every 1.5 seconds.
|
|
||||||
autoFocusHandler.sendMessageDelayed(message, 1500L);
|
|
||||||
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() {
|
|
||||||
return cameraManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CameraManager(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
camera = null;
|
|
||||||
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.
|
|
||||||
useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 void openDriver(SurfaceHolder holder) throws IOException {
|
|
||||||
if (camera == null) {
|
|
||||||
camera = Camera.open();
|
|
||||||
if (camera == null) {
|
|
||||||
throw new IOException();
|
|
||||||
}
|
|
||||||
camera.setPreviewDisplay(holder);
|
|
||||||
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
getScreenResolution();
|
|
||||||
}
|
|
||||||
|
|
||||||
setCameraParameters();
|
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
|
||||||
if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
|
|
||||||
FlashlightManager.enableFlashlight();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the camera driver if still in use.
|
|
||||||
*/
|
|
||||||
public void closeDriver() {
|
|
||||||
if (camera != null) {
|
|
||||||
FlashlightManager.disableFlashlight();
|
|
||||||
camera.release();
|
|
||||||
camera = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asks the camera hardware to begin drawing preview frames to the screen.
|
|
||||||
*/
|
|
||||||
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 message The what field of the message to be sent.
|
|
||||||
*/
|
|
||||||
public void requestPreviewFrame(Handler handler, int message) {
|
|
||||||
if (camera != null && previewing) {
|
|
||||||
previewHandler = handler;
|
|
||||||
previewMessage = message;
|
|
||||||
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) {
|
|
||||||
if (camera != null && previewing) {
|
|
||||||
autoFocusHandler = handler;
|
|
||||||
autoFocusMessage = message;
|
|
||||||
camera.autoFocus(autoFocusCallback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the framing rect which the UI should draw to show the user where to place the
|
|
||||||
* barcode. This target helps with alignment as well as forces the user to hold the device
|
|
||||||
* far enough away to ensure the image will be in focus.
|
|
||||||
*
|
|
||||||
* @return The rectangle to draw on screen in window coordinates.
|
|
||||||
*/
|
|
||||||
public Rect getFramingRect() {
|
|
||||||
if (framingRect == null) {
|
|
||||||
if (camera == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
int width = cameraResolution.x * 3 / 4;
|
|
||||||
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 framingRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the result points from still resolution coordinates to screen coordinates.
|
|
||||||
*
|
|
||||||
* @param points The points returned by the Reader subclass through Result.getResultPoints().
|
|
||||||
* @return An array of Points scaled to the size of the framing rect and offset appropriately
|
|
||||||
* so they can be drawn in screen coordinates.
|
|
||||||
*/
|
|
||||||
public Point[] convertResultPoints(ResultPoint[] points) {
|
|
||||||
Rect frame = getFramingRect();
|
|
||||||
int count = points.length;
|
|
||||||
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() + 0.5f);
|
|
||||||
output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory method to build the appropriate LuminanceSource object based on the format
|
|
||||||
* of the preview buffers, as described by Camera.Parameters.
|
|
||||||
*
|
|
||||||
* @param data A preview frame.
|
|
||||||
* @param width The width of the image.
|
|
||||||
* @param height The height of the image.
|
|
||||||
* @return A PlanarYUVLuminanceSource instance.
|
|
||||||
*/
|
|
||||||
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
|
|
||||||
Rect rect = getFramingRect();
|
|
||||||
switch (previewFormat) {
|
|
||||||
// This is the standard Android format which all devices are REQUIRED to support.
|
|
||||||
// In theory, it's the only one we should ever care about.
|
|
||||||
case PixelFormat.YCbCr_420_SP:
|
|
||||||
// This format has never been seen in the wild, but is compatible as we only care
|
|
||||||
// about the Y channel, so allow it.
|
|
||||||
case PixelFormat.YCbCr_422_SP:
|
|
||||||
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
|
||||||
rect.width(), rect.height());
|
|
||||||
default:
|
|
||||||
// The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
|
|
||||||
// Fortunately, it too has all the Y data up front, so we can read it.
|
|
||||||
if ("yuv420p".equals(previewFormatString)) {
|
|
||||||
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
|
||||||
rect.width(), rect.height());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Unsupported picture format: " +
|
|
||||||
previewFormat + '/' + previewFormatString);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the camera up to take preview images which are used for both preview and decoding.
|
|
||||||
* 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,
|
|
||||||
* and the planar Y can be used for barcode scanning without a copy in some cases.
|
|
||||||
*/
|
|
||||||
private void setCameraParameters() {
|
|
||||||
Camera.Parameters parameters = camera.getParameters();
|
|
||||||
previewFormat = parameters.getPreviewFormat();
|
|
||||||
previewFormatString = parameters.get("preview-format");
|
|
||||||
Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
|
|
||||||
|
|
||||||
cameraResolution = getCameraResolution(parameters);
|
|
||||||
Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
|
|
||||||
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
|
|
||||||
|
|
||||||
setFlash(parameters);
|
|
||||||
setZoom(parameters);
|
|
||||||
//setSharpness(parameters);
|
|
||||||
|
|
||||||
camera.setParameters(parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Point getScreenResolution() {
|
|
||||||
if (screenResolution == null) {
|
|
||||||
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
||||||
Display display = manager.getDefaultDisplay();
|
|
||||||
screenResolution = new Point(display.getWidth(), display.getHeight());
|
|
||||||
}
|
|
||||||
return screenResolution;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Point getCameraResolution(Camera.Parameters parameters) {
|
|
||||||
|
|
||||||
String previewSizeValueString = parameters.get("preview-size-values");
|
|
||||||
// saw this on Xperia
|
|
||||||
if (previewSizeValueString == null) {
|
|
||||||
previewSizeValueString = parameters.get("preview-size-value");
|
|
||||||
}
|
|
||||||
|
|
||||||
Point cameraResolution = null;
|
|
||||||
|
|
||||||
if (previewSizeValueString != null) {
|
|
||||||
Log.v(TAG, "preview-size parameter: " + previewSizeValueString);
|
|
||||||
cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cameraResolution == null) {
|
|
||||||
// Ensure that the camera resolution is a multiple of 8, as the screen may not be.
|
|
||||||
cameraResolution = new Point(
|
|
||||||
(screenResolution.x >> 3) << 3,
|
|
||||||
(screenResolution.y >> 3) << 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cameraResolution;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Point findBestPreviewSizeValue(String previewSizeValueString, Point screenResolution) {
|
|
||||||
int bestX = 0;
|
|
||||||
int bestY = 0;
|
|
||||||
int diff = Integer.MAX_VALUE;
|
|
||||||
for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
|
|
||||||
|
|
||||||
previewSize = previewSize.trim();
|
|
||||||
int dimPosition = previewSize.indexOf('x');
|
|
||||||
if (dimPosition < 0) {
|
|
||||||
Log.w(TAG, "Bad preview-size: " + previewSize);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newX;
|
|
||||||
int newY;
|
|
||||||
try {
|
|
||||||
newX = Integer.parseInt(previewSize.substring(0, dimPosition));
|
|
||||||
newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.w(TAG, "Bad preview-size: " + previewSize);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
|
|
||||||
if (newDiff == 0) {
|
|
||||||
bestX = newX;
|
|
||||||
bestY = newY;
|
|
||||||
break;
|
|
||||||
} else if (newDiff < diff) {
|
|
||||||
bestX = newX;
|
|
||||||
bestY = newY;
|
|
||||||
diff = newDiff;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestX > 0 && bestY > 0) {
|
|
||||||
return new Point(bestX, bestY);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int findBestMotZoomValue(String stringValues, int tenDesiredZoom) {
|
|
||||||
int tenBestValue = 0;
|
|
||||||
for (String stringValue : COMMA_PATTERN.split(stringValues)) {
|
|
||||||
stringValue = stringValue.trim();
|
|
||||||
double value;
|
|
||||||
try {
|
|
||||||
value = Double.parseDouble(stringValue);
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
return tenDesiredZoom;
|
|
||||||
}
|
|
||||||
int tenValue = (int) (10.0 * value);
|
|
||||||
if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
|
|
||||||
tenBestValue = tenValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tenBestValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setFlash(Camera.Parameters parameters) {
|
|
||||||
// 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setZoom(Camera.Parameters parameters) {
|
|
||||||
|
|
||||||
String zoomSupportedString = parameters.get("zoom-supported");
|
|
||||||
if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int tenDesiredZoom = TEN_DESIRED_ZOOM;
|
|
||||||
|
|
||||||
String maxZoomString = parameters.get("max-zoom");
|
|
||||||
if (maxZoomString != null) {
|
|
||||||
try {
|
|
||||||
int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
|
|
||||||
if (tenDesiredZoom > tenMaxZoom) {
|
|
||||||
tenDesiredZoom = tenMaxZoom;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.w(TAG, "Bad max-zoom: " + maxZoomString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
|
|
||||||
if (takingPictureZoomMaxString != null) {
|
|
||||||
try {
|
|
||||||
int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
|
|
||||||
if (tenDesiredZoom > tenMaxZoom) {
|
|
||||||
tenDesiredZoom = tenMaxZoom;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String motZoomValuesString = parameters.get("mot-zoom-values");
|
|
||||||
if (motZoomValuesString != null) {
|
|
||||||
tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
|
|
||||||
}
|
|
||||||
|
|
||||||
String motZoomStepString = parameters.get("mot-zoom-step");
|
|
||||||
if (motZoomStepString != null) {
|
|
||||||
try {
|
|
||||||
double motZoomStep = Double.parseDouble(motZoomStepString.trim());
|
|
||||||
int tenZoomStep = (int) (10.0 * motZoomStep);
|
|
||||||
if (tenZoomStep > 1) {
|
|
||||||
tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
// continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set zoom. This helps encourage the user to pull back.
|
|
||||||
// Some devices like the Behold have a zoom parameter
|
|
||||||
if (maxZoomString != null || motZoomValuesString != null) {
|
|
||||||
parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Most devices, like the Hero, appear to expose this zoom parameter.
|
|
||||||
// It takes on values like "27" which appears to mean 2.7x zoom
|
|
||||||
if (takingPictureZoomMaxString != null) {
|
|
||||||
parameters.set("taking-picture-zoom", tenDesiredZoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
private void setSharpness(Camera.Parameters parameters) {
|
|
||||||
|
|
||||||
int desiredSharpness = DESIRED_SHARPNESS;
|
|
||||||
|
|
||||||
String maxSharpnessString = parameters.get("sharpness-max");
|
|
||||||
if (maxSharpnessString != null) {
|
|
||||||
try {
|
|
||||||
int maxSharpness = Integer.parseInt(maxSharpnessString);
|
|
||||||
if (desiredSharpness > maxSharpness) {
|
|
||||||
desiredSharpness = maxSharpness;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException nfe) {
|
|
||||||
Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parameters.set("sharpness", desiredSharpness);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ package com.google.zxing.client.android;
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
import com.google.zxing.ResultPoint;
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
import com.google.zxing.client.android.history.HistoryManager;
|
import com.google.zxing.client.android.history.HistoryManager;
|
||||||
import com.google.zxing.client.android.result.ResultButtonListener;
|
import com.google.zxing.client.android.result.ResultButtonListener;
|
||||||
import com.google.zxing.client.android.result.ResultHandler;
|
import com.google.zxing.client.android.result.ResultHandler;
|
||||||
|
|
|
@ -18,6 +18,7 @@ package com.google.zxing.client.android;
|
||||||
|
|
||||||
import com.google.zxing.BarcodeFormat;
|
import com.google.zxing.BarcodeFormat;
|
||||||
import com.google.zxing.Result;
|
import com.google.zxing.Result;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
@ -35,6 +36,7 @@ import java.util.Vector;
|
||||||
* @author dswitkin@google.com (Daniel Switkin)
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
public final class CaptureActivityHandler extends Handler {
|
public final class CaptureActivityHandler extends Handler {
|
||||||
|
|
||||||
private final CaptureActivity activity;
|
private final CaptureActivity activity;
|
||||||
private final DecodeThread decodeThread;
|
private final DecodeThread decodeThread;
|
||||||
private State state;
|
private State state;
|
||||||
|
@ -122,4 +124,5 @@ public final class CaptureActivityHandler extends Handler {
|
||||||
activity.drawViewfinder();
|
activity.drawViewfinder();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ 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 com.google.zxing.ResultPointCallback;
|
import com.google.zxing.ResultPointCallback;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
import com.google.zxing.common.HybridBinarizer;
|
import com.google.zxing.common.HybridBinarizer;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
@ -141,4 +142,5 @@ final class DecodeThread extends Thread {
|
||||||
message.sendToTarget();
|
message.sendToTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ public final class PlanarYUVLuminanceSource extends LuminanceSource {
|
||||||
private final int left;
|
private final int left;
|
||||||
private final int top;
|
private final int top;
|
||||||
|
|
||||||
PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
|
public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
|
||||||
int width, int height) {
|
int width, int height) {
|
||||||
super(width, height);
|
super(width, height);
|
||||||
|
|
||||||
|
|
|
@ -31,16 +31,16 @@ import android.preference.PreferenceScreen;
|
||||||
public final class PreferencesActivity extends PreferenceActivity
|
public final class PreferencesActivity extends PreferenceActivity
|
||||||
implements OnSharedPreferenceChangeListener {
|
implements OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
static final String KEY_DECODE_1D = "preferences_decode_1D";
|
public static final String KEY_DECODE_1D = "preferences_decode_1D";
|
||||||
static final String KEY_DECODE_QR = "preferences_decode_QR";
|
public static final String KEY_DECODE_QR = "preferences_decode_QR";
|
||||||
public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
|
public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
|
||||||
|
|
||||||
static final String KEY_PLAY_BEEP = "preferences_play_beep";
|
public static final String KEY_PLAY_BEEP = "preferences_play_beep";
|
||||||
static final String KEY_VIBRATE = "preferences_vibrate";
|
public static final String KEY_VIBRATE = "preferences_vibrate";
|
||||||
static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
|
public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
|
||||||
static final String KEY_FRONT_LIGHT = "preferences_front_light";
|
public static final String KEY_FRONT_LIGHT = "preferences_front_light";
|
||||||
|
|
||||||
static final String KEY_HELP_VERSION_SHOWN = "preferences_help_version_shown";
|
public static final String KEY_HELP_VERSION_SHOWN = "preferences_help_version_shown";
|
||||||
public static final String KEY_NOT_OUR_RESULTS_SHOWN = "preferences_not_out_results_shown";
|
public static final String KEY_NOT_OUR_RESULTS_SHOWN = "preferences_not_out_results_shown";
|
||||||
|
|
||||||
private CheckBoxPreference decode1D;
|
private CheckBoxPreference decode1D;
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
package com.google.zxing.client.android;
|
package com.google.zxing.client.android;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
import com.google.zxing.client.android.camera.CameraManager;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -24,7 +27,6 @@ import android.graphics.Paint;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import com.google.zxing.ResultPoint;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -36,6 +38,7 @@ import java.util.HashSet;
|
||||||
* @author dswitkin@google.com (Daniel Switkin)
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
*/
|
*/
|
||||||
public final class ViewfinderView extends View {
|
public final class ViewfinderView extends View {
|
||||||
|
|
||||||
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
|
private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
|
||||||
private static final long ANIMATION_DELAY = 100L;
|
private static final long ANIMATION_DELAY = 100L;
|
||||||
private static final int OPAQUE = 0xFF;
|
private static final int OPAQUE = 0xFF;
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* 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.camera;
|
||||||
|
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
|
||||||
|
final class AutoFocusCallback implements Camera.AutoFocusCallback {
|
||||||
|
|
||||||
|
private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
|
||||||
|
|
||||||
|
private final CameraConfigurationManager configManager;
|
||||||
|
private boolean reinitCamera;
|
||||||
|
private Handler autoFocusHandler;
|
||||||
|
private int autoFocusMessage;
|
||||||
|
|
||||||
|
AutoFocusCallback(CameraConfigurationManager configManager) {
|
||||||
|
this.configManager = configManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
|
||||||
|
this.autoFocusHandler = autoFocusHandler;
|
||||||
|
this.autoFocusMessage = autoFocusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onAutoFocus(boolean success, Camera camera) {
|
||||||
|
if (autoFocusHandler != null) {
|
||||||
|
Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
|
||||||
|
// Simulate continuous autofocus by sending a focus request every
|
||||||
|
// AUTOFOCUS_INTERVAL_MS milliseconds.
|
||||||
|
autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
|
||||||
|
autoFocusHandler = null;
|
||||||
|
if (!reinitCamera) {
|
||||||
|
reinitCamera = true;
|
||||||
|
configManager.setDesiredCameraParameters(camera);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,269 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* 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.camera;
|
||||||
|
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
final class CameraConfigurationManager {
|
||||||
|
|
||||||
|
private static final String TAG = CameraConfigurationManager.class.getSimpleName();
|
||||||
|
|
||||||
|
private static final int TEN_DESIRED_ZOOM = 27;
|
||||||
|
private static final int DESIRED_SHARPNESS = 30;
|
||||||
|
|
||||||
|
private static final Pattern COMMA_PATTERN = Pattern.compile(",");
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private Point screenResolution;
|
||||||
|
private Point cameraResolution;
|
||||||
|
private int previewFormat;
|
||||||
|
private String previewFormatString;
|
||||||
|
|
||||||
|
CameraConfigurationManager(Context context) {
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads, one time, values from the camera that are needed by the app.
|
||||||
|
*/
|
||||||
|
void initFromCameraParameters(Camera camera) {
|
||||||
|
Camera.Parameters parameters = camera.getParameters();
|
||||||
|
previewFormat = parameters.getPreviewFormat();
|
||||||
|
previewFormatString = parameters.get("preview-format");
|
||||||
|
Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
|
||||||
|
screenResolution = getScreenResolution();
|
||||||
|
cameraResolution = getCameraResolution(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the camera up to take preview images which are used for both preview and decoding.
|
||||||
|
* 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,
|
||||||
|
* and the planar Y can be used for barcode scanning without a copy in some cases.
|
||||||
|
*/
|
||||||
|
void setDesiredCameraParameters(Camera camera) {
|
||||||
|
Camera.Parameters parameters = camera.getParameters();
|
||||||
|
Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
|
||||||
|
parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
|
||||||
|
setFlash(parameters);
|
||||||
|
setZoom(parameters);
|
||||||
|
//setSharpness(parameters);
|
||||||
|
camera.setParameters(parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point getCameraResolution() {
|
||||||
|
return cameraResolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPreviewFormat() {
|
||||||
|
return previewFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getPreviewFormatString() {
|
||||||
|
return previewFormatString;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point getScreenResolution() {
|
||||||
|
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
Display display = manager.getDefaultDisplay();
|
||||||
|
return new Point(display.getWidth(), display.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point getCameraResolution(Camera.Parameters parameters) {
|
||||||
|
|
||||||
|
String previewSizeValueString = parameters.get("preview-size-values");
|
||||||
|
// saw this on Xperia
|
||||||
|
if (previewSizeValueString == null) {
|
||||||
|
previewSizeValueString = parameters.get("preview-size-value");
|
||||||
|
}
|
||||||
|
|
||||||
|
Point cameraResolution = null;
|
||||||
|
|
||||||
|
if (previewSizeValueString != null) {
|
||||||
|
Log.v(TAG, "preview-size parameter: " + previewSizeValueString);
|
||||||
|
cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cameraResolution == null) {
|
||||||
|
// Ensure that the camera resolution is a multiple of 8, as the screen may not be.
|
||||||
|
cameraResolution = new Point(
|
||||||
|
(screenResolution.x >> 3) << 3,
|
||||||
|
(screenResolution.y >> 3) << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cameraResolution;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Point findBestPreviewSizeValue(String previewSizeValueString, Point screenResolution) {
|
||||||
|
int bestX = 0;
|
||||||
|
int bestY = 0;
|
||||||
|
int diff = Integer.MAX_VALUE;
|
||||||
|
for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
|
||||||
|
|
||||||
|
previewSize = previewSize.trim();
|
||||||
|
int dimPosition = previewSize.indexOf('x');
|
||||||
|
if (dimPosition < 0) {
|
||||||
|
Log.w(TAG, "Bad preview-size: " + previewSize);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newX;
|
||||||
|
int newY;
|
||||||
|
try {
|
||||||
|
newX = Integer.parseInt(previewSize.substring(0, dimPosition));
|
||||||
|
newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Log.w(TAG, "Bad preview-size: " + previewSize);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
|
||||||
|
if (newDiff == 0) {
|
||||||
|
bestX = newX;
|
||||||
|
bestY = newY;
|
||||||
|
break;
|
||||||
|
} else if (newDiff < diff) {
|
||||||
|
bestX = newX;
|
||||||
|
bestY = newY;
|
||||||
|
diff = newDiff;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestX > 0 && bestY > 0) {
|
||||||
|
return new Point(bestX, bestY);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findBestMotZoomValue(String stringValues, int tenDesiredZoom) {
|
||||||
|
int tenBestValue = 0;
|
||||||
|
for (String stringValue : COMMA_PATTERN.split(stringValues)) {
|
||||||
|
stringValue = stringValue.trim();
|
||||||
|
double value;
|
||||||
|
try {
|
||||||
|
value = Double.parseDouble(stringValue);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
return tenDesiredZoom;
|
||||||
|
}
|
||||||
|
int tenValue = (int) (10.0 * value);
|
||||||
|
if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
|
||||||
|
tenBestValue = tenValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return tenBestValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setFlash(Camera.Parameters parameters) {
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setZoom(Camera.Parameters parameters) {
|
||||||
|
|
||||||
|
String zoomSupportedString = parameters.get("zoom-supported");
|
||||||
|
if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tenDesiredZoom = TEN_DESIRED_ZOOM;
|
||||||
|
|
||||||
|
String maxZoomString = parameters.get("max-zoom");
|
||||||
|
if (maxZoomString != null) {
|
||||||
|
try {
|
||||||
|
int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
|
||||||
|
if (tenDesiredZoom > tenMaxZoom) {
|
||||||
|
tenDesiredZoom = tenMaxZoom;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Log.w(TAG, "Bad max-zoom: " + maxZoomString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
|
||||||
|
if (takingPictureZoomMaxString != null) {
|
||||||
|
try {
|
||||||
|
int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
|
||||||
|
if (tenDesiredZoom > tenMaxZoom) {
|
||||||
|
tenDesiredZoom = tenMaxZoom;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String motZoomValuesString = parameters.get("mot-zoom-values");
|
||||||
|
if (motZoomValuesString != null) {
|
||||||
|
tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
String motZoomStepString = parameters.get("mot-zoom-step");
|
||||||
|
if (motZoomStepString != null) {
|
||||||
|
try {
|
||||||
|
double motZoomStep = Double.parseDouble(motZoomStepString.trim());
|
||||||
|
int tenZoomStep = (int) (10.0 * motZoomStep);
|
||||||
|
if (tenZoomStep > 1) {
|
||||||
|
tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
// continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set zoom. This helps encourage the user to pull back.
|
||||||
|
// Some devices like the Behold have a zoom parameter
|
||||||
|
if (maxZoomString != null || motZoomValuesString != null) {
|
||||||
|
parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most devices, like the Hero, appear to expose this zoom parameter.
|
||||||
|
// It takes on values like "27" which appears to mean 2.7x zoom
|
||||||
|
if (takingPictureZoomMaxString != null) {
|
||||||
|
parameters.set("taking-picture-zoom", tenDesiredZoom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
private void setSharpness(Camera.Parameters parameters) {
|
||||||
|
|
||||||
|
int desiredSharpness = DESIRED_SHARPNESS;
|
||||||
|
|
||||||
|
String maxSharpnessString = parameters.get("sharpness-max");
|
||||||
|
if (maxSharpnessString != null) {
|
||||||
|
try {
|
||||||
|
int maxSharpness = Integer.parseInt(maxSharpnessString);
|
||||||
|
if (desiredSharpness > maxSharpness) {
|
||||||
|
desiredSharpness = maxSharpness;
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters.set("sharpness", desiredSharpness);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
286
android/src/com/google/zxing/client/android/camera/CameraManager.java
Executable file
286
android/src/com/google/zxing/client/android/camera/CameraManager.java
Executable file
|
@ -0,0 +1,286 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 ZXing authors
|
||||||
|
*
|
||||||
|
* 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.camera;
|
||||||
|
|
||||||
|
import com.google.zxing.ResultPoint;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import com.google.zxing.client.android.PlanarYUVLuminanceSource;
|
||||||
|
import com.google.zxing.client.android.PreferencesActivity;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* both preview and decoding.
|
||||||
|
*
|
||||||
|
* @author dswitkin@google.com (Daniel Switkin)
|
||||||
|
*/
|
||||||
|
public final class CameraManager {
|
||||||
|
|
||||||
|
private static final String TAG = CameraManager.class.getSimpleName();
|
||||||
|
|
||||||
|
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 cameraManager;
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
private final CameraConfigurationManager configManager;
|
||||||
|
private Camera camera;
|
||||||
|
private Rect framingRect;
|
||||||
|
private boolean initialized;
|
||||||
|
private boolean previewing;
|
||||||
|
private final boolean useOneShotPreviewCallback;
|
||||||
|
/**
|
||||||
|
* Preview frames are delivered here, which we pass on to the registered handler. Make sure to
|
||||||
|
* clear the handler so it will only receive one message.
|
||||||
|
*/
|
||||||
|
private final PreviewCallback previewCallback;
|
||||||
|
/** Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. */
|
||||||
|
private final AutoFocusCallback autoFocusCallback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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() {
|
||||||
|
return cameraManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CameraManager(Context context) {
|
||||||
|
|
||||||
|
this.context = context;
|
||||||
|
this.configManager = new CameraConfigurationManager(context);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
|
||||||
|
|
||||||
|
previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
|
||||||
|
autoFocusCallback = new AutoFocusCallback(configManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 void openDriver(SurfaceHolder holder) throws IOException {
|
||||||
|
if (camera == null) {
|
||||||
|
camera = Camera.open();
|
||||||
|
if (camera == null) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
camera.setPreviewDisplay(holder);
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
configManager.initFromCameraParameters(camera);
|
||||||
|
}
|
||||||
|
configManager.setDesiredCameraParameters(camera);
|
||||||
|
|
||||||
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||||
|
if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
|
||||||
|
FlashlightManager.enableFlashlight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the camera driver if still in use.
|
||||||
|
*/
|
||||||
|
public void closeDriver() {
|
||||||
|
if (camera != null) {
|
||||||
|
FlashlightManager.disableFlashlight();
|
||||||
|
camera.release();
|
||||||
|
camera = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asks the camera hardware to begin drawing preview frames to the screen.
|
||||||
|
*/
|
||||||
|
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();
|
||||||
|
previewCallback.setHandler(null, 0);
|
||||||
|
autoFocusCallback.setHandler(null, 0);
|
||||||
|
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 message The what field of the message to be sent.
|
||||||
|
*/
|
||||||
|
public void requestPreviewFrame(Handler handler, int message) {
|
||||||
|
if (camera != null && previewing) {
|
||||||
|
previewCallback.setHandler(handler, message);
|
||||||
|
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) {
|
||||||
|
if (camera != null && previewing) {
|
||||||
|
autoFocusCallback.setHandler(handler, message);
|
||||||
|
camera.autoFocus(autoFocusCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the framing rect which the UI should draw to show the user where to place the
|
||||||
|
* barcode. This target helps with alignment as well as forces the user to hold the device
|
||||||
|
* far enough away to ensure the image will be in focus.
|
||||||
|
*
|
||||||
|
* @return The rectangle to draw on screen in window coordinates.
|
||||||
|
*/
|
||||||
|
public Rect getFramingRect() {
|
||||||
|
Point cameraResolution = configManager.getCameraResolution();
|
||||||
|
if (framingRect == null) {
|
||||||
|
if (camera == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int width = cameraResolution.x * 3 / 4;
|
||||||
|
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 framingRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the result points from still resolution coordinates to screen coordinates.
|
||||||
|
*
|
||||||
|
* @param points The points returned by the Reader subclass through Result.getResultPoints().
|
||||||
|
* @return An array of Points scaled to the size of the framing rect and offset appropriately
|
||||||
|
* so they can be drawn in screen coordinates.
|
||||||
|
*/
|
||||||
|
public Point[] convertResultPoints(ResultPoint[] points) {
|
||||||
|
Rect frame = getFramingRect();
|
||||||
|
int count = points.length;
|
||||||
|
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() + 0.5f);
|
||||||
|
output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A factory method to build the appropriate LuminanceSource object based on the format
|
||||||
|
* of the preview buffers, as described by Camera.Parameters.
|
||||||
|
*
|
||||||
|
* @param data A preview frame.
|
||||||
|
* @param width The width of the image.
|
||||||
|
* @param height The height of the image.
|
||||||
|
* @return A PlanarYUVLuminanceSource instance.
|
||||||
|
*/
|
||||||
|
public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
|
||||||
|
Rect rect = getFramingRect();
|
||||||
|
int previewFormat = configManager.getPreviewFormat();
|
||||||
|
String previewFormatString = configManager.getPreviewFormatString();
|
||||||
|
switch (previewFormat) {
|
||||||
|
// This is the standard Android format which all devices are REQUIRED to support.
|
||||||
|
// In theory, it's the only one we should ever care about.
|
||||||
|
case PixelFormat.YCbCr_420_SP:
|
||||||
|
// This format has never been seen in the wild, but is compatible as we only care
|
||||||
|
// about the Y channel, so allow it.
|
||||||
|
case PixelFormat.YCbCr_422_SP:
|
||||||
|
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
||||||
|
rect.width(), rect.height());
|
||||||
|
default:
|
||||||
|
// The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
|
||||||
|
// Fortunately, it too has all the Y data up front, so we can read it.
|
||||||
|
if ("yuv420p".equals(previewFormatString)) {
|
||||||
|
return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
|
||||||
|
rect.width(), rect.height());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Unsupported picture format: " +
|
||||||
|
previewFormat + '/' + previewFormatString);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.google.zxing.client.android;
|
package com.google.zxing.client.android.camera;
|
||||||
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010 ZXing authors
|
||||||
|
*
|
||||||
|
* 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.camera;
|
||||||
|
|
||||||
|
import android.graphics.Point;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
|
||||||
|
final class PreviewCallback implements Camera.PreviewCallback {
|
||||||
|
|
||||||
|
private final CameraConfigurationManager configManager;
|
||||||
|
private final boolean useOneShotPreviewCallback;
|
||||||
|
private Handler previewHandler;
|
||||||
|
private int previewMessage;
|
||||||
|
|
||||||
|
PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
|
||||||
|
this.configManager = configManager;
|
||||||
|
this.useOneShotPreviewCallback = useOneShotPreviewCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHandler(Handler previewHandler, int previewMessage) {
|
||||||
|
this.previewHandler = previewHandler;
|
||||||
|
this.previewMessage = previewMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||||
|
Point cameraResolution = configManager.getCameraResolution();
|
||||||
|
if (!useOneShotPreviewCallback) {
|
||||||
|
camera.setPreviewCallback(null);
|
||||||
|
}
|
||||||
|
if (previewHandler != null) {
|
||||||
|
Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
|
||||||
|
cameraResolution.y, data);
|
||||||
|
message.sendToTarget();
|
||||||
|
previewHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue