AndroidCameraAgentImpl.java revision b30d2c670f1262f0d60181e40dad33f2151fee4a
1/* 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 17package com.android.ex.camera2.portability; 18 19import 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; 31import android.os.Build; 32import android.os.Handler; 33import android.os.HandlerThread; 34import android.os.Looper; 35import android.os.Message; 36import android.view.SurfaceHolder; 37 38import com.android.ex.camera2.portability.debug.Log; 39 40import java.io.IOException; 41import java.util.StringTokenizer; 42 43/** 44 * A class to implement {@link CameraAgent} of the Android camera framework. 45 */ 46class AndroidCameraAgentImpl implements CameraAgent { 47 private static final Log.Tag TAG = new Log.Tag("AndroidCamMgrImpl"); 48 49 private Parameters mParameters; 50 private boolean mParametersIsDirty; 51 private CameraDeviceInfo.Characteristics mCharacteristics; 52 private AndroidCameraCapabilities mCapabilities; 53 54 private final CameraHandler mCameraHandler; 55 private final HandlerThread mCameraHandlerThread; 56 private final CameraStateHolder mCameraState; 57 private final DispatchThread mDispatchThread; 58 59 private Handler mCameraExceptionCallbackHandler; 60 private CameraExceptionCallback mCameraExceptionCallback = 61 new CameraExceptionCallback() { 62 @Override 63 public void onCameraException(RuntimeException e) { 64 throw e; 65 } 66 }; 67 68 AndroidCameraAgentImpl() { 69 mCameraHandlerThread = new HandlerThread("Camera Handler Thread"); 70 mCameraHandlerThread.start(); 71 mCameraHandler = new CameraHandler(mCameraHandlerThread.getLooper()); 72 mCameraExceptionCallbackHandler = mCameraHandler; 73 mCameraState = new CameraStateHolder(); 74 mDispatchThread = new DispatchThread(mCameraHandler, mCameraHandlerThread); 75 mDispatchThread.start(); 76 } 77 78 @Override 79 public void setCameraDefaultExceptionCallback(CameraExceptionCallback callback, 80 Handler handler) { 81 synchronized (mCameraExceptionCallback) { 82 mCameraExceptionCallback = callback; 83 mCameraExceptionCallbackHandler = handler; 84 } 85 } 86 87 @Override 88 public void recycle() { 89 closeCamera(null, true); 90 mDispatchThread.end(); 91 } 92 93 @Override 94 public CameraDeviceInfo getCameraDeviceInfo() { 95 return AndroidCameraDeviceInfo.create(); 96 } 97 98 private static class AndroidCameraDeviceInfo implements CameraDeviceInfo { 99 private final Camera.CameraInfo[] mCameraInfos; 100 private final int mNumberOfCameras; 101 private final int mFirstBackCameraId; 102 private final int mFirstFrontCameraId; 103 104 private AndroidCameraDeviceInfo(Camera.CameraInfo[] info, int numberOfCameras, 105 int firstBackCameraId, int firstFrontCameraId) { 106 107 mCameraInfos = info; 108 mNumberOfCameras = numberOfCameras; 109 mFirstBackCameraId = firstBackCameraId; 110 mFirstFrontCameraId = firstFrontCameraId; 111 } 112 113 public static AndroidCameraDeviceInfo create() { 114 int numberOfCameras; 115 Camera.CameraInfo[] cameraInfos; 116 try { 117 numberOfCameras = Camera.getNumberOfCameras(); 118 cameraInfos = new Camera.CameraInfo[numberOfCameras]; 119 for (int i = 0; i < numberOfCameras; i++) { 120 cameraInfos[i] = new Camera.CameraInfo(); 121 Camera.getCameraInfo(i, cameraInfos[i]); 122 } 123 } catch (RuntimeException ex) { 124 return null; 125 } 126 127 int firstFront = NO_DEVICE; 128 int firstBack = NO_DEVICE; 129 // Get the first (smallest) back and first front camera id. 130 for (int i = numberOfCameras - 1; i >= 0; i--) { 131 if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_BACK) { 132 firstBack = i; 133 } else { 134 if (cameraInfos[i].facing == Camera.CameraInfo.CAMERA_FACING_FRONT) { 135 firstFront = i; 136 } 137 } 138 } 139 140 return new AndroidCameraDeviceInfo(cameraInfos, numberOfCameras, firstBack, firstFront); 141 } 142 143 @Override 144 public Characteristics getCharacteristics(int cameraId) { 145 Camera.CameraInfo info = mCameraInfos[cameraId]; 146 if (info != null) { 147 return new AndroidCharacteristics(info); 148 } else { 149 return null; 150 } 151 } 152 153 @Override 154 public int getNumberOfCameras() { 155 return mNumberOfCameras; 156 } 157 158 @Override 159 public int getFirstBackCameraId() { 160 return mFirstBackCameraId; 161 } 162 163 @Override 164 public int getFirstFrontCameraId() { 165 return mFirstFrontCameraId; 166 } 167 168 private static class AndroidCharacteristics implements Characteristics { 169 private Camera.CameraInfo mCameraInfo; 170 171 AndroidCharacteristics(Camera.CameraInfo cameraInfo) { 172 mCameraInfo = cameraInfo; 173 } 174 175 @Override 176 public boolean isFacingBack() { 177 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK; 178 } 179 180 @Override 181 public boolean isFacingFront() { 182 return mCameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT; 183 } 184 185 @Override 186 public int getSensorOrientation() { 187 return mCameraInfo.orientation; 188 } 189 190 @Override 191 public boolean canDisableShutterSound() { 192 return mCameraInfo.canDisableShutterSound; 193 } 194 } 195 } 196 197 /** 198 * The handler on which the actual camera operations happen. 199 */ 200 private class CameraHandler extends HistoryHandler { 201 202 // Used to retain a copy of Parameters for setting parameters. 203 private Parameters mParamsToSet; 204 private Camera mCamera; 205 private int mCameraId; 206 207 private class CaptureCallbacks { 208 public final ShutterCallback mShutter; 209 public final PictureCallback mRaw; 210 public final PictureCallback mPostView; 211 public final PictureCallback mJpeg; 212 213 CaptureCallbacks(ShutterCallback shutter, PictureCallback raw, PictureCallback postView, 214 PictureCallback jpeg) { 215 mShutter = shutter; 216 mRaw = raw; 217 mPostView = postView; 218 mJpeg = jpeg; 219 } 220 } 221 222 CameraHandler(Looper looper) { 223 super(looper); 224 } 225 226 private void startFaceDetection() { 227 mCamera.startFaceDetection(); 228 } 229 230 private void stopFaceDetection() { 231 mCamera.stopFaceDetection(); 232 } 233 234 private void setFaceDetectionListener(FaceDetectionListener listener) { 235 mCamera.setFaceDetectionListener(listener); 236 } 237 238 private void setPreviewTexture(Object surfaceTexture) { 239 try { 240 mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture); 241 } catch (IOException e) { 242 Log.e(TAG, "Could not set preview texture", e); 243 } 244 } 245 246 @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) 247 private void enableShutterSound(boolean enable) { 248 mCamera.enableShutterSound(enable); 249 } 250 251 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 252 private void setAutoFocusMoveCallback( 253 android.hardware.Camera camera, Object cb) { 254 try { 255 camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb); 256 } catch (RuntimeException ex) { 257 Log.w(TAG, ex.getMessage()); 258 } 259 } 260 261 private void capture(final CaptureCallbacks cb) { 262 try { 263 mCamera.takePicture(cb.mShutter, cb.mRaw, cb.mPostView, cb.mJpeg); 264 } catch (RuntimeException e) { 265 // TODO: output camera state and focus state for debugging. 266 Log.e(TAG, "take picture failed."); 267 throw e; 268 } 269 } 270 271 public void requestTakePicture( 272 final ShutterCallback shutter, 273 final PictureCallback raw, 274 final PictureCallback postView, 275 final PictureCallback jpeg) { 276 final CaptureCallbacks callbacks = new CaptureCallbacks(shutter, raw, postView, jpeg); 277 obtainMessage(CameraActions.CAPTURE_PHOTO, callbacks).sendToTarget(); 278 } 279 280 /** 281 * This method does not deal with the API level check. Everyone should 282 * check first for supported operations before sending message to this handler. 283 */ 284 @Override 285 public void handleMessage(final Message msg) { 286 super.handleMessage(msg); 287 try { 288 switch (msg.what) { 289 case CameraActions.OPEN_CAMERA: { 290 final CameraOpenCallback openCallback = (CameraOpenCallback) msg.obj; 291 final int cameraId = msg.arg1; 292 if (mCameraState.getState() != CameraStateHolder.CAMERA_UNOPENED) { 293 openCallback.onDeviceOpenedAlready(cameraId, generateHistoryString(cameraId)); 294 break; 295 } 296 297 mCamera = android.hardware.Camera.open(cameraId); 298 if (mCamera != null) { 299 mCameraId = cameraId; 300 mParametersIsDirty = true; 301 302 // Get an instance of Camera.Parameters for later use. 303 mParamsToSet = mCamera.getParameters(); 304 mCharacteristics = 305 AndroidCameraDeviceInfo.create().getCharacteristics(cameraId); 306 mCapabilities = new AndroidCameraCapabilities(mParamsToSet); 307 308 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 309 if (openCallback != null) { 310 openCallback.onCameraOpened( 311 new AndroidCameraProxyImpl(cameraId, mCamera, 312 mCharacteristics, mCapabilities)); 313 } 314 } else { 315 if (openCallback != null) { 316 openCallback.onDeviceOpenFailure(cameraId, generateHistoryString(cameraId)); 317 } 318 } 319 break; 320 } 321 322 case CameraActions.RELEASE: { 323 if (mCamera != null) { 324 mCamera.release(); 325 mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); 326 mCamera = null; 327 } else { 328 Log.w(TAG, "Releasing camera without any camera opened."); 329 } 330 break; 331 } 332 333 case CameraActions.RECONNECT: { 334 final CameraOpenCallbackForward cbForward = 335 (CameraOpenCallbackForward) msg.obj; 336 final int cameraId = msg.arg1; 337 try { 338 mCamera.reconnect(); 339 } catch (IOException ex) { 340 if (cbForward != null) { 341 cbForward.onReconnectionFailure(AndroidCameraAgentImpl.this, 342 generateHistoryString(mCameraId)); 343 } 344 break; 345 } 346 347 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 348 if (cbForward != null) { 349 cbForward.onCameraOpened( 350 new AndroidCameraProxyImpl(cameraId, mCamera, mCharacteristics, 351 mCapabilities)); 352 } 353 break; 354 } 355 356 case CameraActions.UNLOCK: { 357 mCamera.unlock(); 358 mCameraState.setState(CameraStateHolder.CAMERA_UNLOCKED); 359 break; 360 } 361 362 case CameraActions.LOCK: { 363 mCamera.lock(); 364 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 365 break; 366 } 367 368 case CameraActions.SET_PREVIEW_TEXTURE_ASYNC: { 369 setPreviewTexture(msg.obj); 370 break; 371 } 372 373 case CameraActions.SET_PREVIEW_DISPLAY_ASYNC: { 374 try { 375 mCamera.setPreviewDisplay((SurfaceHolder) msg.obj); 376 } catch (IOException e) { 377 throw new RuntimeException(e); 378 } 379 break; 380 } 381 382 case CameraActions.START_PREVIEW_ASYNC: { 383 final CameraStartPreviewCallbackForward cbForward = 384 (CameraStartPreviewCallbackForward) msg.obj; 385 mCamera.startPreview(); 386 if (cbForward != null) { 387 cbForward.onPreviewStarted(); 388 } 389 break; 390 } 391 392 case CameraActions.STOP_PREVIEW: { 393 mCamera.stopPreview(); 394 break; 395 } 396 397 case CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER: { 398 mCamera.setPreviewCallbackWithBuffer((PreviewCallback) msg.obj); 399 break; 400 } 401 402 case CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK: { 403 mCamera.setOneShotPreviewCallback((PreviewCallback) msg.obj); 404 break; 405 } 406 407 case CameraActions.ADD_CALLBACK_BUFFER: { 408 mCamera.addCallbackBuffer((byte[]) msg.obj); 409 break; 410 } 411 412 case CameraActions.AUTO_FOCUS: { 413 mCameraState.setState(CameraStateHolder.CAMERA_FOCUSING); 414 mCamera.autoFocus((AutoFocusCallback) msg.obj); 415 break; 416 } 417 418 case CameraActions.CANCEL_AUTO_FOCUS: { 419 mCamera.cancelAutoFocus(); 420 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 421 break; 422 } 423 424 case CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK: { 425 setAutoFocusMoveCallback(mCamera, msg.obj); 426 break; 427 } 428 429 case CameraActions.SET_DISPLAY_ORIENTATION: { 430 mCamera.setDisplayOrientation(msg.arg1); 431 break; 432 } 433 434 case CameraActions.SET_ZOOM_CHANGE_LISTENER: { 435 mCamera.setZoomChangeListener((OnZoomChangeListener) msg.obj); 436 break; 437 } 438 439 case CameraActions.SET_FACE_DETECTION_LISTENER: { 440 setFaceDetectionListener((FaceDetectionListener) msg.obj); 441 break; 442 } 443 444 case CameraActions.START_FACE_DETECTION: { 445 startFaceDetection(); 446 break; 447 } 448 449 case CameraActions.STOP_FACE_DETECTION: { 450 stopFaceDetection(); 451 break; 452 } 453 454 case CameraActions.SET_ERROR_CALLBACK: { 455 mCamera.setErrorCallback((ErrorCallback) msg.obj); 456 break; 457 } 458 459 case CameraActions.APPLY_SETTINGS: { 460 mParametersIsDirty = true; 461 CameraSettings settings = (CameraSettings) msg.obj; 462 applyToParameters(settings); 463 mCamera.setParameters(mParamsToSet); 464 break; 465 } 466 467 case CameraActions.SET_PARAMETERS: { 468 mParametersIsDirty = true; 469 mParamsToSet.unflatten((String) msg.obj); 470 mCamera.setParameters(mParamsToSet); 471 break; 472 } 473 474 case CameraActions.GET_PARAMETERS: { 475 if (mParametersIsDirty) { 476 mParameters = mCamera.getParameters(); 477 mParametersIsDirty = false; 478 } 479 break; 480 } 481 482 case CameraActions.SET_PREVIEW_CALLBACK: { 483 mCamera.setPreviewCallback((PreviewCallback) msg.obj); 484 break; 485 } 486 487 case CameraActions.ENABLE_SHUTTER_SOUND: { 488 enableShutterSound((msg.arg1 == 1) ? true : false); 489 break; 490 } 491 492 case CameraActions.REFRESH_PARAMETERS: { 493 mParametersIsDirty = true; 494 break; 495 } 496 497 case CameraActions.CAPTURE_PHOTO: { 498 mCameraState.setState(CameraStateHolder.CAMERA_CAPTURING); 499 capture((CaptureCallbacks) msg.obj); 500 break; 501 } 502 503 default: { 504 throw new RuntimeException("Invalid CameraProxy message=" + msg.what); 505 } 506 } 507 } catch (final RuntimeException e) { 508 if (msg.what != CameraActions.RELEASE && mCamera != null) { 509 try { 510 mCamera.release(); 511 mCameraState.setState(CameraStateHolder.CAMERA_UNOPENED); 512 } catch (Exception ex) { 513 Log.e(TAG, "Fail to release the camera."); 514 } 515 mCamera = null; 516 } else { 517 if (mCamera == null) { 518 if (msg.what == CameraActions.OPEN_CAMERA) { 519 final int cameraId = msg.arg1; 520 if (msg.obj != null) { 521 ((CameraOpenCallback) msg.obj).onDeviceOpenFailure( 522 msg.arg1, generateHistoryString(cameraId)); 523 } 524 } else { 525 Log.w(TAG, "Cannot handle message " + msg.what + ", mCamera is null."); 526 } 527 return; 528 } 529 } 530 synchronized (mCameraExceptionCallback) { 531 mCameraExceptionCallbackHandler.post(new Runnable() { 532 @Override 533 public void run() { 534 mCameraExceptionCallback.onCameraException(e); 535 } 536 }); 537 } 538 } 539 } 540 541 private void applyToParameters(final CameraSettings settings) { 542 final CameraCapabilities.Stringifier stringifier = mCapabilities.getStringifier(); 543 Size photoSize = settings.getCurrentPhotoSize(); 544 mParamsToSet.setPictureSize(photoSize.width(), photoSize.height()); 545 Size previewSize = settings.getCurrentPreviewSize(); 546 mParamsToSet.setPreviewSize(previewSize.width(), previewSize.height()); 547 if (settings.getPreviewFrameRate() == -1) { 548 mParamsToSet.setPreviewFpsRange(settings.getPreviewFpsRangeMin(), 549 settings.getPreviewFpsRangeMax()); 550 } else { 551 mParamsToSet.setPreviewFrameRate(settings.getPreviewFrameRate()); 552 } 553 mParamsToSet.setPreviewFormat(settings.getCurrentPreviewFormat()); 554 mParamsToSet.setJpegQuality(settings.getPhotoJpegCompressionQuality()); 555 if (mCapabilities.supports(CameraCapabilities.Feature.ZOOM)) { 556 // Should use settings.getCurrentZoomRatio() instead here. 557 mParamsToSet.setZoom(settings.getCurrentZoomIndex()); 558 } 559 mParamsToSet.setRotation((int) settings.getCurrentPhotoRotationDegrees()); 560 mParamsToSet.setExposureCompensation(settings.getExposureCompensationIndex()); 561 if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_EXPOSURE_LOCK)) { 562 mParamsToSet.setAutoExposureLock(settings.isAutoExposureLocked()); 563 } 564 mParamsToSet.setFocusMode(stringifier.stringify(settings.getCurrentFocusMode())); 565 if (mCapabilities.supports(CameraCapabilities.Feature.AUTO_WHITE_BALANCE_LOCK)) { 566 mParamsToSet.setAutoWhiteBalanceLock(settings.isAutoWhiteBalanceLocked()); 567 } 568 if (mCapabilities.supports(CameraCapabilities.Feature.FOCUS_AREA)) { 569 if (settings.getFocusAreas().size() != 0) { 570 mParamsToSet.setFocusAreas(settings.getFocusAreas()); 571 } 572 } 573 if (mCapabilities.supports(CameraCapabilities.Feature.METERING_AREA)) { 574 if (settings.getMeteringAreas().size() != 0) { 575 mParamsToSet.setMeteringAreas(settings.getMeteringAreas()); 576 } 577 } 578 if (settings.getCurrentFlashMode() != CameraCapabilities.FlashMode.NO_FLASH) { 579 mParamsToSet.setFlashMode(stringifier.stringify(settings.getCurrentFlashMode())); 580 } 581 if (settings.getCurrentSceneMode() != CameraCapabilities.SceneMode.NO_SCENE_MODE) { 582 if (settings.getCurrentSceneMode() != null) { 583 mParamsToSet 584 .setSceneMode(stringifier.stringify(settings.getCurrentSceneMode())); 585 } 586 } 587 mParamsToSet.setRecordingHint(settings.isRecordingHintEnabled()); 588 Size jpegThumbSize = settings.getExifThumbnailSize(); 589 mParamsToSet.setJpegThumbnailSize(jpegThumbSize.width(), jpegThumbSize.height()); 590 mParamsToSet.setPictureFormat(settings.getCurrentPhotoFormat()); 591 592 CameraSettings.GpsData gpsData = settings.getGpsData(); 593 if (gpsData == null) { 594 mParamsToSet.removeGpsData(); 595 } else { 596 mParamsToSet.setGpsTimestamp(gpsData.timeStamp); 597 if (gpsData.processingMethod != null) { 598 // It's a hack since we always use GPS time stamp but does 599 // not use other fields sometimes. Setting processing 600 // method to null means the other fields should not be used. 601 mParamsToSet.setGpsAltitude(gpsData.altitude); 602 mParamsToSet.setGpsLatitude(gpsData.latitude); 603 mParamsToSet.setGpsLongitude(gpsData.longitude); 604 mParamsToSet.setGpsProcessingMethod(gpsData.processingMethod); 605 } 606 } 607 608 } 609 } 610 611 @Override 612 public void openCamera(final Handler handler, final int cameraId, 613 final CameraOpenCallback callback) { 614 mDispatchThread.runJob(new Runnable() { 615 @Override 616 public void run() { 617 mCameraHandler.obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, 618 CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); 619 } 620 }); 621 } 622 623 @Override 624 public void closeCamera(CameraProxy camera, boolean synced) { 625 if (synced) { 626 final WaitDoneBundle bundle = new WaitDoneBundle(); 627 628 mDispatchThread.runJobSync(new Runnable() { 629 @Override 630 public void run() { 631 mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); 632 mCameraHandler.post(bundle.mUnlockRunnable); 633 } 634 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); 635 } else { 636 mDispatchThread.runJob(new Runnable() { 637 @Override 638 public void run() { 639 mCameraHandler.removeCallbacksAndMessages(null); 640 mCameraHandler.obtainMessage(CameraActions.RELEASE).sendToTarget(); 641 } 642 }); 643 } 644 } 645 646 /** 647 * A class which implements {@link CameraAgent.CameraProxy} and 648 * camera handler thread. 649 */ 650 private class AndroidCameraProxyImpl implements CameraAgent.CameraProxy { 651 private final int mCameraId; 652 /* TODO: remove this Camera instance. */ 653 private final Camera mCamera; 654 private final CameraDeviceInfo.Characteristics mCharacteristics; 655 private final AndroidCameraCapabilities mCapabilities; 656 657 private AndroidCameraProxyImpl(int cameraId, Camera camera, 658 CameraDeviceInfo.Characteristics characteristics, 659 AndroidCameraCapabilities capabilities) { 660 mCamera = camera; 661 mCameraId = cameraId; 662 mCharacteristics = characteristics; 663 mCapabilities = capabilities; 664 } 665 666 @Override 667 public android.hardware.Camera getCamera() { 668 return mCamera; 669 } 670 671 @Override 672 public int getCameraId() { 673 return mCameraId; 674 } 675 676 @Override 677 public CameraDeviceInfo.Characteristics getCharacteristics() { 678 return mCharacteristics; 679 } 680 681 @Override 682 public CameraCapabilities getCapabilities() { 683 return new AndroidCameraCapabilities(mCapabilities); 684 } 685 686 @Override 687 public void reconnect(final Handler handler, final CameraOpenCallback cb) { 688 mDispatchThread.runJob(new Runnable() { 689 @Override 690 public void run() { 691 mCameraHandler.obtainMessage(CameraActions.RECONNECT, mCameraId, 0, 692 CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); 693 } 694 }); 695 } 696 697 @Override 698 public void unlock() { 699 final WaitDoneBundle bundle = new WaitDoneBundle(); 700 mDispatchThread.runJobSync(new Runnable() { 701 @Override 702 public void run() { 703 mCameraHandler.sendEmptyMessage(CameraActions.UNLOCK); 704 mCameraHandler.post(bundle.mUnlockRunnable); 705 } 706 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); 707 } 708 709 @Override 710 public void lock() { 711 mDispatchThread.runJob(new Runnable() { 712 @Override 713 public void run() { 714 mCameraHandler.sendEmptyMessage(CameraActions.LOCK); 715 } 716 }); 717 } 718 719 @Override 720 public void setPreviewTexture(final SurfaceTexture surfaceTexture) { 721 mDispatchThread.runJob(new Runnable() { 722 @Override 723 public void run() { 724 mCameraHandler 725 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 726 .sendToTarget(); 727 } 728 }); 729 } 730 731 @Override 732 public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { 733 final WaitDoneBundle bundle = new WaitDoneBundle(); 734 mDispatchThread.runJobSync(new Runnable() { 735 @Override 736 public void run() { 737 mCameraHandler 738 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 739 .sendToTarget(); 740 mCameraHandler.post(bundle.mUnlockRunnable); 741 } 742 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); 743 } 744 745 @Override 746 public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { 747 mDispatchThread.runJob(new Runnable() { 748 @Override 749 public void run() { 750 mCameraHandler 751 .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) 752 .sendToTarget(); 753 } 754 }); 755 } 756 757 @Override 758 public void startPreview() { 759 mDispatchThread.runJob(new Runnable() { 760 @Override 761 public void run() { 762 mCameraHandler 763 .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); 764 } 765 }); 766 } 767 768 @Override 769 public void startPreviewWithCallback(final Handler handler, 770 final CameraStartPreviewCallback cb) { 771 mDispatchThread.runJob(new Runnable() { 772 @Override 773 public void run() { 774 mCameraHandler.obtainMessage(CameraActions.START_PREVIEW_ASYNC, 775 CameraStartPreviewCallbackForward.getNewInstance(handler, cb)).sendToTarget(); 776 } 777 }); 778 } 779 780 @Override 781 public void stopPreview() { 782 final WaitDoneBundle bundle = new WaitDoneBundle(); 783 mDispatchThread.runJobSync(new Runnable() { 784 @Override 785 public void run() { 786 mCameraHandler.sendEmptyMessage(CameraActions.STOP_PREVIEW); 787 mCameraHandler.post(bundle.mUnlockRunnable); 788 } 789 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); 790 } 791 792 @Override 793 public void setPreviewDataCallback( 794 final Handler handler, final CameraPreviewDataCallback cb) { 795 mDispatchThread.runJob(new Runnable() { 796 @Override 797 public void run() { 798 mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK, 799 PreviewCallbackForward.getNewInstance( 800 handler, AndroidCameraProxyImpl.this, cb)) 801 .sendToTarget(); 802 } 803 }); 804 } 805 @Override 806 public void setOneShotPreviewCallback(final Handler handler, 807 final CameraPreviewDataCallback cb) { 808 mDispatchThread.runJob(new Runnable() { 809 @Override 810 public void run() { 811 mCameraHandler.obtainMessage(CameraActions.SET_ONE_SHOT_PREVIEW_CALLBACK, 812 PreviewCallbackForward 813 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 814 .sendToTarget(); 815 } 816 }); 817 } 818 819 @Override 820 public void setPreviewDataCallbackWithBuffer( 821 final Handler handler, final CameraPreviewDataCallback cb) { 822 mDispatchThread.runJob(new Runnable() { 823 @Override 824 public void run() { 825 mCameraHandler.obtainMessage(CameraActions.SET_PREVIEW_CALLBACK_WITH_BUFFER, 826 PreviewCallbackForward 827 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 828 .sendToTarget(); 829 } 830 }); 831 } 832 833 @Override 834 public void addCallbackBuffer(final byte[] callbackBuffer) { 835 mDispatchThread.runJob(new Runnable() { 836 @Override 837 public void run() { 838 mCameraHandler.obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) 839 .sendToTarget(); 840 } 841 }); 842 } 843 844 @Override 845 public void autoFocus(final Handler handler, final CameraAFCallback cb) { 846 final AutoFocusCallback afCallback = new AutoFocusCallback() { 847 @Override 848 public void onAutoFocus(final boolean b, Camera camera) { 849 if (mCameraState.getState() != CameraStateHolder.CAMERA_FOCUSING) { 850 Log.w(TAG, "onAutoFocus callback returning when not focusing"); 851 } else { 852 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 853 } 854 handler.post(new Runnable() { 855 @Override 856 public void run() { 857 cb.onAutoFocus(b, AndroidCameraProxyImpl.this); 858 } 859 }); 860 } 861 }; 862 mDispatchThread.runJob(new Runnable() { 863 @Override 864 public void run() { 865 mCameraState.waitForStates(CameraStateHolder.CAMERA_IDLE); 866 mCameraHandler.obtainMessage(CameraActions.AUTO_FOCUS, afCallback) 867 .sendToTarget(); 868 } 869 }); 870 } 871 872 @Override 873 public void cancelAutoFocus() { 874 mDispatchThread.runJob(new Runnable() { 875 @Override 876 public void run() { 877 mCameraHandler.removeMessages(CameraActions.AUTO_FOCUS); 878 mCameraHandler.sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS); 879 } 880 }); 881 } 882 883 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 884 @Override 885 public void setAutoFocusMoveCallback( 886 final Handler handler, final CameraAFMoveCallback cb) { 887 mDispatchThread.runJob(new Runnable() { 888 @Override 889 public void run() { 890 mCameraHandler.obtainMessage(CameraActions.SET_AUTO_FOCUS_MOVE_CALLBACK, 891 AFMoveCallbackForward.getNewInstance( 892 handler, AndroidCameraProxyImpl.this, cb)) 893 .sendToTarget(); 894 } 895 }); 896 } 897 898 @Override 899 public void takePicture( 900 final Handler handler, final CameraShutterCallback shutter, 901 final CameraPictureCallback raw, final CameraPictureCallback post, 902 final CameraPictureCallback jpeg) { 903 final PictureCallback jpegCallback = new PictureCallback() { 904 @Override 905 public void onPictureTaken(final byte[] data, Camera camera) { 906 if (mCameraState.getState() != CameraStateHolder.CAMERA_CAPTURING) { 907 Log.w(TAG, "picture callback returning when not capturing"); 908 } else { 909 mCameraState.setState(CameraStateHolder.CAMERA_IDLE); 910 } 911 handler.post(new Runnable() { 912 @Override 913 public void run() { 914 jpeg.onPictureTaken(data, AndroidCameraProxyImpl.this); 915 } 916 }); 917 } 918 }; 919 920 mDispatchThread.runJob(new Runnable() { 921 @Override 922 public void run() { 923 mCameraState.waitForStates( 924 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 925 mCameraHandler.requestTakePicture(ShutterCallbackForward 926 .getNewInstance(handler, AndroidCameraProxyImpl.this, shutter), 927 PictureCallbackForward 928 .getNewInstance(handler, AndroidCameraProxyImpl.this, raw), 929 PictureCallbackForward 930 .getNewInstance(handler, AndroidCameraProxyImpl.this, post), 931 jpegCallback 932 ); 933 } 934 }); 935 } 936 937 @Override 938 public void setDisplayOrientation(final int degrees) { 939 mDispatchThread.runJob(new Runnable() { 940 @Override 941 public void run() { 942 mCameraHandler.obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 0) 943 .sendToTarget(); 944 } 945 }); 946 } 947 948 @Override 949 public void setZoomChangeListener(final OnZoomChangeListener listener) { 950 mDispatchThread.runJob(new Runnable() { 951 @Override 952 public void run() { 953 mCameraHandler.obtainMessage(CameraActions.SET_ZOOM_CHANGE_LISTENER, listener) 954 .sendToTarget(); 955 } 956 }); 957 } 958 959 @Override 960 public void setFaceDetectionCallback(final Handler handler, 961 final CameraFaceDetectionCallback cb) { 962 mDispatchThread.runJob(new Runnable() { 963 @Override 964 public void run() { 965 mCameraHandler.obtainMessage(CameraActions.SET_FACE_DETECTION_LISTENER, 966 FaceDetectionCallbackForward 967 .getNewInstance(handler, AndroidCameraProxyImpl.this, cb)) 968 .sendToTarget(); 969 } 970 }); 971 } 972 973 @Override 974 public void startFaceDetection() { 975 mDispatchThread.runJob(new Runnable() { 976 @Override 977 public void run() { 978 mCameraHandler.sendEmptyMessage(CameraActions.START_FACE_DETECTION); 979 } 980 }); 981 } 982 983 @Override 984 public void stopFaceDetection() { 985 mDispatchThread.runJob(new Runnable() { 986 @Override 987 public void run() { 988 mCameraHandler.sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); 989 } 990 }); 991 } 992 993 @Override 994 public void setErrorCallback(final Handler handler, final CameraErrorCallback cb) { 995 mDispatchThread.runJob(new Runnable() { 996 @Override 997 public void run() { 998 mCameraHandler.obtainMessage(CameraActions.SET_ERROR_CALLBACK, 999 ErrorCallbackForward.getNewInstance( 1000 handler, AndroidCameraProxyImpl.this, cb)) 1001 .sendToTarget(); 1002 } 1003 }); 1004 } 1005 1006 @Override 1007 public void setParameters(final Parameters params) { 1008 if (params == null) { 1009 Log.v(TAG, "null parameters in setParameters()"); 1010 return; 1011 } 1012 final String flattenedParameters = params.flatten(); 1013 mDispatchThread.runJob(new Runnable() { 1014 @Override 1015 public void run() { 1016 mCameraState.waitForStates( 1017 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 1018 mCameraHandler.obtainMessage(CameraActions.SET_PARAMETERS, flattenedParameters) 1019 .sendToTarget(); 1020 } 1021 }); 1022 } 1023 1024 @Override 1025 public Parameters getParameters() { 1026 final WaitDoneBundle bundle = new WaitDoneBundle(); 1027 mDispatchThread.runJobSync(new Runnable() { 1028 @Override 1029 public void run() { 1030 mCameraHandler.sendEmptyMessage(CameraActions.GET_PARAMETERS); 1031 mCameraHandler.post(bundle.mUnlockRunnable); 1032 } 1033 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "get parameters"); 1034 return mParameters; 1035 } 1036 1037 @Override 1038 public CameraSettings getSettings() { 1039 return new AndroidCameraSettings(mCapabilities, getParameters()); 1040 } 1041 1042 @Override 1043 public boolean applySettings(final CameraSettings settings) { 1044 if (settings == null) { 1045 Log.v(TAG, "null parameters in applySettings()"); 1046 return false; 1047 } 1048 if (!mCapabilities.supports(settings)) { 1049 return false; 1050 } 1051 1052 final CameraSettings copyOfSettings = new CameraSettings(settings); 1053 mDispatchThread.runJob(new Runnable() { 1054 @Override 1055 public void run() { 1056 mCameraState.waitForStates( 1057 CameraStateHolder.CAMERA_IDLE | CameraStateHolder.CAMERA_UNLOCKED); 1058 mCameraHandler.obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) 1059 .sendToTarget(); 1060 } 1061 }); 1062 return true; 1063 } 1064 1065 @Override 1066 public void refreshSettings() { 1067 mDispatchThread.runJob(new Runnable() { 1068 @Override 1069 public void run() { 1070 mCameraHandler.sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); 1071 } 1072 }); 1073 } 1074 1075 @Override 1076 public void enableShutterSound(final boolean enable) { 1077 mDispatchThread.runJob(new Runnable() { 1078 @Override 1079 public void run() { 1080 mCameraHandler 1081 .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) 1082 .sendToTarget(); 1083 } 1084 }); 1085 } 1086 1087 @Override 1088 public String dumpDeviceSettings() { 1089 String flattened = mParameters.flatten(); 1090 StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); 1091 String dumpedSettings = new String(); 1092 while (tokenizer.hasMoreElements()) { 1093 dumpedSettings += tokenizer.nextToken() + '\n'; 1094 } 1095 1096 return dumpedSettings; 1097 } 1098 } 1099 1100 private static class WaitDoneBundle { 1101 public Runnable mUnlockRunnable; 1102 private final Object mWaitLock; 1103 1104 WaitDoneBundle() { 1105 mWaitLock = new Object(); 1106 mUnlockRunnable = new Runnable() { 1107 @Override 1108 public void run() { 1109 synchronized (mWaitLock) { 1110 mWaitLock.notifyAll(); 1111 } 1112 } 1113 }; 1114 } 1115 } 1116 1117 /** 1118 * A helper class to forward AutoFocusCallback to another thread. 1119 */ 1120 private static class AFCallbackForward implements AutoFocusCallback { 1121 private final Handler mHandler; 1122 private final CameraProxy mCamera; 1123 private final CameraAFCallback mCallback; 1124 1125 /** 1126 * Returns a new instance of {@link AFCallbackForward}. 1127 * 1128 * @param handler The handler in which the callback will be invoked in. 1129 * @param camera The {@link CameraProxy} which the callback is from. 1130 * @param cb The callback to be invoked. 1131 * @return The instance of the {@link AFCallbackForward}, 1132 * or null if any parameter is null. 1133 */ 1134 public static AFCallbackForward getNewInstance( 1135 Handler handler, CameraProxy camera, CameraAFCallback cb) { 1136 if (handler == null || camera == null || cb == null) { 1137 return null; 1138 } 1139 return new AFCallbackForward(handler, camera, cb); 1140 } 1141 1142 private AFCallbackForward( 1143 Handler h, CameraProxy camera, CameraAFCallback cb) { 1144 mHandler = h; 1145 mCamera = camera; 1146 mCallback = cb; 1147 } 1148 1149 @Override 1150 public void onAutoFocus(final boolean b, Camera camera) { 1151 mHandler.post(new Runnable() { 1152 @Override 1153 public void run() { 1154 mCallback.onAutoFocus(b, mCamera); 1155 } 1156 }); 1157 } 1158 } 1159 1160 /** 1161 * A helper class to forward ErrorCallback to another thread. 1162 */ 1163 private static class ErrorCallbackForward implements Camera.ErrorCallback { 1164 private final Handler mHandler; 1165 private final CameraProxy mCamera; 1166 private final CameraErrorCallback mCallback; 1167 1168 /** 1169 * Returns a new instance of {@link AFCallbackForward}. 1170 * 1171 * @param handler The handler in which the callback will be invoked in. 1172 * @param camera The {@link CameraProxy} which the callback is from. 1173 * @param cb The callback to be invoked. 1174 * @return The instance of the {@link AFCallbackForward}, 1175 * or null if any parameter is null. 1176 */ 1177 public static ErrorCallbackForward getNewInstance( 1178 Handler handler, CameraProxy camera, CameraErrorCallback cb) { 1179 if (handler == null || camera == null || cb == null) { 1180 return null; 1181 } 1182 return new ErrorCallbackForward(handler, camera, cb); 1183 } 1184 1185 private ErrorCallbackForward( 1186 Handler h, CameraProxy camera, CameraErrorCallback cb) { 1187 mHandler = h; 1188 mCamera = camera; 1189 mCallback = cb; 1190 } 1191 1192 @Override 1193 public void onError(final int error, Camera camera) { 1194 mHandler.post(new Runnable() { 1195 @Override 1196 public void run() { 1197 mCallback.onError(error, mCamera); 1198 } 1199 }); 1200 } 1201 } 1202 1203 /** A helper class to forward AutoFocusMoveCallback to another thread. */ 1204 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 1205 private static class AFMoveCallbackForward implements AutoFocusMoveCallback { 1206 private final Handler mHandler; 1207 private final CameraAFMoveCallback mCallback; 1208 private final CameraProxy mCamera; 1209 1210 /** 1211 * Returns a new instance of {@link AFMoveCallbackForward}. 1212 * 1213 * @param handler The handler in which the callback will be invoked in. 1214 * @param camera The {@link CameraProxy} which the callback is from. 1215 * @param cb The callback to be invoked. 1216 * @return The instance of the {@link AFMoveCallbackForward}, 1217 * or null if any parameter is null. 1218 */ 1219 public static AFMoveCallbackForward getNewInstance( 1220 Handler handler, CameraProxy camera, CameraAFMoveCallback cb) { 1221 if (handler == null || camera == null || cb == null) { 1222 return null; 1223 } 1224 return new AFMoveCallbackForward(handler, camera, cb); 1225 } 1226 1227 private AFMoveCallbackForward( 1228 Handler h, CameraProxy camera, CameraAFMoveCallback cb) { 1229 mHandler = h; 1230 mCamera = camera; 1231 mCallback = cb; 1232 } 1233 1234 @Override 1235 public void onAutoFocusMoving( 1236 final boolean moving, android.hardware.Camera camera) { 1237 mHandler.post(new Runnable() { 1238 @Override 1239 public void run() { 1240 mCallback.onAutoFocusMoving(moving, mCamera); 1241 } 1242 }); 1243 } 1244 } 1245 1246 /** 1247 * A helper class to forward ShutterCallback to to another thread. 1248 */ 1249 private static class ShutterCallbackForward implements ShutterCallback { 1250 private final Handler mHandler; 1251 private final CameraShutterCallback mCallback; 1252 private final CameraProxy mCamera; 1253 1254 /** 1255 * Returns a new instance of {@link ShutterCallbackForward}. 1256 * 1257 * @param handler The handler in which the callback will be invoked in. 1258 * @param camera The {@link CameraProxy} which the callback is from. 1259 * @param cb The callback to be invoked. 1260 * @return The instance of the {@link ShutterCallbackForward}, 1261 * or null if any parameter is null. 1262 */ 1263 public static ShutterCallbackForward getNewInstance( 1264 Handler handler, CameraProxy camera, CameraShutterCallback cb) { 1265 if (handler == null || camera == null || cb == null) { 1266 return null; 1267 } 1268 return new ShutterCallbackForward(handler, camera, cb); 1269 } 1270 1271 private ShutterCallbackForward( 1272 Handler h, CameraProxy camera, CameraShutterCallback cb) { 1273 mHandler = h; 1274 mCamera = camera; 1275 mCallback = cb; 1276 } 1277 1278 @Override 1279 public void onShutter() { 1280 mHandler.post(new Runnable() { 1281 @Override 1282 public void run() { 1283 mCallback.onShutter(mCamera); 1284 } 1285 }); 1286 } 1287 } 1288 1289 /** 1290 * A helper class to forward PictureCallback to another thread. 1291 */ 1292 private static class PictureCallbackForward implements PictureCallback { 1293 private final Handler mHandler; 1294 private final CameraPictureCallback mCallback; 1295 private final CameraProxy mCamera; 1296 1297 /** 1298 * Returns a new instance of {@link PictureCallbackForward}. 1299 * 1300 * @param handler The handler in which the callback will be invoked in. 1301 * @param camera The {@link CameraProxy} which the callback is from. 1302 * @param cb The callback to be invoked. 1303 * @return The instance of the {@link PictureCallbackForward}, 1304 * or null if any parameters is null. 1305 */ 1306 public static PictureCallbackForward getNewInstance( 1307 Handler handler, CameraProxy camera, CameraPictureCallback cb) { 1308 if (handler == null || camera == null || cb == null) { 1309 return null; 1310 } 1311 return new PictureCallbackForward(handler, camera, cb); 1312 } 1313 1314 private PictureCallbackForward( 1315 Handler h, CameraProxy camera, CameraPictureCallback cb) { 1316 mHandler = h; 1317 mCamera = camera; 1318 mCallback = cb; 1319 } 1320 1321 @Override 1322 public void onPictureTaken( 1323 final byte[] data, android.hardware.Camera camera) { 1324 mHandler.post(new Runnable() { 1325 @Override 1326 public void run() { 1327 mCallback.onPictureTaken(data, mCamera); 1328 } 1329 }); 1330 } 1331 } 1332 1333 /** 1334 * A helper class to forward PreviewCallback to another thread. 1335 */ 1336 private static class PreviewCallbackForward implements PreviewCallback { 1337 private final Handler mHandler; 1338 private final CameraPreviewDataCallback mCallback; 1339 private final CameraProxy mCamera; 1340 1341 /** 1342 * Returns a new instance of {@link PreviewCallbackForward}. 1343 * 1344 * @param handler The handler in which the callback will be invoked in. 1345 * @param camera The {@link CameraProxy} which the callback is from. 1346 * @param cb The callback to be invoked. 1347 * @return The instance of the {@link PreviewCallbackForward}, 1348 * or null if any parameters is null. 1349 */ 1350 public static PreviewCallbackForward getNewInstance( 1351 Handler handler, CameraProxy camera, CameraPreviewDataCallback cb) { 1352 if (handler == null || camera == null || cb == null) { 1353 return null; 1354 } 1355 return new PreviewCallbackForward(handler, camera, cb); 1356 } 1357 1358 private PreviewCallbackForward( 1359 Handler h, CameraProxy camera, CameraPreviewDataCallback cb) { 1360 mHandler = h; 1361 mCamera = camera; 1362 mCallback = cb; 1363 } 1364 1365 @Override 1366 public void onPreviewFrame( 1367 final byte[] data, android.hardware.Camera camera) { 1368 mHandler.post(new Runnable() { 1369 @Override 1370 public void run() { 1371 mCallback.onPreviewFrame(data, mCamera); 1372 } 1373 }); 1374 } 1375 } 1376 1377 private static class FaceDetectionCallbackForward implements FaceDetectionListener { 1378 private final Handler mHandler; 1379 private final CameraFaceDetectionCallback mCallback; 1380 private final CameraProxy mCamera; 1381 1382 /** 1383 * Returns a new instance of {@link FaceDetectionCallbackForward}. 1384 * 1385 * @param handler The handler in which the callback will be invoked in. 1386 * @param camera The {@link CameraProxy} which the callback is from. 1387 * @param cb The callback to be invoked. 1388 * @return The instance of the {@link FaceDetectionCallbackForward}, 1389 * or null if any parameter is null. 1390 */ 1391 public static FaceDetectionCallbackForward getNewInstance( 1392 Handler handler, CameraProxy camera, CameraFaceDetectionCallback cb) { 1393 if (handler == null || camera == null || cb == null) { 1394 return null; 1395 } 1396 return new FaceDetectionCallbackForward(handler, camera, cb); 1397 } 1398 1399 private FaceDetectionCallbackForward( 1400 Handler h, CameraProxy camera, CameraFaceDetectionCallback cb) { 1401 mHandler = h; 1402 mCamera = camera; 1403 mCallback = cb; 1404 } 1405 1406 @Override 1407 public void onFaceDetection( 1408 final Camera.Face[] faces, Camera camera) { 1409 mHandler.post(new Runnable() { 1410 @Override 1411 public void run() { 1412 mCallback.onFaceDetection(faces, mCamera); 1413 } 1414 }); 1415 } 1416 } 1417} 1418