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