e06b01cd4000eeed278dc7762dcbeb80d0def0e0
[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     if (Build.MODEL.contains("Behold II") && CameraManager.SDK_INT == 3) { // 3 = Cupcake
188       parameters.set("flash-value", 1);
189     } else {
190       parameters.set("flash-value", 2);
191     }
192     // This is the standard setting to turn the flash off that all devices should honor.
193     parameters.set("flash-mode", "off");
194   }
195
196   private void setZoom(Camera.Parameters parameters) {
197
198     String zoomSupportedString = parameters.get("zoom-supported");
199     if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
200       return;
201     }
202
203     int tenDesiredZoom = TEN_DESIRED_ZOOM;
204
205     String maxZoomString = parameters.get("max-zoom");
206     if (maxZoomString != null) {
207       try {
208         int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
209         if (tenDesiredZoom > tenMaxZoom) {
210           tenDesiredZoom = tenMaxZoom;
211         }
212       } catch (NumberFormatException nfe) {
213         Log.w(TAG, "Bad max-zoom: " + maxZoomString);
214       }
215     }
216
217     String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
218     if (takingPictureZoomMaxString != null) {
219       try {
220         int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
221         if (tenDesiredZoom > tenMaxZoom) {
222           tenDesiredZoom = tenMaxZoom;
223         }
224       } catch (NumberFormatException nfe) {
225         Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
226       }
227     }
228
229     String motZoomValuesString = parameters.get("mot-zoom-values");
230     if (motZoomValuesString != null) {
231       tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
232     }
233
234     String motZoomStepString = parameters.get("mot-zoom-step");
235     if (motZoomStepString != null) {
236       try {
237         double motZoomStep = Double.parseDouble(motZoomStepString.trim());
238         int tenZoomStep = (int) (10.0 * motZoomStep);
239         if (tenZoomStep > 1) {
240           tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
241         }
242       } catch (NumberFormatException nfe) {
243         // continue
244       }
245     }
246
247     // Set zoom. This helps encourage the user to pull back.
248     // Some devices like the Behold have a zoom parameter
249     if (maxZoomString != null || motZoomValuesString != null) {
250       parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
251     }
252
253     // Most devices, like the Hero, appear to expose this zoom parameter.
254     // It takes on values like "27" which appears to mean 2.7x zoom
255     if (takingPictureZoomMaxString != null) {
256       parameters.set("taking-picture-zoom", tenDesiredZoom);
257     }
258   }
259
260   /*
261   private void setSharpness(Camera.Parameters parameters) {
262
263     int desiredSharpness = DESIRED_SHARPNESS;
264
265     String maxSharpnessString = parameters.get("sharpness-max");
266     if (maxSharpnessString != null) {
267       try {
268         int maxSharpness = Integer.parseInt(maxSharpnessString);
269         if (desiredSharpness > maxSharpness) {
270           desiredSharpness = maxSharpness;
271         }
272       } catch (NumberFormatException nfe) {
273         Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
274       }
275     }
276
277     parameters.set("sharpness", desiredSharpness);
278   }
279    */
280 }