Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package org.briarproject.android.util;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.Size;
import android.os.Build;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import static android.hardware.Camera.CameraInfo.CAMERA_FACING_FRONT;
import static android.hardware.Camera.Parameters.FOCUS_MODE_AUTO;
import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE;
import static android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO;
import static android.hardware.Camera.Parameters.FOCUS_MODE_EDOF;
import static android.hardware.Camera.Parameters.FOCUS_MODE_FIXED;
import static android.hardware.Camera.Parameters.FOCUS_MODE_MACRO;
import static android.hardware.Camera.Parameters.SCENE_MODE_BARCODE;
import static android.view.SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
@SuppressWarnings("deprecation")
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
AutoFocusCallback {
private static final int AUTO_FOCUS_RETRY_DELAY = 5000; // Milliseconds
private static final Logger LOG =
Logger.getLogger(CameraView.class.getName());
private Camera camera = null;
private PreviewConsumer previewConsumer = null;
private int displayOrientation = 0, surfaceWidth = 0, surfaceHeight = 0;
private boolean autoFocus = false, surfaceExists = false;
public CameraView(Context context) {
super(context);
}
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CameraView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
setKeepScreenOn(true);
SurfaceHolder holder = getHolder();
if (Build.VERSION.SDK_INT < 11)
holder.setType(SURFACE_TYPE_PUSH_BUFFERS);
holder.addCallback(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
setKeepScreenOn(false);
getHolder().removeCallback(this);
}
public void start(Camera camera, PreviewConsumer previewConsumer,
int rotationDegrees) {
this.camera = camera;
this.previewConsumer = previewConsumer;
setDisplayOrientation(rotationDegrees);
Parameters params = camera.getParameters();
setFocusMode(params);
setPreviewSize(params);
applyParameters(params);
if (surfaceExists) startPreview(getHolder());
}
public void stop() {
stopPreview();
try {
camera.release();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error releasing camera", e);
}
camera = null;
}
private void startPreview(SurfaceHolder holder) {
try {
camera.setPreviewDisplay(holder);
camera.startPreview();
if (autoFocus) camera.autoFocus(this);
previewConsumer.start(camera);
} catch (IOException | RuntimeException e) {
LOG.log(WARNING, "Error starting camera preview", e);
}
}
private void stopPreview() {
try {
previewConsumer.stop();
if (autoFocus) camera.cancelAutoFocus();
camera.stopPreview();
} catch (RuntimeException e) {
LOG.log(WARNING, "Error stopping camera preview", e);
}
}
private void setDisplayOrientation(int rotationDegrees) {
int orientation;
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(0, info);
if (info.facing == CAMERA_FACING_FRONT) {
orientation = (info.orientation + rotationDegrees) % 360;
orientation = (360 - orientation) % 360;
} else {
orientation = (info.orientation - rotationDegrees + 360) % 360;
}
if(LOG.isLoggable(INFO))
LOG.info("Display orientation " + orientation + " degrees");
try {
camera.setDisplayOrientation(orientation);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting display orientation", e);
}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
displayOrientation = orientation;
}
private void setFocusMode(Parameters params) {
if (Build.VERSION.SDK_INT >= 15 &&
params.isVideoStabilizationSupported()) {
LOG.info("Enabling video stabilisation");
params.setVideoStabilization(true);
}
// This returns null on the HTC Wildfire S
List<String> sceneModes = params.getSupportedSceneModes();
if (sceneModes == null) sceneModes = Collections.emptyList();
List<String> focusModes = params.getSupportedFocusModes();
if (LOG.isLoggable(INFO)) {
LOG.info("Scene modes: " + sceneModes);
LOG.info("Focus modes: " + focusModes);
}
if (sceneModes.contains(SCENE_MODE_BARCODE)) {
LOG.info("Setting scene mode to barcode");
params.setSceneMode(SCENE_MODE_BARCODE);
} else if (Build.VERSION.SDK_INT >= 14 &&
focusModes.contains(FOCUS_MODE_CONTINUOUS_PICTURE)) {
LOG.info("Setting focus mode to continuous picture");
params.setFocusMode(FOCUS_MODE_CONTINUOUS_PICTURE);
} else if (focusModes.contains(FOCUS_MODE_CONTINUOUS_VIDEO)) {
LOG.info("Setting focus mode to continuous video");
params.setFocusMode(FOCUS_MODE_CONTINUOUS_VIDEO);
} else if (focusModes.contains(FOCUS_MODE_EDOF)) {
LOG.info("Setting focus mode to EDOF");
params.setFocusMode(FOCUS_MODE_EDOF);
} else if (focusModes.contains(FOCUS_MODE_MACRO)) {
LOG.info("Setting focus mode to macro");
params.setFocusMode(FOCUS_MODE_MACRO);
autoFocus = true;
} else if (focusModes.contains(FOCUS_MODE_AUTO)) {
LOG.info("Setting focus mode to auto");
params.setFocusMode(FOCUS_MODE_AUTO);
autoFocus = true;
} else if (focusModes.contains(FOCUS_MODE_FIXED)) {
LOG.info("Setting focus mode to fixed");
params.setFocusMode(FOCUS_MODE_FIXED);
} else {
LOG.info("No suitable focus mode");
}
params.setZoom(0);
}
private void setPreviewSize(Parameters params) {
if (surfaceWidth == 0 || surfaceHeight == 0) return;
float idealRatio = (float) surfaceWidth / surfaceHeight;
DisplayMetrics screen = getContext().getResources().getDisplayMetrics();
int screenMax = Math.max(screen.widthPixels, screen.heightPixels);
boolean rotatePreview = displayOrientation % 180 == 90;
List<Size> sizes = params.getSupportedPreviewSizes();
Size bestSize = null;
float bestScore = 0;
for (Size size : sizes) {
int width = rotatePreview ? size.height : size.width;
int height = rotatePreview ? size.width : size.height;
float ratio = (float) width / height;
float stretch = Math.max(ratio / idealRatio, idealRatio / ratio);
int pixels = width * height;
float score = width * height / stretch;
if (LOG.isLoggable(INFO)) {
LOG.info("Size " + size.width + "x" + size.height
+ ", stretch " + stretch + ", pixels " + pixels
+ ", score " + score);
}
// Large preview sizes can crash older devices
int maxDimension = Math.max(width, height);
if (Build.VERSION.SDK_INT < 14 && maxDimension > screenMax) {
LOG.info("Too large for screen");
continue;
}
if (bestSize == null || score > bestScore) {
bestSize = size;
bestScore = score;
}
}
if (bestSize != null) {
if(LOG.isLoggable(INFO))
LOG.info("Best size " + bestSize.width + "x" + bestSize.height);
params.setPreviewSize(bestSize.width, bestSize.height);
}
}
private void applyParameters(Parameters params) {
try {
camera.setParameters(params);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error setting camera parameters", e);
}
}
public void surfaceCreated(SurfaceHolder holder) {
LOG.info("Surface created");
surfaceExists = true;
if (camera != null) startPreview(holder);
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
surfaceWidth = w;
surfaceHeight = h;
if (camera == null) return; // We are stopped
stopPreview();
try {
Parameters params = camera.getParameters();
setPreviewSize(params);
applyParameters(params);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error getting camera parameters", e);
}
startPreview(holder);
}
public void surfaceDestroyed(SurfaceHolder holder) {
LOG.info("Surface destroyed");
surfaceExists = false;
}
public void onAutoFocus(boolean success, final Camera camera) {
LOG.info("Auto focus succeeded: " + success);
postDelayed(new Runnable() {
public void run() {
retryAutoFocus();
}
}, AUTO_FOCUS_RETRY_DELAY);
}
private void retryAutoFocus() {
try {
if (camera != null) camera.autoFocus(this);
} catch (RuntimeException e) {
LOG.log(WARNING, "Error retrying auto focus", e);
}
}
}