2 * Copyright (C) 2008 ZXing authors
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.google.zxing.client.androidtest;
19 import android.content.Context;
20 import android.graphics.Point;
21 import android.graphics.Rect;
22 import android.hardware.Camera;
23 import android.os.Build;
24 import android.os.Handler;
25 import android.os.Message;
26 import android.util.Log;
27 import android.view.Display;
28 import android.view.SurfaceHolder;
29 import android.view.WindowManager;
31 import java.io.IOException;
34 * This object wraps the Camera service object and expects to be the only one talking to it. The
35 * implementation encapsulates the steps needed to take preview-sized images, which are used for
36 * both preview and decoding.
38 * @author dswitkin@google.com (Daniel Switkin)
40 final class CameraManager {
41 private static final String TAG = "CameraManager";
42 private static final int MIN_FRAME_WIDTH = 240;
43 private static final int MIN_FRAME_HEIGHT = 240;
44 private static final int MAX_FRAME_WIDTH = 480;
45 private static final int MAX_FRAME_HEIGHT = 360;
47 private static CameraManager cameraManager;
48 private Camera camera;
49 private final Context context;
50 private Point screenResolution;
51 private Point cameraResolution;
52 private Rect framingRect;
53 private Handler previewHandler;
54 private int previewMessage;
55 private Handler autoFocusHandler;
56 private int autoFocusMessage;
57 private boolean initialized;
58 private boolean previewing;
59 private int previewFormat;
60 private String previewFormatString;
61 private boolean useOneShotPreviewCallback;
64 * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
65 * clear the handler so it will only receive one message.
67 private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
68 public void onPreviewFrame(byte[] data, Camera camera) {
69 if (!useOneShotPreviewCallback) {
70 camera.setPreviewCallback(null);
72 if (previewHandler != null) {
73 Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
74 cameraResolution.y, data);
75 message.sendToTarget();
76 previewHandler = null;
82 * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
84 private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
85 public void onAutoFocus(boolean success, Camera camera) {
86 if (autoFocusHandler != null) {
87 Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
88 // Barcode Scanner needs to insert a delay here because it does continuous focus,
89 // but this test app does not, so send the message immediately.
90 message.sendToTarget();
91 autoFocusHandler = null;
97 * Initializes this static object with the Context of the calling Activity.
99 * @param context The Activity which wants to use the camera.
101 public static void init(Context context) {
102 if (cameraManager == null) {
103 cameraManager = new CameraManager(context);
108 * Gets the CameraManager singleton instance.
110 * @return A reference to the CameraManager singleton.
112 public static CameraManager get() {
113 return cameraManager;
116 private CameraManager(Context context) {
117 this.context = context;
122 // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
123 // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
124 // the more efficient one shot callback, as the older one can swamp the system and cause it
125 // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
126 if (Integer.parseInt(Build.VERSION.SDK) <= Build.VERSION_CODES.CUPCAKE) {
127 useOneShotPreviewCallback = false;
129 useOneShotPreviewCallback = true;
134 * Opens the camera driver and initializes the hardware parameters.
136 * @param holder The surface object which the camera will draw preview frames into.
137 * @throws IOException Indicates the camera driver failed to open.
139 public String openDriver(SurfaceHolder holder, boolean getParameters) throws IOException {
140 String result = null;
141 if (camera == null) {
142 camera = Camera.open();
143 camera.setPreviewDisplay(holder);
147 getScreenResolution();
151 result = collectCameraParameters();
153 setCameraParameters();
159 * Closes the camera driver if still in use.
161 public void closeDriver() {
162 if (camera != null) {
169 * Asks the camera hardware to begin drawing preview frames to the screen.
171 public void startPreview() {
172 if (camera != null && !previewing) {
173 camera.startPreview();
179 * Tells the camera to stop drawing preview frames.
181 public void stopPreview() {
182 if (camera != null && previewing) {
183 if (!useOneShotPreviewCallback) {
184 camera.setPreviewCallback(null);
186 camera.stopPreview();
187 previewHandler = null;
188 autoFocusHandler = null;
194 * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
195 * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
198 * @param handler The handler to send the message to.
199 * @param message The what field of the message to be sent.
201 public void requestPreviewFrame(Handler handler, int message) {
202 if (camera != null && previewing) {
203 previewHandler = handler;
204 previewMessage = message;
205 if (useOneShotPreviewCallback) {
206 camera.setOneShotPreviewCallback(previewCallback);
208 camera.setPreviewCallback(previewCallback);
214 * Asks the camera hardware to perform an autofocus.
216 * @param handler The Handler to notify when the autofocus completes.
217 * @param message The message to deliver.
219 public void requestAutoFocus(Handler handler, int message) {
220 if (camera != null && previewing) {
221 autoFocusHandler = handler;
222 autoFocusMessage = message;
223 camera.autoFocus(autoFocusCallback);
228 * Calculates the framing rect which the UI should draw to show the user where to place the
229 * barcode. This target helps with alignment as well as forces the user to hold the device
230 * far enough away to ensure the image will be in focus.
232 * @return The rectangle to draw on screen in window coordinates.
234 public Rect getFramingRect() {
235 if (framingRect == null) {
236 if (camera == null) {
239 int width = cameraResolution.x * 3 / 4;
240 if (width < MIN_FRAME_WIDTH) {
241 width = MIN_FRAME_WIDTH;
242 } else if (width > MAX_FRAME_WIDTH) {
243 width = MAX_FRAME_WIDTH;
245 int height = cameraResolution.y * 3 / 4;
246 if (height < MIN_FRAME_HEIGHT) {
247 height = MIN_FRAME_HEIGHT;
248 } else if (height > MAX_FRAME_HEIGHT) {
249 height = MAX_FRAME_HEIGHT;
251 int leftOffset = (cameraResolution.x - width) / 2;
252 int topOffset = (cameraResolution.y - height) / 2;
253 framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
254 Log.v(TAG, "Calculated framing rect: " + framingRect);
260 * Sets the camera up to take preview images which are used for both preview and decoding.
261 * We detect the preview format here so that buildLuminanceSource() can build an appropriate
262 * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
263 * and the planar Y can be used for barcode scanning without a copy in some cases.
265 private void setCameraParameters() {
266 Camera.Parameters parameters = camera.getParameters();
267 Camera.Size size = parameters.getPreviewSize();
268 Log.v(TAG, "Default preview size: " + size.width + ", " + size.height);
269 previewFormat = parameters.getPreviewFormat();
270 previewFormatString = parameters.get("preview-format");
271 Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
273 // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
274 // TODO: A better solution would be to request the supported preview resolutions
275 // and pick the best match, but this parameter is not standardized in Cupcake.
276 cameraResolution = new Point();
277 cameraResolution.x = (screenResolution.x >> 3) << 3;
278 cameraResolution.y = (screenResolution.y >> 3) << 3;
279 Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
280 parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
282 // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
283 parameters.set("flash-value", 2);
285 // This is the standard setting to turn the flash off that all devices should honor.
286 parameters.set("flash-mode", "off");
288 camera.setParameters(parameters);
291 private String collectCameraParameters() {
292 Camera.Parameters parameters = camera.getParameters();
293 String[] params = parameters.flatten().split(";");
294 StringBuffer result = new StringBuffer();
295 result.append("Default camera parameters:");
296 for (String param : params) {
297 result.append("\n ");
298 result.append(param);
301 return result.toString();
304 private Point getScreenResolution() {
305 if (screenResolution == null) {
306 WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
307 Display display = manager.getDefaultDisplay();
308 screenResolution = new Point(display.getWidth(), display.getHeight());
310 return screenResolution;