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