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