Issue 376: re-set camera params after first auto-focus callback to make it work on...
[zxing.git] / android / src / com / google / zxing / client / android / camera / CameraConfigurationManager.java
1 /*
2  * Copyright (C) 2010 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.android.camera;
18
19 import java.util.regex.Pattern;
20
21 import android.content.Context;
22 import android.graphics.Point;
23 import android.hardware.Camera;
24 import android.util.Log;
25 import android.view.Display;
26 import android.view.WindowManager;
27
28 final class CameraConfigurationManager {
29
30   private static final String TAG = CameraConfigurationManager.class.getSimpleName();
31
32   private static final int TEN_DESIRED_ZOOM = 27;
33   private static final int DESIRED_SHARPNESS = 30;
34
35   private static final Pattern COMMA_PATTERN = Pattern.compile(",");
36
37   private final Context context;
38   private Point screenResolution;
39   private Point cameraResolution;
40   private int previewFormat;
41   private String previewFormatString;
42
43   CameraConfigurationManager(Context context) {
44     this.context = context;
45   }
46
47   /**
48    * Reads, one time, values from the camera that are needed by the app.
49    */
50   void initFromCameraParameters(Camera camera) {
51     Camera.Parameters parameters = camera.getParameters();
52     previewFormat = parameters.getPreviewFormat();
53     previewFormatString = parameters.get("preview-format");
54     Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
55     screenResolution = getScreenResolution();
56     cameraResolution = getCameraResolution(parameters);
57   }
58
59   /**
60    * Sets the camera up to take preview images which are used for both preview and decoding.
61    * We detect the preview format here so that buildLuminanceSource() can build an appropriate
62    * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
63    * and the planar Y can be used for barcode scanning without a copy in some cases.
64    */
65   void setDesiredCameraParameters(Camera camera) {
66     Camera.Parameters parameters = camera.getParameters();
67     Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
68     parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
69     setFlash(parameters);
70     setZoom(parameters);
71     //setSharpness(parameters);
72     camera.setParameters(parameters);
73   }
74
75   Point getCameraResolution() {
76     return cameraResolution;
77   }
78
79   int getPreviewFormat() {
80     return previewFormat;
81   }
82
83   String getPreviewFormatString() {
84     return previewFormatString;
85   }
86
87   private Point getScreenResolution() {
88     WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
89     Display display = manager.getDefaultDisplay();
90     return new Point(display.getWidth(), display.getHeight());
91   }
92
93   private Point getCameraResolution(Camera.Parameters parameters) {
94
95     String previewSizeValueString = parameters.get("preview-size-values");
96     // saw this on Xperia
97     if (previewSizeValueString == null) {
98       previewSizeValueString = parameters.get("preview-size-value");
99     }
100
101     Point cameraResolution = null;
102
103     if (previewSizeValueString != null) {
104       Log.v(TAG, "preview-size parameter: " + previewSizeValueString);
105       cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
106     }
107
108     if (cameraResolution == null) {
109       // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
110       cameraResolution = new Point(
111           (screenResolution.x >> 3) << 3,
112           (screenResolution.y >> 3) << 3);
113     }
114
115     return cameraResolution;
116   }
117
118   private static Point findBestPreviewSizeValue(String previewSizeValueString, Point screenResolution) {
119     int bestX = 0;
120     int bestY = 0;
121     int diff = Integer.MAX_VALUE;
122     for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
123
124       previewSize = previewSize.trim();
125       int dimPosition = previewSize.indexOf('x');
126       if (dimPosition < 0) {
127         Log.w(TAG, "Bad preview-size: " + previewSize);
128         continue;
129       }
130
131       int newX;
132       int newY;
133       try {
134         newX = Integer.parseInt(previewSize.substring(0, dimPosition));
135         newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
136       } catch (NumberFormatException nfe) {
137         Log.w(TAG, "Bad preview-size: " + previewSize);
138         continue;
139       }
140
141       int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
142       if (newDiff == 0) {
143         bestX = newX;
144         bestY = newY;
145         break;
146       } else if (newDiff < diff) {
147         bestX = newX;
148         bestY = newY;
149         diff = newDiff;
150       }
151
152     }
153
154     if (bestX > 0 && bestY > 0) {
155       return new Point(bestX, bestY);
156     }
157     return null;
158   }
159
160   private static int findBestMotZoomValue(String stringValues, int tenDesiredZoom) {
161     int tenBestValue = 0;
162     for (String stringValue : COMMA_PATTERN.split(stringValues)) {
163       stringValue = stringValue.trim();
164       double value;
165       try {
166         value = Double.parseDouble(stringValue);
167       } catch (NumberFormatException nfe) {
168         return tenDesiredZoom;
169       }
170       int tenValue = (int) (10.0 * value);
171       if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
172         tenBestValue = tenValue;
173       }
174     }
175     return tenBestValue;
176   }
177
178   private void setFlash(Camera.Parameters parameters) {
179     // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
180     parameters.set("flash-value", 2);
181     // This is the standard setting to turn the flash off that all devices should honor.
182     parameters.set("flash-mode", "off");
183   }
184
185   private void setZoom(Camera.Parameters parameters) {
186
187     String zoomSupportedString = parameters.get("zoom-supported");
188     if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
189       return;
190     }
191
192     int tenDesiredZoom = TEN_DESIRED_ZOOM;
193
194     String maxZoomString = parameters.get("max-zoom");
195     if (maxZoomString != null) {
196       try {
197         int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
198         if (tenDesiredZoom > tenMaxZoom) {
199           tenDesiredZoom = tenMaxZoom;
200         }
201       } catch (NumberFormatException nfe) {
202         Log.w(TAG, "Bad max-zoom: " + maxZoomString);
203       }
204     }
205
206     String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
207     if (takingPictureZoomMaxString != null) {
208       try {
209         int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
210         if (tenDesiredZoom > tenMaxZoom) {
211           tenDesiredZoom = tenMaxZoom;
212         }
213       } catch (NumberFormatException nfe) {
214         Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
215       }
216     }
217
218     String motZoomValuesString = parameters.get("mot-zoom-values");
219     if (motZoomValuesString != null) {
220       tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
221     }
222
223     String motZoomStepString = parameters.get("mot-zoom-step");
224     if (motZoomStepString != null) {
225       try {
226         double motZoomStep = Double.parseDouble(motZoomStepString.trim());
227         int tenZoomStep = (int) (10.0 * motZoomStep);
228         if (tenZoomStep > 1) {
229           tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
230         }
231       } catch (NumberFormatException nfe) {
232         // continue
233       }
234     }
235
236     // Set zoom. This helps encourage the user to pull back.
237     // Some devices like the Behold have a zoom parameter
238     if (maxZoomString != null || motZoomValuesString != null) {
239       parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
240     }
241
242     // Most devices, like the Hero, appear to expose this zoom parameter.
243     // It takes on values like "27" which appears to mean 2.7x zoom
244     if (takingPictureZoomMaxString != null) {
245       parameters.set("taking-picture-zoom", tenDesiredZoom);
246     }
247   }
248
249   /*
250   private void setSharpness(Camera.Parameters parameters) {
251
252     int desiredSharpness = DESIRED_SHARPNESS;
253
254     String maxSharpnessString = parameters.get("sharpness-max");
255     if (maxSharpnessString != null) {
256       try {
257         int maxSharpness = Integer.parseInt(maxSharpnessString);
258         if (desiredSharpness > maxSharpness) {
259           desiredSharpness = maxSharpness;
260         }
261       } catch (NumberFormatException nfe) {
262         Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
263       }
264     }
265
266     parameters.set("sharpness", desiredSharpness);
267   }
268    */
269 }