blob: 5aacf419bb49979680e49441fdc6aa280630a54a [file] [log] [blame]
Angus Kong9ef99252013-07-18 18:04:19 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
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
Angus Kong20fad242013-11-11 18:23:46 -080017package com.android.camera.app;
Angus Kong9ef99252013-07-18 18:04:19 -070018
Angus Kong9ef99252013-07-18 18:04:19 -070019import android.annotation.TargetApi;
20import android.graphics.SurfaceTexture;
21import android.hardware.Camera;
22import android.hardware.Camera.AutoFocusCallback;
23import android.hardware.Camera.AutoFocusMoveCallback;
24import android.hardware.Camera.ErrorCallback;
25import android.hardware.Camera.FaceDetectionListener;
26import android.hardware.Camera.OnZoomChangeListener;
27import android.hardware.Camera.Parameters;
28import android.hardware.Camera.PictureCallback;
29import android.hardware.Camera.PreviewCallback;
30import android.hardware.Camera.ShutterCallback;
Sascha Haeberling638e6f02013-09-18 14:28:51 -070031import android.os.Build;
Angus Kong9ef99252013-07-18 18:04:19 -070032import android.os.Handler;
33import android.os.HandlerThread;
34import android.os.Looper;
35import android.os.Message;
Angus Kong62753ae2014-02-10 10:53:54 -080036import android.os.SystemClock;
Angus Kong9ef99252013-07-18 18:04:19 -070037import android.view.SurfaceHolder;
38
Angus Kong2bca2102014-03-11 16:27:30 -070039import com.android.camera.debug.Log;
Erin Dahlgren630d55b2014-03-10 14:40:59 -070040
Sascha Haeberling58501152014-01-06 11:02:35 -080041import java.io.IOException;
Angus Kong62753ae2014-02-10 10:53:54 -080042import java.util.LinkedList;
43import java.util.Queue;
Sascha Haeberling58501152014-01-06 11:02:35 -080044
Angus Kong9ef99252013-07-18 18:04:19 -070045/**
46 * A class to implement {@link CameraManager} of the Android camera framework.
47 */
48class AndroidCameraManagerImpl implements CameraManager {
Angus Kong2bca2102014-03-11 16:27:30 -070049 private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl");
Angus Kong62753ae2014-02-10 10:53:54 -080050 private static final long CAMERA_OPERATION_TIMEOUT_MS = 2500;
51 private static final long MAX_MESSAGE_QUEUE_LENGTH = 256;
Angus Kong9ef99252013-07-18 18:04:19 -070052
53 private Parameters mParameters;
54 private boolean mParametersIsDirty;
Angus Kong9ef99252013-07-18 18:04:19 -070055
56 /* Messages used in CameraHandler. */
57 // Camera initialization/finalization
58 private static final int OPEN_CAMERA = 1;
59 private static final int RELEASE = 2;
60 private static final int RECONNECT = 3;
61 private static final int UNLOCK = 4;
62 private static final int LOCK = 5;
63 // Preview
64 private static final int SET_PREVIEW_TEXTURE_ASYNC = 101;
65 private static final int START_PREVIEW_ASYNC = 102;
66 private static final int STOP_PREVIEW = 103;
67 private static final int SET_PREVIEW_CALLBACK_WITH_BUFFER = 104;
68 private static final int ADD_CALLBACK_BUFFER = 105;
69 private static final int SET_PREVIEW_DISPLAY_ASYNC = 106;
70 private static final int SET_PREVIEW_CALLBACK = 107;
Doris Liu4d4a4bc2013-12-19 18:55:54 -080071 private static final int SET_ONE_SHOT_PREVIEW_CALLBACK = 108;
Angus Kong9ef99252013-07-18 18:04:19 -070072 // Parameters
73 private static final int SET_PARAMETERS = 201;
74 private static final int GET_PARAMETERS = 202;
75 private static final int REFRESH_PARAMETERS = 203;
76 // Focus, Zoom
77 private static final int AUTO_FOCUS = 301;
78 private static final int CANCEL_AUTO_FOCUS = 302;
79 private static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 303;
80 private static final int SET_ZOOM_CHANGE_LISTENER = 304;
81 // Face detection
82 private static final int SET_FACE_DETECTION_LISTENER = 461;
83 private static final int START_FACE_DETECTION = 462;
84 private static final int STOP_FACE_DETECTION = 463;
85 private static final int SET_ERROR_CALLBACK = 464;
86 // Presentation
87 private static final int ENABLE_SHUTTER_SOUND = 501;
88 private static final int SET_DISPLAY_ORIENTATION = 502;
Angus Kong188cfc32013-11-17 03:04:04 -080089 // Capture
90 private static final int CAPTURE_PHOTO = 601;
Angus Kong9ef99252013-07-18 18:04:19 -070091
Angus Kong62753ae2014-02-10 10:53:54 -080092 /** Camera states **/
93 // These states are defined bitwise so we can easily to specify a set of
94 // states together.
95 private static final int CAMERA_UNOPENED = 1;
96 private static final int CAMERA_IDLE = 1 << 1;
97 private static final int CAMERA_UNLOCKED = 1 << 2;
98 private static final int CAMERA_CAPTURING = 1 << 3;
99 private static final int CAMERA_FOCUSING = 1 << 4;
100
Sascha Haeberling58501152014-01-06 11:02:35 -0800101 private final CameraHandler mCameraHandler;
Angus Kong62753ae2014-02-10 10:53:54 -0800102 private final HandlerThread mCameraHandlerThread;
103 private final CameraStateHolder mCameraState;
104 private final DispatchThread mDispatchThread;
Angus Kong9ef99252013-07-18 18:04:19 -0700105
106 // Used to retain a copy of Parameters for setting parameters.
107 private Parameters mParamsToSet;
108
Erin Dahlgren630d55b2014-03-10 14:40:59 -0700109 private Handler mCameraExceptionCallbackHandler;
110 private CameraExceptionCallback mCameraExceptionCallback =
111 new CameraExceptionCallback() {
112 @Override
113 public void onCameraException(RuntimeException e) {
114 throw e;
115 }
116 };
117
Angus Kong9ef99252013-07-18 18:04:19 -0700118 AndroidCameraManagerImpl() {
Angus Kong62753ae2014-02-10 10:53:54 -0800119 mCameraHandlerThread = new HandlerThread("Camera Handler Thread");
120 mCameraHandlerThread.start();
121 mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper());
Erin Dahlgren630d55b2014-03-10 14:40:59 -0700122 mCameraExceptionCallbackHandler = mCameraHandler;
Angus Kong62753ae2014-02-10 10:53:54 -0800123 mCameraState = new CameraStateHolder();
124 mDispatchThread = new DispatchThread();
125 mDispatchThread.start();
Angus Kong9ef99252013-07-18 18:04:19 -0700126 }
127
Erin Dahlgren630d55b2014-03-10 14:40:59 -0700128 @Override
129 public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback,
130 Handler handler) {
131 synchronized (mCameraExceptionCallback) {
132 mCameraExceptionCallback = callback;
133 mCameraExceptionCallbackHandler = handler;
134 }
135 }
136
Angus Kong62753ae2014-02-10 10:53:54 -0800137 /**
138 * Recycles the resources used by this instance. CameraManager will be in
139 * an unusable state after calling this.
140 */
141 public void recycle() {
142 mDispatchThread.end();
143 }
144
145 private static class CameraStateHolder {
146 private int mState;
147
148 public CameraStateHolder() {
149 setState(CAMERA_UNOPENED);
150 }
151
152 public CameraStateHolder(int state) {
153 setState(state);
154 }
155
156 public synchronized void setState(int state) {
157 mState = state;
158 this.notifyAll();
159 }
160
161 public synchronized int getState() {
162 return mState;
163 }
164
165 private interface ConditionChecker {
166 /**
167 * @return Whether the condition holds.
168 */
169 boolean success();
170 }
171
172 /**
173 * A helper method used by {@link #waitToAvoidStates(int)} and
174 * {@link #waitForStates(int)}. This method will wait until the
175 * condition is successful.
176 *
177 * @param stateChecker The state checker to be used.
178 * @param timeoutMs The timeout limit in milliseconds.
179 * @return {@code false} if the wait is interrupted or timeout limit is
180 * reached.
181 */
182 private boolean waitForCondition(ConditionChecker stateChecker,
183 long timeoutMs) {
184 long timeBound = SystemClock.uptimeMillis() + timeoutMs;
185 synchronized (this) {
186 while (!stateChecker.success()) {
187 try {
188 this.wait(timeoutMs);
189 } catch (InterruptedException ex) {
190 if (SystemClock.uptimeMillis() > timeBound) {
191 // Timeout.
192 Log.w(TAG, "Timeout waiting.");
193 }
194 return false;
195 }
196 }
197 }
198 return true;
199 }
200
201 /**
202 * Block the current thread until the state becomes one of the
203 * specified.
204 *
205 * @param states Expected states.
206 * @return {@code false} if the wait is interrupted or timeout limit is
207 * reached.
208 */
209 public boolean waitForStates(final int states) {
210 return waitForCondition(new ConditionChecker() {
211 @Override
212 public boolean success() {
213 return (states | mState) == states;
214 }
215 }, CAMERA_OPERATION_TIMEOUT_MS);
216 }
217
218 /**
219 * Block the current thread until the state becomes NOT one of the
220 * specified.
221 *
222 * @param states States to avoid.
223 * @return {@code false} if the wait is interrupted or timeout limit is
224 * reached.
225 */
226 public boolean waitToAvoidStates(final int states) {
227 return waitForCondition(new ConditionChecker() {
228 @Override
229 public boolean success() {
230 return (states & mState) == 0;
231 }
232 }, CAMERA_OPERATION_TIMEOUT_MS);
233 }
234 }
235
236 /**
237 * The handler on which the actual camera operations happen.
238 */
Angus Kong9ef99252013-07-18 18:04:19 -0700239 private class CameraHandler extends Handler {
Angus Kong62753ae2014-02-10 10:53:54 -0800240 private Camera mCamera;
Angus Kong188cfc32013-11-17 03:04:04 -0800241 private class CaptureCallbacks {
242 public final ShutterCallback mShutter;
243 public final PictureCallback mRaw;
244 public final PictureCallback mPostView;
245 public final PictureCallback mJpeg;
246
247 CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView,
248 PictureCallback jpeg) {
249 mShutter = shutter;
250 mRaw = raw;
251 mPostView = postView;
252 mJpeg = jpeg;
253 }
254 }
255
Angus Kong9ef99252013-07-18 18:04:19 -0700256 CameraHandler(Looper looper) {
257 super(looper);
258 }
259
Angus Kong9ef99252013-07-18 18:04:19 -0700260 private void startFaceDetection() {
261 mCamera.startFaceDetection();
262 }
263
Angus Kong9ef99252013-07-18 18:04:19 -0700264 private void stopFaceDetection() {
265 mCamera.stopFaceDetection();
266 }
267
Angus Kong9ef99252013-07-18 18:04:19 -0700268 private void setFaceDetectionListener(FaceDetectionListener listener) {
269 mCamera.setFaceDetectionListener(listener);
270 }
271
Angus Kong9ef99252013-07-18 18:04:19 -0700272 private void setPreviewTexture(Object surfaceTexture) {
273 try {
274 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture);
275 } catch (IOException e) {
Sascha Haeberling464f2202013-10-17 15:29:38 -0700276 Log.e(TAG, "Could not set preview texture", e);
Angus Kong9ef99252013-07-18 18:04:19 -0700277 }
278 }
279
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700280 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
Angus Kong9ef99252013-07-18 18:04:19 -0700281 private void enableShutterSound(boolean enable) {
282 mCamera.enableShutterSound(enable);
283 }
284
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700285 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Angus Kong9ef99252013-07-18 18:04:19 -0700286 private void setAutoFocusMoveCallback(
287 android.hardware.Camera camera, Object cb) {
288 camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb);
289 }
290
Angus Kong188cfc32013-11-17 03:04:04 -0800291 private void capture(final CaptureCallbacks cb) {
292 try {
293 mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg);
294 } catch (RuntimeException e) {
295 // TODO: output camera state and focus state for debugging.
296 Log.e(TAG, "take picture failed.");
297 throw e;
298 }
299 }
300
Angus Kong9ef99252013-07-18 18:04:19 -0700301 public void requestTakePicture(
302 final ShutterCallback shutter,
303 final PictureCallback raw,
304 final PictureCallback postView,
305 final PictureCallback jpeg) {
Angus Kong188cfc32013-11-17 03:04:04 -0800306 final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg);
307 obtainMessage(CAPTURE_PHOTO, callbacks).sendToTarget();
Angus Kong9ef99252013-07-18 18:04:19 -0700308 }
309
310 /**
Angus Kong9ef99252013-07-18 18:04:19 -0700311 * This method does not deal with the API level check. Everyone should
312 * check first for supported operations before sending message to this handler.
313 */
314 @Override
315 public void handleMessage(final Message msg) {
316 try {
317 switch (msg.what) {
Angus Kong20fad242013-11-11 18:23:46 -0800318 case OPEN_CAMERA: {
319 final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj;
320 final int cameraId = msg.arg1;
Angus Kong62753ae2014-02-10 10:53:54 -0800321 if (mCameraState.getState() != CAMERA_UNOPENED) {
322 openCallback.onDeviceOpenedAlready(cameraId);
323 break;
324 }
325
Angus Kong20fad242013-11-11 18:23:46 -0800326 mCamera = android.hardware.Camera.open(cameraId);
327 if (mCamera != null) {
328 mParametersIsDirty = true;
329
330 // Get a instance of Camera.Parameters for later use.
331 if (mParamsToSet == null) {
332 mParamsToSet = mCamera.getParameters();
333 }
334
Angus Kong62753ae2014-02-10 10:53:54 -0800335 mCameraState.setState(CAMERA_IDLE);
Angus Kong20fad242013-11-11 18:23:46 -0800336 if (openCallback != null) {
Angus Kong62753ae2014-02-10 10:53:54 -0800337 openCallback.onCameraOpened(
338 new AndroidCameraProxyImpl(cameraId, mCamera));
Angus Kong20fad242013-11-11 18:23:46 -0800339 }
340 } else {
341 if (openCallback != null) {
342 openCallback.onDeviceOpenFailure(cameraId);
343 }
344 }
Angus Kong62753ae2014-02-10 10:53:54 -0800345 break;
Angus Kong20fad242013-11-11 18:23:46 -0800346 }
Angus Kong9ef99252013-07-18 18:04:19 -0700347
Angus Kong20fad242013-11-11 18:23:46 -0800348 case RELEASE: {
Angus Kong9ef99252013-07-18 18:04:19 -0700349 mCamera.release();
Angus Kong62753ae2014-02-10 10:53:54 -0800350 mCameraState.setState(CAMERA_UNOPENED);
Angus Kong9ef99252013-07-18 18:04:19 -0700351 mCamera = null;
Angus Kong62753ae2014-02-10 10:53:54 -0800352 break;
Angus Kong20fad242013-11-11 18:23:46 -0800353 }
Angus Kong9ef99252013-07-18 18:04:19 -0700354
Angus Kong20fad242013-11-11 18:23:46 -0800355 case RECONNECT: {
356 final CameraOpenCallbackForward cbForward =
357 (CameraOpenCallbackForward) msg.obj;
358 final int cameraId = msg.arg1;
359 try {
360 mCamera.reconnect();
361 } catch (IOException ex) {
362 if (cbForward != null) {
363 cbForward.onReconnectionFailure(AndroidCameraManagerImpl.this);
364 }
Angus Kong62753ae2014-02-10 10:53:54 -0800365 break;
Angus Kong20fad242013-11-11 18:23:46 -0800366 }
367
Angus Kong62753ae2014-02-10 10:53:54 -0800368 mCameraState.setState(CAMERA_IDLE);
Angus Kong20fad242013-11-11 18:23:46 -0800369 if (cbForward != null) {
Angus Kong62753ae2014-02-10 10:53:54 -0800370 cbForward.onCameraOpened(new AndroidCameraProxyImpl(cameraId, mCamera));
Angus Kong20fad242013-11-11 18:23:46 -0800371 }
Angus Kong62753ae2014-02-10 10:53:54 -0800372 break;
Angus Kong20fad242013-11-11 18:23:46 -0800373 }
Angus Kong9ef99252013-07-18 18:04:19 -0700374
Angus Kong20fad242013-11-11 18:23:46 -0800375 case UNLOCK: {
Angus Kong9ef99252013-07-18 18:04:19 -0700376 mCamera.unlock();
Angus Kong62753ae2014-02-10 10:53:54 -0800377 mCameraState.setState(CAMERA_UNLOCKED);
378 break;
Angus Kong20fad242013-11-11 18:23:46 -0800379 }
Angus Kong9ef99252013-07-18 18:04:19 -0700380
Angus Kong20fad242013-11-11 18:23:46 -0800381 case LOCK: {
Angus Kong9ef99252013-07-18 18:04:19 -0700382 mCamera.lock();
Angus Kong62753ae2014-02-10 10:53:54 -0800383 mCameraState.setState(CAMERA_IDLE);
384 break;
Angus Kong20fad242013-11-11 18:23:46 -0800385 }
Angus Kong9ef99252013-07-18 18:04:19 -0700386
Angus Kong20fad242013-11-11 18:23:46 -0800387 case SET_PREVIEW_TEXTURE_ASYNC: {
Angus Kong9ef99252013-07-18 18:04:19 -0700388 setPreviewTexture(msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800389 break;
Angus Kong20fad242013-11-11 18:23:46 -0800390 }
Angus Kong9ef99252013-07-18 18:04:19 -0700391
Angus Kong20fad242013-11-11 18:23:46 -0800392 case SET_PREVIEW_DISPLAY_ASYNC: {
Angus Kong9ef99252013-07-18 18:04:19 -0700393 try {
394 mCamera.setPreviewDisplay((SurfaceHolder) msg.obj);
395 } catch (IOException e) {
396 throw new RuntimeException(e);
397 }
Angus Kong62753ae2014-02-10 10:53:54 -0800398 break;
Angus Kong20fad242013-11-11 18:23:46 -0800399 }
Angus Kong9ef99252013-07-18 18:04:19 -0700400
Angus Kong20fad242013-11-11 18:23:46 -0800401 case START_PREVIEW_ASYNC: {
Erin Dahlgrend05374f2014-03-11 16:23:29 -0700402 final CameraStartPreviewCallbackForward cbForward =
403 (CameraStartPreviewCallbackForward) msg.obj;
Angus Kong9ef99252013-07-18 18:04:19 -0700404 mCamera.startPreview();
Erin Dahlgrend05374f2014-03-11 16:23:29 -0700405 if (cbForward != null) {
406 cbForward.onPreviewStarted();
407 }
Angus Kong62753ae2014-02-10 10:53:54 -0800408 break;
Angus Kong20fad242013-11-11 18:23:46 -0800409 }
Angus Kong9ef99252013-07-18 18:04:19 -0700410
Angus Kong20fad242013-11-11 18:23:46 -0800411 case STOP_PREVIEW: {
Angus Kong9ef99252013-07-18 18:04:19 -0700412 mCamera.stopPreview();
Angus Kong62753ae2014-02-10 10:53:54 -0800413 break;
Angus Kong20fad242013-11-11 18:23:46 -0800414 }
Angus Kong9ef99252013-07-18 18:04:19 -0700415
Angus Kong20fad242013-11-11 18:23:46 -0800416 case SET_PREVIEW_CALLBACK_WITH_BUFFER: {
Angus Kong62753ae2014-02-10 10:53:54 -0800417 mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj);
418 break;
Angus Kong20fad242013-11-11 18:23:46 -0800419 }
Angus Kong9ef99252013-07-18 18:04:19 -0700420
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800421 case SET_ONE_SHOT_PREVIEW_CALLBACK: {
Angus Kong62753ae2014-02-10 10:53:54 -0800422 mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj);
423 break;
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800424 }
425
Angus Kong20fad242013-11-11 18:23:46 -0800426 case ADD_CALLBACK_BUFFER: {
Angus Kong9ef99252013-07-18 18:04:19 -0700427 mCamera.addCallbackBuffer((byte[]) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800428 break;
Angus Kong20fad242013-11-11 18:23:46 -0800429 }
Angus Kong9ef99252013-07-18 18:04:19 -0700430
Angus Kong20fad242013-11-11 18:23:46 -0800431 case AUTO_FOCUS: {
Angus Kong62753ae2014-02-10 10:53:54 -0800432 mCameraState.setState(CAMERA_FOCUSING);
Angus Kong9ef99252013-07-18 18:04:19 -0700433 mCamera.autoFocus((AutoFocusCallback) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800434 break;
Angus Kong20fad242013-11-11 18:23:46 -0800435 }
Angus Kong9ef99252013-07-18 18:04:19 -0700436
Angus Kong20fad242013-11-11 18:23:46 -0800437 case CANCEL_AUTO_FOCUS: {
Angus Kong9ef99252013-07-18 18:04:19 -0700438 mCamera.cancelAutoFocus();
Angus Kong62753ae2014-02-10 10:53:54 -0800439 mCameraState.setState(CAMERA_IDLE);
440 break;
Angus Kong20fad242013-11-11 18:23:46 -0800441 }
Angus Kong9ef99252013-07-18 18:04:19 -0700442
Angus Kong20fad242013-11-11 18:23:46 -0800443 case SET_AUTO_FOCUS_MOVE_CALLBACK: {
Angus Kong9ef99252013-07-18 18:04:19 -0700444 setAutoFocusMoveCallback(mCamera, msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800445 break;
Angus Kong20fad242013-11-11 18:23:46 -0800446 }
Angus Kong9ef99252013-07-18 18:04:19 -0700447
Angus Kong20fad242013-11-11 18:23:46 -0800448 case SET_DISPLAY_ORIENTATION: {
Angus Kong9ef99252013-07-18 18:04:19 -0700449 mCamera.setDisplayOrientation(msg.arg1);
Angus Kong62753ae2014-02-10 10:53:54 -0800450 break;
Angus Kong20fad242013-11-11 18:23:46 -0800451 }
Angus Kong9ef99252013-07-18 18:04:19 -0700452
Angus Kong20fad242013-11-11 18:23:46 -0800453 case SET_ZOOM_CHANGE_LISTENER: {
Angus Kong62753ae2014-02-10 10:53:54 -0800454 mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj);
455 break;
Angus Kong20fad242013-11-11 18:23:46 -0800456 }
Angus Kong9ef99252013-07-18 18:04:19 -0700457
Angus Kong20fad242013-11-11 18:23:46 -0800458 case SET_FACE_DETECTION_LISTENER: {
Angus Kong9ef99252013-07-18 18:04:19 -0700459 setFaceDetectionListener((FaceDetectionListener) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800460 break;
Angus Kong20fad242013-11-11 18:23:46 -0800461 }
Angus Kong9ef99252013-07-18 18:04:19 -0700462
Angus Kong20fad242013-11-11 18:23:46 -0800463 case START_FACE_DETECTION: {
Angus Kong9ef99252013-07-18 18:04:19 -0700464 startFaceDetection();
Angus Kong62753ae2014-02-10 10:53:54 -0800465 break;
Angus Kong20fad242013-11-11 18:23:46 -0800466 }
Angus Kong9ef99252013-07-18 18:04:19 -0700467
Angus Kong20fad242013-11-11 18:23:46 -0800468 case STOP_FACE_DETECTION: {
Angus Kong9ef99252013-07-18 18:04:19 -0700469 stopFaceDetection();
Angus Kong62753ae2014-02-10 10:53:54 -0800470 break;
Angus Kong20fad242013-11-11 18:23:46 -0800471 }
Angus Kong9ef99252013-07-18 18:04:19 -0700472
Angus Kong20fad242013-11-11 18:23:46 -0800473 case SET_ERROR_CALLBACK: {
Angus Kong9ef99252013-07-18 18:04:19 -0700474 mCamera.setErrorCallback((ErrorCallback) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800475 break;
Angus Kong20fad242013-11-11 18:23:46 -0800476 }
Angus Kong9ef99252013-07-18 18:04:19 -0700477
Angus Kong20fad242013-11-11 18:23:46 -0800478 case SET_PARAMETERS: {
Angus Kong9ef99252013-07-18 18:04:19 -0700479 mParametersIsDirty = true;
480 mParamsToSet.unflatten((String) msg.obj);
481 mCamera.setParameters(mParamsToSet);
Angus Kong62753ae2014-02-10 10:53:54 -0800482 break;
Angus Kong20fad242013-11-11 18:23:46 -0800483 }
Angus Kong9ef99252013-07-18 18:04:19 -0700484
Angus Kong20fad242013-11-11 18:23:46 -0800485 case GET_PARAMETERS: {
Angus Kong9ef99252013-07-18 18:04:19 -0700486 if (mParametersIsDirty) {
487 mParameters = mCamera.getParameters();
488 mParametersIsDirty = false;
489 }
Angus Kong62753ae2014-02-10 10:53:54 -0800490 break;
Angus Kong20fad242013-11-11 18:23:46 -0800491 }
Angus Kong9ef99252013-07-18 18:04:19 -0700492
Angus Kong20fad242013-11-11 18:23:46 -0800493 case SET_PREVIEW_CALLBACK: {
Angus Kong9ef99252013-07-18 18:04:19 -0700494 mCamera.setPreviewCallback((PreviewCallback) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800495 break;
Angus Kong20fad242013-11-11 18:23:46 -0800496 }
Angus Kong9ef99252013-07-18 18:04:19 -0700497
Angus Kong20fad242013-11-11 18:23:46 -0800498 case ENABLE_SHUTTER_SOUND: {
Angus Kong9ef99252013-07-18 18:04:19 -0700499 enableShutterSound((msg.arg1 == 1) ? true : false);
Angus Kong62753ae2014-02-10 10:53:54 -0800500 break;
Angus Kong20fad242013-11-11 18:23:46 -0800501 }
Angus Kong9ef99252013-07-18 18:04:19 -0700502
Angus Kong20fad242013-11-11 18:23:46 -0800503 case REFRESH_PARAMETERS: {
Angus Kong9ef99252013-07-18 18:04:19 -0700504 mParametersIsDirty = true;
Angus Kong62753ae2014-02-10 10:53:54 -0800505 break;
Angus Kong20fad242013-11-11 18:23:46 -0800506 }
Angus Kong9ef99252013-07-18 18:04:19 -0700507
Angus Kong188cfc32013-11-17 03:04:04 -0800508 case CAPTURE_PHOTO: {
Angus Kong62753ae2014-02-10 10:53:54 -0800509 mCameraState.setState(CAMERA_CAPTURING);
Angus Kong188cfc32013-11-17 03:04:04 -0800510 capture((CaptureCallbacks) msg.obj);
Angus Kong62753ae2014-02-10 10:53:54 -0800511 break;
Angus Kong188cfc32013-11-17 03:04:04 -0800512 }
513
Angus Kong20fad242013-11-11 18:23:46 -0800514 default: {
Angus Kong9ef99252013-07-18 18:04:19 -0700515 throw new RuntimeException("Invalid CameraProxy message=" + msg.what);
Angus Kong20fad242013-11-11 18:23:46 -0800516 }
Angus Kong9ef99252013-07-18 18:04:19 -0700517 }
Erin Dahlgren630d55b2014-03-10 14:40:59 -0700518 } catch (final RuntimeException e) {
Angus Kong9ef99252013-07-18 18:04:19 -0700519 if (msg.what != RELEASE && mCamera != null) {
520 try {
521 mCamera.release();
Angus Kong62753ae2014-02-10 10:53:54 -0800522 mCameraState.setState(CAMERA_UNOPENED);
Angus Kong9ef99252013-07-18 18:04:19 -0700523 } catch (Exception ex) {
524 Log.e(TAG, "Fail to release the camera.");
525 }
526 mCamera = null;
Angus Kong62753ae2014-02-10 10:53:54 -0800527 } else {
528 if (mCamera == null) {
529 if (msg.what == OPEN_CAMERA) {
530 if (msg.obj != null) {
531 ((CameraOpenCallback) msg.obj).onDeviceOpenFailure(msg.arg1);
532 }
533 } else {
Erin Dahlgrend05374f2014-03-11 16:23:29 -0700534 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null.");
ztenghuicfc148d2013-10-28 15:27:02 -0700535 }
Angus Kong62753ae2014-02-10 10:53:54 -0800536 return;
ztenghuicfc148d2013-10-28 15:27:02 -0700537 }
Angus Kong9ef99252013-07-18 18:04:19 -0700538 }
Erin Dahlgren630d55b2014-03-10 14:40:59 -0700539 synchronized (mCameraExceptionCallback) {
540 mCameraExceptionCallbackHandler.post(new Runnable() {
541 @Override
542 public void run() {
543 mCameraExceptionCallback.onCameraException(e);
544 }
545 });
546 }
Angus Kong9ef99252013-07-18 18:04:19 -0700547 }
548 }
549 }
550
Angus Kong62753ae2014-02-10 10:53:54 -0800551 private class DispatchThread extends Thread {
552
553 private Queue<Runnable> mJobQueue;
554 private Boolean mIsEnded;
555
556 public DispatchThread() {
557 super("Camera Job Dispatch Thread");
558 mJobQueue = new LinkedList<Runnable>();
559 mIsEnded = new Boolean(false);
560 }
561
562 /**
563 * Queues up the job.
564 *
565 * @param job The job to run.
566 */
567 public void runJob(Runnable job) {
568 if (isEnded()) {
569 throw new IllegalStateException(
570 "Trying to run job on interrupted dispatcher thread");
571 }
572 synchronized (mJobQueue) {
573 if (mJobQueue.size() == MAX_MESSAGE_QUEUE_LENGTH) {
574 throw new RuntimeException("Camera master thread job queue full");
575 }
576
577 mJobQueue.add(job);
578 mJobQueue.notifyAll();
579 }
580 }
581
582 /**
583 * Queues up the job and wait for it to be done.
584 *
585 * @param job The job to run.
586 * @param timeoutMs Timeout limit in milliseconds.
587 * @return Whether the waiting is interrupted.
588 */
589 public boolean runJobSync(final Runnable job, Object waitLock, long timeoutMs) {
590 synchronized (waitLock) {
591 long timeoutBound = SystemClock.uptimeMillis() + timeoutMs;
592 try {
593 runJob(job);
594 waitLock.wait();
595 } catch (InterruptedException ex) {
596 Log.v(TAG, "Job interrupted");
597 if (SystemClock.uptimeMillis() > timeoutBound) {
598 // Timeout.
599 Log.v(TAG, "Timeout waiting camera operation");
600 }
601 return false;
602 }
603 }
604 return true;
605 }
606
607 /**
608 * Gracefully ends this thread. Will stop after all jobs are processed.
609 */
610 public void end() {
611 synchronized (mIsEnded) {
612 mIsEnded = true;
613 }
614 synchronized(mJobQueue) {
615 mJobQueue.notifyAll();
616 }
617 }
618
619 private boolean isEnded() {
620 synchronized (mIsEnded) {
621 return mIsEnded;
622 }
623 }
624
625 @Override
626 public void run() {
627 while(true) {
628 Runnable job = null;
629 synchronized (mJobQueue) {
630 while (mJobQueue.size() == 0 && !isEnded()) {
631 try {
632 mJobQueue.wait();
633 } catch (InterruptedException ex) {
634 Log.v(TAG, "Dispatcher thread wait() interrupted, exiting");
635 break;
636 }
637 }
638
639 job = mJobQueue.poll();
640 }
641
642 if (job == null) {
643 // mJobQueue.poll() returning null means wait() is
644 // interrupted and the queue is empty.
645 if (isEnded()) {
646 break;
647 }
648 continue;
649 }
650
651 job.run();
652
653 synchronized (DispatchThread.this) {
654 mCameraHandler.post(new Runnable() {
655 @Override
656 public void run() {
657 synchronized (DispatchThread.this) {
658 DispatchThread.this.notifyAll();
659 }
660 }
661 });
662 try {
663 DispatchThread.this.wait();
664 } catch (InterruptedException ex) {
665 // TODO: do something here.
666 }
667 }
668 }
669 mCameraHandlerThread.quitSafely();
670 }
671 }
672
Angus Kong9ef99252013-07-18 18:04:19 -0700673 @Override
Angus Konge6a62c42014-02-28 10:38:20 -0800674 public void cameraOpen(final Handler handler, final int cameraId,
675 final CameraOpenCallback callback) {
676 mDispatchThread.runJob(new Runnable() {
677 @Override
678 public void run() {
679 mCameraHandler.obtainMessage(OPEN_CAMERA, cameraId, 0,
680 CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget();
681 }
682 });
Angus Kong20fad242013-11-11 18:23:46 -0800683 }
684
Angus Kong9ef99252013-07-18 18:04:19 -0700685 /**
Angus Kong4f795b82013-09-16 14:25:35 -0700686 * A class which implements {@link CameraManager.CameraProxy} and
Angus Kong9ef99252013-07-18 18:04:19 -0700687 * camera handler thread.
688 */
Angus Kong2bca2102014-03-11 16:27:30 -0700689 private class AndroidCameraProxyImpl implements CameraManager.CameraProxy {
Angus Kong20fad242013-11-11 18:23:46 -0800690 private final int mCameraId;
Angus Kong62753ae2014-02-10 10:53:54 -0800691 /* TODO: remove this Camera instance. */
692 private final Camera mCamera;
Angus Kong9ef99252013-07-18 18:04:19 -0700693
Angus Kong62753ae2014-02-10 10:53:54 -0800694 private AndroidCameraProxyImpl(int cameraId, Camera camera) {
695 mCamera = camera;
Angus Kong20fad242013-11-11 18:23:46 -0800696 mCameraId = cameraId;
Angus Kong9ef99252013-07-18 18:04:19 -0700697 }
698
699 @Override
700 public android.hardware.Camera getCamera() {
701 return mCamera;
702 }
703
704 @Override
Angus Kong20fad242013-11-11 18:23:46 -0800705 public int getCameraId() {
706 return mCameraId;
707 }
708
709 // TODO: Make this package private.
710 @Override
711 public void release(boolean sync) {
Angus Kong62753ae2014-02-10 10:53:54 -0800712 Log.v(TAG, "camera manager release");
Angus Kong20fad242013-11-11 18:23:46 -0800713 if (sync) {
Angus Kong62753ae2014-02-10 10:53:54 -0800714 final WaitDoneBundle bundle = new WaitDoneBundle();
715 mDispatchThread.runJobSync(new Runnable() {
716 @Override
717 public void run() {
718 mCameraHandler.sendEmptyMessage(RELEASE);
719 mCameraHandler.post(bundle.mUnlockRunnable);
720 }
721 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS);
722 } else {
723 mDispatchThread.runJob(new Runnable() {
724 @Override
725 public void run() {
726 mCameraHandler.removeCallbacksAndMessages(null);
727 mCameraHandler.sendEmptyMessage(RELEASE);
728 }
729 });
Angus Kong20fad242013-11-11 18:23:46 -0800730 }
Angus Kong9ef99252013-07-18 18:04:19 -0700731 }
732
733 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800734 public void reconnect(final Handler handler, final CameraOpenCallback cb) {
735 mDispatchThread.runJob(new Runnable() {
736 @Override
737 public void run() {
738 mCameraHandler.obtainMessage(RECONNECT, mCameraId, 0,
739 CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget();
Angus Kong20fad242013-11-11 18:23:46 -0800740 }
Angus Kong62753ae2014-02-10 10:53:54 -0800741 });
Angus Kong9ef99252013-07-18 18:04:19 -0700742 }
743
744 @Override
745 public void unlock() {
Angus Kong62753ae2014-02-10 10:53:54 -0800746 final WaitDoneBundle bundle = new WaitDoneBundle();
747 mDispatchThread.runJobSync(new Runnable() {
748 @Override
749 public void run() {
750 mCameraHandler.sendEmptyMessage(UNLOCK);
751 mCameraHandler.post(bundle.mUnlockRunnable);
752 }
753 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS);
Angus Kong9ef99252013-07-18 18:04:19 -0700754 }
755
756 @Override
757 public void lock() {
Angus Kong62753ae2014-02-10 10:53:54 -0800758 mDispatchThread.runJob(new Runnable() {
759 @Override
760 public void run() {
761 mCameraHandler.sendEmptyMessage(LOCK);
762 }
763 });
Angus Kong9ef99252013-07-18 18:04:19 -0700764 }
765
Angus Kong9ef99252013-07-18 18:04:19 -0700766 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800767 public void setPreviewTexture(final SurfaceTexture surfaceTexture) {
768 mDispatchThread.runJob(new Runnable() {
769 @Override
770 public void run() {
771 mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
772 .sendToTarget();
773 }
774 });
Angus Kong9ef99252013-07-18 18:04:19 -0700775 }
776
777 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800778 public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) {
779 final WaitDoneBundle bundle = new WaitDoneBundle();
780 mDispatchThread.runJobSync(new Runnable() {
781 @Override
782 public void run() {
783 mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture)
784 .sendToTarget();
785 mCameraHandler.post(bundle.mUnlockRunnable);
786 }
787 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS);
Erin Dahlgrend8de0772014-02-03 10:12:27 -0800788 }
789
790 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800791 public void setPreviewDisplay(final SurfaceHolder surfaceHolder) {
792 mDispatchThread.runJob(new Runnable() {
793 @Override
794 public void run() {
795 mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder)
796 .sendToTarget();
797 }
798 });
Angus Kong9ef99252013-07-18 18:04:19 -0700799 }
800
801 @Override
802 public void startPreview() {
Angus Kong62753ae2014-02-10 10:53:54 -0800803 mDispatchThread.runJob(new Runnable() {
804 @Override
805 public void run() {
Erin Dahlgrend05374f2014-03-11 16:23:29 -0700806 mCameraHandler.obtainMessage(START_PREVIEW_ASYNC, null).sendToTarget();
807 }
808 });
809 }
810
811 @Override
812 public void startPreviewWithCallback(final Handler handler,
813 final CameraStartPreviewCallback cb) {
814 mDispatchThread.runJob(new Runnable() {
815 @Override
816 public void run() {
817 mCameraHandler.obtainMessage(START_PREVIEW_ASYNC,
818 CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget();
Angus Kong62753ae2014-02-10 10:53:54 -0800819 }
820 });
Angus Kong9ef99252013-07-18 18:04:19 -0700821 }
822
823 @Override
824 public void stopPreview() {
Angus Kong62753ae2014-02-10 10:53:54 -0800825 final WaitDoneBundle bundle = new WaitDoneBundle();
826 mDispatchThread.runJobSync(new Runnable() {
827 @Override
828 public void run() {
829 mCameraHandler.sendEmptyMessage(STOP_PREVIEW);
830 mCameraHandler.post(bundle.mUnlockRunnable);
831 }
832 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS);
Angus Kong9ef99252013-07-18 18:04:19 -0700833 }
834
835 @Override
836 public void setPreviewDataCallback(
Angus Kong62753ae2014-02-10 10:53:54 -0800837 final Handler handler, final CameraPreviewDataCallback cb) {
838 mDispatchThread.runJob(new Runnable() {
839 @Override
840 public void run() {
841 mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK, PreviewCallbackForward
842 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
843 .sendToTarget();
844 }
845 });
Angus Kong9ef99252013-07-18 18:04:19 -0700846 }
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800847 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800848 public void setOneShotPreviewCallback(final Handler handler,
849 final CameraPreviewDataCallback cb) {
850 mDispatchThread.runJob(new Runnable() {
851 @Override
852 public void run() {
853 mCameraHandler.obtainMessage(SET_ONE_SHOT_PREVIEW_CALLBACK,
854 PreviewCallbackForward
855 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
856 .sendToTarget();
857 }
858 });
Doris Liu4d4a4bc2013-12-19 18:55:54 -0800859 }
Angus Kong9ef99252013-07-18 18:04:19 -0700860
861 @Override
862 public void setPreviewDataCallbackWithBuffer(
Angus Kong62753ae2014-02-10 10:53:54 -0800863 final Handler handler, final CameraPreviewDataCallback cb) {
864 mDispatchThread.runJob(new Runnable() {
865 @Override
866 public void run() {
867 mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK_WITH_BUFFER,
868 PreviewCallbackForward
869 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
870 .sendToTarget();
871 }
872 });
Angus Kong9ef99252013-07-18 18:04:19 -0700873 }
874
875 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800876 public void addCallbackBuffer(final byte[] callbackBuffer) {
877 mDispatchThread.runJob(new Runnable() {
878 @Override
879 public void run() {
880 mCameraHandler.obtainMessage(ADD_CALLBACK_BUFFER, callbackBuffer)
881 .sendToTarget();
882 }
883 });
Angus Kong9ef99252013-07-18 18:04:19 -0700884 }
885
886 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800887 public void autoFocus(final Handler handler, final CameraAFCallback cb) {
888 final AutoFocusCallback afCallback = new AutoFocusCallback() {
889 @Override
890 public void onAutoFocus(final boolean b, Camera camera) {
891 if (mCameraState.getState() != CAMERA_FOCUSING) {
892 Log.w(TAG, "onAutoFocus callback returning when not focusing");
893 } else {
894 mCameraState.setState(CAMERA_IDLE);
895 }
896 handler.post(new Runnable() {
897 @Override
898 public void run() {
899 cb.onAutoFocus(b, AndroidCameraProxyImpl.this);
900 }
901 });
902 }
903 };
904 mDispatchThread.runJob(new Runnable() {
905 @Override
906 public void run() {
907 mCameraState.waitForStates(CAMERA_IDLE);
908 mCameraHandler.obtainMessage(AUTO_FOCUS, afCallback).sendToTarget();
909 }
910 });
Angus Kong9ef99252013-07-18 18:04:19 -0700911 }
912
913 @Override
914 public void cancelAutoFocus() {
Angus Kong62753ae2014-02-10 10:53:54 -0800915 mDispatchThread.runJob(new Runnable() {
916 @Override
917 public void run() {
918 mCameraHandler.removeMessages(AUTO_FOCUS);
919 mCameraHandler.sendEmptyMessage(CANCEL_AUTO_FOCUS);
920 }
921 });
Angus Kong9ef99252013-07-18 18:04:19 -0700922 }
923
Sascha Haeberling638e6f02013-09-18 14:28:51 -0700924 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Angus Kong9ef99252013-07-18 18:04:19 -0700925 @Override
926 public void setAutoFocusMoveCallback(
Angus Kong62753ae2014-02-10 10:53:54 -0800927 final Handler handler, final CameraAFMoveCallback cb) {
928 mDispatchThread.runJob(new Runnable() {
929 @Override
930 public void run() {
931 mCameraHandler.obtainMessage(SET_AUTO_FOCUS_MOVE_CALLBACK, AFMoveCallbackForward
932 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
933 .sendToTarget();
934 }
935 });
Angus Kong9ef99252013-07-18 18:04:19 -0700936 }
937
938 @Override
939 public void takePicture(
Angus Kong62753ae2014-02-10 10:53:54 -0800940 final Handler handler, final CameraShutterCallback shutter,
941 final CameraPictureCallback raw, final CameraPictureCallback post,
942 final CameraPictureCallback jpeg) {
943 final PictureCallback jpegCallback = new PictureCallback() {
944 @Override
945 public void onPictureTaken(final byte[] data, Camera camera) {
946 if (mCameraState.getState() != CAMERA_CAPTURING) {
947 Log.v(TAG, "picture callback returning when not capturing");
948 } else {
949 mCameraState.setState(CAMERA_IDLE);
950 }
951 handler.post(new Runnable() {
952 @Override
953 public void run() {
954 jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this);
955 }
956 });
957 }
958 };
959
960 mDispatchThread.runJob(new Runnable() {
961 @Override
962 public void run() {
963 mCameraState.waitForStates(CAMERA_IDLE);
964 mCameraHandler.requestTakePicture(ShutterCallbackForward
965 .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter),
966 PictureCallbackForward
967 .getNewInstance(handler, AndroidCameraProxyImpl.this, raw),
968 PictureCallbackForward
969 .getNewInstance(handler, AndroidCameraProxyImpl.this, post),
970 jpegCallback);
971 }
972 });
Angus Kong9ef99252013-07-18 18:04:19 -0700973 }
974
975 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800976 public void setDisplayOrientation(final int degrees) {
977 mDispatchThread.runJob(new Runnable() {
978 @Override
979 public void run() {
980 mCameraHandler.obtainMessage(SET_DISPLAY_ORIENTATION, degrees, 0)
981 .sendToTarget();
982 }
983 });
Angus Kong9ef99252013-07-18 18:04:19 -0700984 }
985
986 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800987 public void setZoomChangeListener(final OnZoomChangeListener listener) {
988 mDispatchThread.runJob(new Runnable() {
989 @Override
990 public void run() {
991 mCameraHandler.obtainMessage(SET_ZOOM_CHANGE_LISTENER, listener).sendToTarget();
992 }
993 });
Angus Kong9ef99252013-07-18 18:04:19 -0700994 }
995
Sascha Haeberling58501152014-01-06 11:02:35 -0800996 @Override
Angus Kong62753ae2014-02-10 10:53:54 -0800997 public void setFaceDetectionCallback(final Handler handler,
998 final CameraFaceDetectionCallback cb) {
999 mDispatchThread.runJob(new Runnable() {
1000 @Override
1001 public void run() {
1002 mCameraHandler.obtainMessage(SET_FACE_DETECTION_LISTENER,
1003 FaceDetectionCallbackForward
1004 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb))
1005 .sendToTarget();
1006 }
1007 });
Angus Kong9ef99252013-07-18 18:04:19 -07001008 }
1009
1010 @Override
1011 public void startFaceDetection() {
Angus Kong62753ae2014-02-10 10:53:54 -08001012 mDispatchThread.runJob(new Runnable() {
1013 @Override
1014 public void run() {
1015 mCameraHandler.sendEmptyMessage(START_FACE_DETECTION);
1016 }
1017 });
Angus Kong9ef99252013-07-18 18:04:19 -07001018 }
1019
1020 @Override
1021 public void stopFaceDetection() {
Angus Kong62753ae2014-02-10 10:53:54 -08001022 mDispatchThread.runJob(new Runnable() {
1023 @Override
1024 public void run() {
1025 mCameraHandler.sendEmptyMessage(STOP_FACE_DETECTION);
1026 }
1027 });
Angus Kong9ef99252013-07-18 18:04:19 -07001028 }
1029
1030 @Override
Angus Kong2bca2102014-03-11 16:27:30 -07001031 public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) {
Angus Kong62753ae2014-02-10 10:53:54 -08001032 mDispatchThread.runJob(new Runnable() {
1033 @Override
1034 public void run() {
Angus Kong2bca2102014-03-11 16:27:30 -07001035 mCameraHandler.obtainMessage(SET_ERROR_CALLBACK, ErrorCallbackForward
1036 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)
1037 ).sendToTarget();
Angus Kong62753ae2014-02-10 10:53:54 -08001038 }
1039 });
Angus Kong9ef99252013-07-18 18:04:19 -07001040 }
1041
1042 @Override
Angus Kong62753ae2014-02-10 10:53:54 -08001043 public void setParameters(final Parameters params) {
Angus Kong9ef99252013-07-18 18:04:19 -07001044 if (params == null) {
1045 Log.v(TAG, "null parameters in setParameters()");
1046 return;
1047 }
Angus Kong723cfb92014-03-04 13:59:28 -08001048 final String flattenedParameters = params.flatten();
Angus Kong62753ae2014-02-10 10:53:54 -08001049 mDispatchThread.runJob(new Runnable() {
1050 @Override
1051 public void run() {
1052 mCameraState.waitForStates(CAMERA_IDLE | CAMERA_UNLOCKED);
Angus Kong723cfb92014-03-04 13:59:28 -08001053 mCameraHandler.obtainMessage(SET_PARAMETERS, flattenedParameters).sendToTarget();
Angus Kong62753ae2014-02-10 10:53:54 -08001054 }
1055 });
Angus Kong9ef99252013-07-18 18:04:19 -07001056 }
1057
1058 @Override
1059 public Parameters getParameters() {
Angus Kong62753ae2014-02-10 10:53:54 -08001060 final WaitDoneBundle bundle = new WaitDoneBundle();
1061 mDispatchThread.runJobSync(new Runnable() {
1062 @Override
1063 public void run() {
1064 mCameraHandler.sendEmptyMessage(GET_PARAMETERS);
1065 mCameraHandler.post(bundle.mUnlockRunnable);
1066 }
1067 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS);
Angus Kong9ef99252013-07-18 18:04:19 -07001068 return mParameters;
1069 }
1070
1071 @Override
1072 public void refreshParameters() {
Angus Kong62753ae2014-02-10 10:53:54 -08001073 mDispatchThread.runJob(new Runnable() {
1074 @Override
1075 public void run() {
1076 mCameraHandler.sendEmptyMessage(REFRESH_PARAMETERS);
1077 }
1078 });
Angus Kong9ef99252013-07-18 18:04:19 -07001079 }
1080
1081 @Override
Angus Kong62753ae2014-02-10 10:53:54 -08001082 public void enableShutterSound(final boolean enable) {
1083 mDispatchThread.runJob(new Runnable() {
1084 @Override
1085 public void run() {
1086 mCameraHandler.obtainMessage(ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0)
1087 .sendToTarget();
1088 }
1089 });
1090 }
1091
1092 }
1093
1094 private static class WaitDoneBundle {
1095 public Runnable mUnlockRunnable;
1096 private Object mWaitLock;
1097
1098 WaitDoneBundle() {
1099 mWaitLock = new Object();
1100 mUnlockRunnable = new Runnable() {
1101 @Override
1102 public void run() {
1103 synchronized (mWaitLock) {
1104 mWaitLock.notifyAll();
1105 }
1106 }
1107 };
Angus Kong9ef99252013-07-18 18:04:19 -07001108 }
1109 }
1110
1111 /**
1112 * A helper class to forward AutoFocusCallback to another thread.
1113 */
1114 private static class AFCallbackForward implements AutoFocusCallback {
1115 private final Handler mHandler;
1116 private final CameraProxy mCamera;
1117 private final CameraAFCallback mCallback;
1118
1119 /**
Angus Kong9e765522013-07-31 14:05:20 -07001120 * Returns a new instance of {@link AFCallbackForward}.
Angus Kong9ef99252013-07-18 18:04:19 -07001121 *
1122 * @param handler The handler in which the callback will be invoked in.
1123 * @param camera The {@link CameraProxy} which the callback is from.
1124 * @param cb The callback to be invoked.
1125 * @return The instance of the {@link AFCallbackForward},
Angus Kong9e765522013-07-31 14:05:20 -07001126 * or null if any parameter is null.
Angus Kong9ef99252013-07-18 18:04:19 -07001127 */
Angus Kong9e765522013-07-31 14:05:20 -07001128 public static AFCallbackForward getNewInstance(
Angus Kong9ef99252013-07-18 18:04:19 -07001129 Handler handler, CameraProxy camera, CameraAFCallback cb) {
1130 if (handler == null || camera == null || cb == null) return null;
1131 return new AFCallbackForward(handler, camera, cb);
1132 }
1133
1134 private AFCallbackForward(
1135 Handler h, CameraProxy camera, CameraAFCallback cb) {
1136 mHandler = h;
1137 mCamera = camera;
1138 mCallback = cb;
1139 }
1140
1141 @Override
1142 public void onAutoFocus(final boolean b, Camera camera) {
1143 mHandler.post(new Runnable() {
1144 @Override
1145 public void run() {
1146 mCallback.onAutoFocus(b, mCamera);
1147 }
1148 });
1149 }
1150 }
1151
Angus Kong2bca2102014-03-11 16:27:30 -07001152 /**
1153 * A helper class to forward ErrorCallback to another thread.
1154 */
1155 private static class ErrorCallbackForward implements Camera.ErrorCallback {
1156 private final Handler mHandler;
1157 private final CameraProxy mCamera;
1158 private final CameraErrorCallback mCallback;
1159
1160 /**
1161 * Returns a new instance of {@link AFCallbackForward}.
1162 *
1163 * @param handler The handler in which the callback will be invoked in.
1164 * @param camera The {@link CameraProxy} which the callback is from.
1165 * @param cb The callback to be invoked.
1166 * @return The instance of the {@link AFCallbackForward},
1167 * or null if any parameter is null.
1168 */
1169 public static ErrorCallbackForward getNewInstance(
1170 Handler handler, CameraProxy camera, CameraErrorCallback cb) {
1171 if (handler == null || camera == null || cb == null) return null;
1172 return new ErrorCallbackForward(handler, camera, cb);
1173 }
1174
1175 private ErrorCallbackForward(
1176 Handler h, CameraProxy camera, CameraErrorCallback cb) {
1177 mHandler = h;
1178 mCamera = camera;
1179 mCallback = cb;
1180 }
1181
1182 @Override
1183 public void onError(final int error, Camera camera) {
1184 mHandler.post(new Runnable() {
1185 @Override
1186 public void run() {
1187 mCallback.onError(error, mCamera);
1188 }
1189 });
1190 }
1191 }
1192
Angus Kong9ef99252013-07-18 18:04:19 -07001193 /** A helper class to forward AutoFocusMoveCallback to another thread. */
Sascha Haeberling638e6f02013-09-18 14:28:51 -07001194 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
Angus Kong9ef99252013-07-18 18:04:19 -07001195 private static class AFMoveCallbackForward implements AutoFocusMoveCallback {
1196 private final Handler mHandler;
1197 private final CameraAFMoveCallback mCallback;
1198 private final CameraProxy mCamera;
1199
1200 /**
Angus Kong9e765522013-07-31 14:05:20 -07001201 * Returns a new instance of {@link AFMoveCallbackForward}.
Angus Kong9ef99252013-07-18 18:04:19 -07001202 *
1203 * @param handler The handler in which the callback will be invoked in.
1204 * @param camera The {@link CameraProxy} which the callback is from.
1205 * @param cb The callback to be invoked.
1206 * @return The instance of the {@link AFMoveCallbackForward},
Angus Kong9e765522013-07-31 14:05:20 -07001207 * or null if any parameter is null.
Angus Kong9ef99252013-07-18 18:04:19 -07001208 */
Angus Kong9e765522013-07-31 14:05:20 -07001209 public static AFMoveCallbackForward getNewInstance(
Angus Kong9ef99252013-07-18 18:04:19 -07001210 Handler handler, CameraProxy camera, CameraAFMoveCallback cb) {
1211 if (handler == null || camera == null || cb == null) return null;
1212 return new AFMoveCallbackForward(handler, camera, cb);
1213 }
1214
1215 private AFMoveCallbackForward(
1216 Handler h, CameraProxy camera, CameraAFMoveCallback cb) {
1217 mHandler = h;
1218 mCamera = camera;
1219 mCallback = cb;
1220 }
1221
1222 @Override
1223 public void onAutoFocusMoving(
1224 final boolean moving, android.hardware.Camera camera) {
1225 mHandler.post(new Runnable() {
1226 @Override
1227 public void run() {
1228 mCallback.onAutoFocusMoving(moving, mCamera);
1229 }
1230 });
1231 }
1232 }
1233
1234 /**
1235 * A helper class to forward ShutterCallback to to another thread.
1236 */
1237 private static class ShutterCallbackForward implements ShutterCallback {
1238 private final Handler mHandler;
1239 private final CameraShutterCallback mCallback;
1240 private final CameraProxy mCamera;
1241
1242 /**
Angus Kong9e765522013-07-31 14:05:20 -07001243 * Returns a new instance of {@link ShutterCallbackForward}.
Angus Kong9ef99252013-07-18 18:04:19 -07001244 *
1245 * @param handler The handler in which the callback will be invoked in.
1246 * @param camera The {@link CameraProxy} which the callback is from.
1247 * @param cb The callback to be invoked.
1248 * @return The instance of the {@link ShutterCallbackForward},
Angus Kong9e765522013-07-31 14:05:20 -07001249 * or null if any parameter is null.
Angus Kong9ef99252013-07-18 18:04:19 -07001250 */
Angus Kong9e765522013-07-31 14:05:20 -07001251 public static ShutterCallbackForward getNewInstance(
Angus Kong9ef99252013-07-18 18:04:19 -07001252 Handler handler, CameraProxy camera, CameraShutterCallback cb) {
1253 if (handler == null || camera == null || cb == null) return null;
1254 return new ShutterCallbackForward(handler, camera, cb);
1255 }
1256
1257 private ShutterCallbackForward(
1258 Handler h, CameraProxy camera, CameraShutterCallback cb) {
1259 mHandler = h;
1260 mCamera = camera;
1261 mCallback = cb;
1262 }
1263
1264 @Override
1265 public void onShutter() {
1266 mHandler.post(new Runnable() {
1267 @Override
1268 public void run() {
1269 mCallback.onShutter(mCamera);
1270 }
1271 });
1272 }
1273 }
1274
1275 /**
1276 * A helper class to forward PictureCallback to another thread.
1277 */
1278 private static class PictureCallbackForward implements PictureCallback {
1279 private final Handler mHandler;
1280 private final CameraPictureCallback mCallback;
1281 private final CameraProxy mCamera;
1282
1283 /**
Angus Kong9e765522013-07-31 14:05:20 -07001284 * Returns a new instance of {@link PictureCallbackForward}.
Angus Kong9ef99252013-07-18 18:04:19 -07001285 *
1286 * @param handler The handler in which the callback will be invoked in.
1287 * @param camera The {@link CameraProxy} which the callback is from.
1288 * @param cb The callback to be invoked.
1289 * @return The instance of the {@link PictureCallbackForward},
Angus Kong9e765522013-07-31 14:05:20 -07001290 * or null if any parameters is null.
Angus Kong9ef99252013-07-18 18:04:19 -07001291 */
Angus Kong9e765522013-07-31 14:05:20 -07001292 public static PictureCallbackForward getNewInstance(
Angus Kong9ef99252013-07-18 18:04:19 -07001293 Handler handler, CameraProxy camera, CameraPictureCallback cb) {
1294 if (handler == null || camera == null || cb == null) return null;
1295 return new PictureCallbackForward(handler, camera, cb);
1296 }
1297
1298 private PictureCallbackForward(
1299 Handler h, CameraProxy camera, CameraPictureCallback cb) {
1300 mHandler = h;
1301 mCamera = camera;
1302 mCallback = cb;
1303 }
1304
1305 @Override
1306 public void onPictureTaken(
1307 final byte[] data, android.hardware.Camera camera) {
1308 mHandler.post(new Runnable() {
1309 @Override
1310 public void run() {
1311 mCallback.onPictureTaken(data, mCamera);
1312 }
1313 });
1314 }
1315 }
1316
1317 /**
1318 * A helper class to forward PreviewCallback to another thread.
1319 */
1320 private static class PreviewCallbackForward implements PreviewCallback {
1321 private final Handler mHandler;
1322 private final CameraPreviewDataCallback mCallback;
1323 private final CameraProxy mCamera;
1324
1325 /**
Angus Kong9e765522013-07-31 14:05:20 -07001326 * Returns a new instance of {@link PreviewCallbackForward}.
Angus Kong9ef99252013-07-18 18:04:19 -07001327 *
1328 * @param handler The handler in which the callback will be invoked in.
1329 * @param camera The {@link CameraProxy} which the callback is from.
1330 * @param cb The callback to be invoked.
Angus Kong9e765522013-07-31 14:05:20 -07001331 * @return The instance of the {@link PreviewCallbackForward},
1332 * or null if any parameters is null.
Angus Kong9ef99252013-07-18 18:04:19 -07001333 */
Angus Kong9e765522013-07-31 14:05:20 -07001334 public static PreviewCallbackForward getNewInstance(
Angus Kong9ef99252013-07-18 18:04:19 -07001335 Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) {
1336 if (handler == null || camera == null || cb == null) return null;
1337 return new PreviewCallbackForward(handler, camera, cb);
1338 }
1339
1340 private PreviewCallbackForward(
1341 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) {
1342 mHandler = h;
1343 mCamera = camera;
1344 mCallback = cb;
1345 }
1346
1347 @Override
1348 public void onPreviewFrame(
1349 final byte[] data, android.hardware.Camera camera) {
1350 mHandler.post(new Runnable() {
1351 @Override
1352 public void run() {
1353 mCallback.onPreviewFrame(data, mCamera);
1354 }
1355 });
1356 }
1357 }
Angus Kong9e765522013-07-31 14:05:20 -07001358
1359 private static class FaceDetectionCallbackForward implements FaceDetectionListener {
1360 private final Handler mHandler;
1361 private final CameraFaceDetectionCallback mCallback;
1362 private final CameraProxy mCamera;
1363
1364 /**
1365 * Returns a new instance of {@link FaceDetectionCallbackForward}.
1366 *
1367 * @param handler The handler in which the callback will be invoked in.
1368 * @param camera The {@link CameraProxy} which the callback is from.
1369 * @param cb The callback to be invoked.
1370 * @return The instance of the {@link FaceDetectionCallbackForward},
1371 * or null if any parameter is null.
1372 */
1373 public static FaceDetectionCallbackForward getNewInstance(
1374 Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) {
1375 if (handler == null || camera == null || cb == null) return null;
1376 return new FaceDetectionCallbackForward(handler, camera, cb);
1377 }
1378
1379 private FaceDetectionCallbackForward(
1380 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) {
1381 mHandler = h;
1382 mCamera = camera;
1383 mCallback = cb;
1384 }
1385
1386 @Override
1387 public void onFaceDetection(
1388 final Camera.Face[] faces, Camera camera) {
1389 mHandler.post(new Runnable() {
1390 @Override
1391 public void run() {
1392 mCallback.onFaceDetection(faces, mCamera);
1393 }
1394 });
1395 }
1396 }
Angus Kong4f795b82013-09-16 14:25:35 -07001397
1398 /**
1399 * A callback helps to invoke the original callback on another
1400 * {@link android.os.Handler}.
1401 */
Angus Kong62848152013-11-08 17:25:29 -08001402 private static class CameraOpenCallbackForward implements CameraOpenCallback {
Angus Kong4f795b82013-09-16 14:25:35 -07001403 private final Handler mHandler;
Angus Kong62848152013-11-08 17:25:29 -08001404 private final CameraOpenCallback mCallback;
Angus Kong4f795b82013-09-16 14:25:35 -07001405
1406 /**
1407 * Returns a new instance of {@link FaceDetectionCallbackForward}.
1408 *
1409 * @param handler The handler in which the callback will be invoked in.
1410 * @param cb The callback to be invoked.
1411 * @return The instance of the {@link FaceDetectionCallbackForward}, or
1412 * null if any parameter is null.
1413 */
Angus Kong62848152013-11-08 17:25:29 -08001414 public static CameraOpenCallbackForward getNewInstance(
1415 Handler handler, CameraOpenCallback cb) {
Angus Kong4f795b82013-09-16 14:25:35 -07001416 if (handler == null || cb == null) {
1417 return null;
1418 }
Angus Kong62848152013-11-08 17:25:29 -08001419 return new CameraOpenCallbackForward(handler, cb);
Angus Kong4f795b82013-09-16 14:25:35 -07001420 }
1421
Angus Kong62848152013-11-08 17:25:29 -08001422 private CameraOpenCallbackForward(Handler h, CameraOpenCallback cb) {
ztenghuie1aa59b2013-10-25 14:56:32 -07001423 // Given that we are using the main thread handler, we can create it
1424 // here instead of holding onto the PhotoModule objects. In this
1425 // way, we can avoid memory leak.
1426 mHandler = new Handler(Looper.getMainLooper());
Angus Kong4f795b82013-09-16 14:25:35 -07001427 mCallback = cb;
1428 }
1429
1430 @Override
Angus Kong62848152013-11-08 17:25:29 -08001431 public void onCameraOpened(final CameraProxy camera) {
1432 mHandler.post(new Runnable() {
1433 @Override
1434 public void run() {
1435 mCallback.onCameraOpened(camera);
1436 }
1437 });
1438 }
1439
1440 @Override
Angus Kong4f795b82013-09-16 14:25:35 -07001441 public void onCameraDisabled(final int cameraId) {
1442 mHandler.post(new Runnable() {
1443 @Override
1444 public void run() {
1445 mCallback.onCameraDisabled(cameraId);
1446 }
1447 });
1448 }
1449
1450 @Override
1451 public void onDeviceOpenFailure(final int cameraId) {
1452 mHandler.post(new Runnable() {
1453 @Override
1454 public void run() {
1455 mCallback.onDeviceOpenFailure(cameraId);
1456 }
1457 });
1458 }
1459
1460 @Override
Angus Kong62753ae2014-02-10 10:53:54 -08001461 public void onDeviceOpenedAlready(final int cameraId) {
1462 mHandler.post(new Runnable() {
1463 @Override
1464 public void run() {
1465 mCallback.onDeviceOpenedAlready(cameraId);
1466 }
1467 });
1468 }
1469
1470 @Override
Angus Kong4f795b82013-09-16 14:25:35 -07001471 public void onReconnectionFailure(final CameraManager mgr) {
1472 mHandler.post(new Runnable() {
1473 @Override
1474 public void run() {
1475 mCallback.onReconnectionFailure(mgr);
1476 }
1477 });
1478 }
1479 }
Erin Dahlgrend05374f2014-03-11 16:23:29 -07001480
1481 private static class CameraStartPreviewCallbackForward
1482 implements CameraStartPreviewCallback {
1483 private final Handler mHandler;
1484 private final CameraStartPreviewCallback mCallback;
1485
1486 public static CameraStartPreviewCallbackForward getNewInstance(
1487 Handler handler, CameraStartPreviewCallback cb) {
1488 if (handler == null || cb == null) {
1489 return null;
1490 }
1491 return new CameraStartPreviewCallbackForward(handler, cb);
1492 }
1493
1494 private CameraStartPreviewCallbackForward(Handler h,
1495 CameraStartPreviewCallback cb) {
1496 mHandler = h;
1497 mCallback = cb;
1498 }
1499
1500 @Override
1501 public void onPreviewStarted() {
1502 mHandler.post(new Runnable() {
1503 @Override
1504 public void run() {
1505 mCallback.onPreviewStarted();
1506 }
1507 });
1508 }
1509 }
Angus Kong9ef99252013-07-18 18:04:19 -07001510}