0df0f7aedf3250acaa073d32c4f3074ff5bf2534
[zxing.git] / androidtest / src / com / google / zxing / client / androidtest / CameraManager.java
1 /*
2  * Copyright (C) 2008 ZXing authors
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.google.zxing.client.androidtest;
18
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;
30
31 import java.io.IOException;
32
33 /**
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.
37  *
38  * @author dswitkin@google.com (Daniel Switkin)
39  */
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;
46
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;
62
63   /**
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.
66    */
67   private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
68     public void onPreviewFrame(byte[] data, Camera camera) {
69       if (!useOneShotPreviewCallback) {
70         camera.setPreviewCallback(null);
71       }
72       if (previewHandler != null) {
73         Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
74             cameraResolution.y, data);
75         message.sendToTarget();
76         previewHandler = null;
77       }
78     }
79   };
80
81   /**
82    * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
83    */
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;
92       }
93     }
94   };
95
96   /**
97    * Initializes this static object with the Context of the calling Activity.
98    *
99    * @param context The Activity which wants to use the camera.
100    */
101   public static void init(Context context) {
102     if (cameraManager == null) {
103       cameraManager = new CameraManager(context);
104     }
105   }
106
107   /**
108    * Gets the CameraManager singleton instance.
109    *
110    * @return A reference to the CameraManager singleton.
111    */
112   public static CameraManager get() {
113     return cameraManager;
114   }
115
116   private CameraManager(Context context) {
117     this.context = context;
118     camera = null;
119     initialized = false;
120     previewing = false;
121
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;
128     } else {
129       useOneShotPreviewCallback = true;
130     }
131   }
132
133   /**
134    * Opens the camera driver and initializes the hardware parameters.
135    *
136    * @param holder The surface object which the camera will draw preview frames into.
137    * @throws IOException Indicates the camera driver failed to open.
138    */
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);
144
145       if (!initialized) {
146         initialized = true;
147         getScreenResolution();
148       }
149
150       if (getParameters) {
151         result = collectCameraParameters();
152       }
153       setCameraParameters();
154     }
155     return result;
156   }
157
158   /**
159    * Closes the camera driver if still in use.
160    */
161   public void closeDriver() {
162     if (camera != null) {
163       camera.release();
164       camera = null;
165     }
166   }
167
168   /**
169    * Asks the camera hardware to begin drawing preview frames to the screen.
170    */
171   public void startPreview() {
172     if (camera != null && !previewing) {
173       camera.startPreview();
174       previewing = true;
175     }
176   }
177
178   /**
179    * Tells the camera to stop drawing preview frames.
180    */
181   public void stopPreview() {
182     if (camera != null && previewing) {
183       if (!useOneShotPreviewCallback) {
184         camera.setPreviewCallback(null);
185       }
186       camera.stopPreview();
187       previewHandler = null;
188       autoFocusHandler = null;
189       previewing = false;
190     }
191   }
192
193   /**
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,
196    * respectively.
197    *
198    * @param handler The handler to send the message to.
199    * @param message The what field of the message to be sent.
200    */
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);
207       } else {
208         camera.setPreviewCallback(previewCallback);
209       }
210     }
211   }
212
213   /**
214    * Asks the camera hardware to perform an autofocus.
215    *
216    * @param handler The Handler to notify when the autofocus completes.
217    * @param message The message to deliver.
218    */
219   public void requestAutoFocus(Handler handler, int message) {
220     if (camera != null && previewing) {
221       autoFocusHandler = handler;
222       autoFocusMessage = message;
223       camera.autoFocus(autoFocusCallback);
224     }
225   }
226
227   /**
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.
231    *
232    * @return The rectangle to draw on screen in window coordinates.
233    */
234   public Rect getFramingRect() {
235     if (framingRect == null) {
236       if (camera == null) {
237         return null;
238       }
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;
244       }
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;
250       }
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);
255     }
256     return framingRect;
257   }
258
259   /**
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.
264    */
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);
272
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);
281
282     // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
283     parameters.set("flash-value", 2);
284
285     // This is the standard setting to turn the flash off that all devices should honor.
286     parameters.set("flash-mode", "off");
287
288     camera.setParameters(parameters);
289   }
290
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);
299     }
300     result.append('\n');
301     return result.toString();
302   }
303
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());
309     }
310     return screenResolution;
311   }
312 }