1 /* 2 * Copyright (C) 2012 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.OnZoomChangeListener; 23 import android.os.Build; 24 import android.os.Handler; 25 import android.os.Looper; 26 import android.os.Message; 27 import android.view.SurfaceHolder; 28 29 import com.android.ex.camera2.portability.debug.Log; 30 31 /** 32 * An interface which provides possible camera device operations. 33 * 34 * The client should call {@code CameraAgent.openCamera} to get an instance 35 * of {@link CameraAgent.CameraProxy} to control the camera. Classes 36 * implementing this interface should have its own one unique {@code Thread} 37 * other than the main thread for camera operations. Camera device callbacks 38 * are wrapped since the client should not deal with 39 * {@code android.hardware.Camera} directly. 40 * 41 * TODO: provide callback interfaces for: 42 * {@code android.hardware.Camera.ErrorCallback}, 43 * {@code android.hardware.Camera.OnZoomChangeListener}, and 44 */ 45 public abstract class CameraAgent { 46 public static final long CAMERA_OPERATION_TIMEOUT_MS = 3500; 47 48 private static final Log.Tag TAG = new Log.Tag("CamAgnt"); 49 50 public static class CameraStartPreviewCallbackForward 51 implements CameraStartPreviewCallback { 52 private final Handler mHandler; 53 private final CameraStartPreviewCallback mCallback; 54 55 public static CameraStartPreviewCallbackForward getNewInstance( 56 Handler handler, CameraStartPreviewCallback cb) { 57 if (handler == null || cb == null) { 58 return null; 59 } 60 return new CameraStartPreviewCallbackForward(handler, cb); 61 } 62 63 private CameraStartPreviewCallbackForward(Handler h, 64 CameraStartPreviewCallback cb) { 65 mHandler = h; 66 mCallback = cb; 67 } 68 69 @Override 70 public void onPreviewStarted() { 71 mHandler.post(new Runnable() { 72 @Override 73 public void run() { 74 mCallback.onPreviewStarted(); 75 }}); 76 } 77 } 78 79 /** 80 * A callback helps to invoke the original callback on another 81 * {@link android.os.Handler}. 82 */ 83 public static class CameraOpenCallbackForward implements CameraOpenCallback { 84 private final Handler mHandler; 85 private final CameraOpenCallback mCallback; 86 87 /** 88 * Returns a new instance of {@link FaceDetectionCallbackForward}. 89 * 90 * @param handler The handler in which the callback will be invoked in. 91 * @param cb The callback to be invoked. 92 * @return The instance of the {@link FaceDetectionCallbackForward}, or 93 * null if any parameter is null. 94 */ 95 public static CameraOpenCallbackForward getNewInstance( 96 Handler handler, CameraOpenCallback cb) { 97 if (handler == null || cb == null) { 98 return null; 99 } 100 return new CameraOpenCallbackForward(handler, cb); 101 } 102 103 private CameraOpenCallbackForward(Handler h, CameraOpenCallback cb) { 104 // Given that we are using the main thread handler, we can create it 105 // here instead of holding onto the PhotoModule objects. In this 106 // way, we can avoid memory leak. 107 mHandler = new Handler(Looper.getMainLooper()); 108 mCallback = cb; 109 } 110 111 @Override 112 public void onCameraOpened(final CameraProxy camera) { 113 mHandler.post(new Runnable() { 114 @Override 115 public void run() { 116 mCallback.onCameraOpened(camera); 117 }}); 118 } 119 120 @Override 121 public void onCameraDisabled(final int cameraId) { 122 mHandler.post(new Runnable() { 123 @Override 124 public void run() { 125 mCallback.onCameraDisabled(cameraId); 126 }}); 127 } 128 129 @Override 130 public void onDeviceOpenFailure(final int cameraId, final String info) { 131 mHandler.post(new Runnable() { 132 @Override 133 public void run() { 134 mCallback.onDeviceOpenFailure(cameraId, info); 135 }}); 136 } 137 138 @Override 139 public void onDeviceOpenedAlready(final int cameraId, final String info) { 140 mHandler.post(new Runnable() { 141 @Override 142 public void run() { 143 mCallback.onDeviceOpenedAlready(cameraId, info); 144 }}); 145 } 146 147 @Override 148 public void onReconnectionFailure(final CameraAgent mgr, final String info) { 149 mHandler.post(new Runnable() { 150 @Override 151 public void run() { 152 mCallback.onReconnectionFailure(mgr, info); 153 }}); 154 } 155 } 156 157 /** 158 * An interface which wraps 159 * {@link android.hardware.Camera.ErrorCallback} 160 */ 161 public static interface CameraErrorCallback { 162 public void onError(int error, CameraProxy camera); 163 } 164 165 /** 166 * An interface which wraps 167 * {@link android.hardware.Camera.AutoFocusCallback}. 168 */ 169 public static interface CameraAFCallback { 170 public void onAutoFocus(boolean focused, CameraProxy camera); 171 } 172 173 /** 174 * An interface which wraps 175 * {@link android.hardware.Camera.AutoFocusMoveCallback}. 176 */ 177 public static interface CameraAFMoveCallback { 178 public void onAutoFocusMoving(boolean moving, CameraProxy camera); 179 } 180 181 /** 182 * An interface which wraps 183 * {@link android.hardware.Camera.ShutterCallback}. 184 */ 185 public static interface CameraShutterCallback { 186 public void onShutter(CameraProxy camera); 187 } 188 189 /** 190 * An interface which wraps 191 * {@link android.hardware.Camera.PictureCallback}. 192 */ 193 public static interface CameraPictureCallback { 194 public void onPictureTaken(byte[] data, CameraProxy camera); 195 } 196 197 /** 198 * An interface which wraps 199 * {@link android.hardware.Camera.PreviewCallback}. 200 */ 201 public static interface CameraPreviewDataCallback { 202 public void onPreviewFrame(byte[] data, CameraProxy camera); 203 } 204 205 /** 206 * An interface which wraps 207 * {@link android.hardware.Camera.FaceDetectionListener}. 208 */ 209 public static interface CameraFaceDetectionCallback { 210 /** 211 * Callback for face detection. 212 * 213 * @param faces Recognized face in the preview. 214 * @param camera The camera which the preview image comes from. 215 */ 216 public void onFaceDetection(Camera.Face[] faces, CameraProxy camera); 217 } 218 219 /** 220 * An interface to be called when the camera preview has started. 221 */ 222 public static interface CameraStartPreviewCallback { 223 /** 224 * Callback when the preview starts. 225 */ 226 public void onPreviewStarted(); 227 } 228 229 /** 230 * An interface to be called for any events when opening or closing the 231 * camera device. This error callback is different from the one defined 232 * in the framework, {@link android.hardware.Camera.ErrorCallback}, which 233 * is used after the camera is opened. 234 */ 235 public static interface CameraOpenCallback { 236 /** 237 * Callback when camera open succeeds. 238 */ 239 public void onCameraOpened(CameraProxy camera); 240 241 /** 242 * Callback when {@link com.android.camera.CameraDisabledException} is 243 * caught. 244 * 245 * @param cameraId The disabled camera. 246 */ 247 public void onCameraDisabled(int cameraId); 248 249 /** 250 * Callback when {@link com.android.camera.CameraHardwareException} is 251 * caught. 252 * 253 * @param cameraId The camera with the hardware failure. 254 * @param info The extra info regarding this failure. 255 */ 256 public void onDeviceOpenFailure(int cameraId, String info); 257 258 /** 259 * Callback when trying to open the camera which is already opened. 260 * 261 * @param cameraId The camera which is causing the open error. 262 */ 263 public void onDeviceOpenedAlready(int cameraId, String info); 264 265 /** 266 * Callback when {@link java.io.IOException} is caught during 267 * {@link android.hardware.Camera#reconnect()}. 268 * 269 * @param mgr The {@link CameraAgent} 270 * with the reconnect failure. 271 */ 272 public void onReconnectionFailure(CameraAgent mgr, String info); 273 } 274 275 /** 276 * Opens the camera of the specified ID asynchronously. The camera device 277 * will be opened in the camera handler thread and will be returned through 278 * the {@link CameraAgent.CameraOpenCallback# 279 * onCameraOpened(com.android.camera.cameradevice.CameraAgent.CameraProxy)}. 280 * 281 * @param handler The {@link android.os.Handler} in which the callback 282 * was handled. 283 * @param callback The callback for the result. 284 * @param cameraId The camera ID to open. 285 */ 286 public void openCamera(final Handler handler, final int cameraId, 287 final CameraOpenCallback callback) { 288 try { 289 getDispatchThread().runJob(new Runnable() { 290 @Override 291 public void run() { 292 getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, 293 CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); 294 } 295 }); 296 } catch (final RuntimeException ex) { 297 getCameraExceptionHandler().onDispatchThreadException(ex); 298 } 299 } 300 301 /** 302 * Closes the camera device. 303 * 304 * @param camera The camera to close. {@code null} means all. 305 * @param synced Whether this call should be synchronous. 306 */ 307 public void closeCamera(CameraProxy camera, boolean synced) { 308 try { 309 if (synced) { 310 // Don't bother to wait since camera is in bad state. 311 if (getCameraState().isInvalid()) { 312 return; 313 } 314 final WaitDoneBundle bundle = new WaitDoneBundle(); 315 316 getDispatchThread().runJobSync(new Runnable() { 317 @Override 318 public void run() { 319 getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); 320 getCameraHandler().post(bundle.mUnlockRunnable); 321 }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); 322 } else { 323 getDispatchThread().runJob(new Runnable() { 324 @Override 325 public void run() { 326 getCameraHandler().removeCallbacksAndMessages(null); 327 getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); 328 }}); 329 } 330 } catch (final RuntimeException ex) { 331 getCameraExceptionHandler().onDispatchThreadException(ex); 332 } 333 } 334 335 /** 336 * Sets a callback for handling camera api runtime exceptions on 337 * a handler. 338 */ 339 public abstract void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler); 340 341 /** 342 * Recycles the resources used by this instance. CameraAgent will be in 343 * an unusable state after calling this. 344 */ 345 public abstract void recycle(); 346 347 /** 348 * @return The camera devices info. 349 */ 350 public abstract CameraDeviceInfo getCameraDeviceInfo(); 351 352 /** 353 * @return The handler to which camera tasks should be posted. 354 */ 355 protected abstract Handler getCameraHandler(); 356 357 /** 358 * @return The thread used on which client callbacks are served. 359 */ 360 protected abstract DispatchThread getDispatchThread(); 361 362 /** 363 * @return The state machine tracking the camera API's current status. 364 */ 365 protected abstract CameraStateHolder getCameraState(); 366 367 /** 368 * @return The exception handler. 369 */ 370 protected abstract CameraExceptionHandler getCameraExceptionHandler(); 371 372 /** 373 * An interface that takes camera operation requests and post messages to the 374 * camera handler thread. All camera operations made through this interface is 375 * asynchronous by default except those mentioned specifically. 376 */ 377 public abstract static class CameraProxy { 378 379 /** 380 * Returns the underlying {@link android.hardware.Camera} object used 381 * by this proxy. This method should only be used when handing the 382 * camera device over to {@link android.media.MediaRecorder} for 383 * recording. 384 */ 385 @Deprecated 386 public abstract android.hardware.Camera getCamera(); 387 388 /** 389 * @return The camera ID associated to by this 390 * {@link CameraAgent.CameraProxy}. 391 */ 392 public abstract int getCameraId(); 393 394 /** 395 * @return The camera characteristics. 396 */ 397 public abstract CameraDeviceInfo.Characteristics getCharacteristics(); 398 399 /** 400 * @return The camera capabilities. 401 */ 402 public abstract CameraCapabilities getCapabilities(); 403 404 /** 405 * @return The camera agent which creates this proxy. 406 */ 407 public abstract CameraAgent getAgent(); 408 409 /** 410 * Reconnects to the camera device. On success, the camera device will 411 * be returned through {@link CameraAgent 412 * .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraAgent 413 * .CameraProxy)}. 414 * @see android.hardware.Camera#reconnect() 415 * 416 * @param handler The {@link android.os.Handler} in which the callback 417 * was handled. 418 * @param cb The callback when any error happens. 419 */ 420 public void reconnect(final Handler handler, final CameraOpenCallback cb) { 421 try { 422 getDispatchThread().runJob(new Runnable() { 423 @Override 424 public void run() { 425 getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0, 426 CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); 427 }}); 428 } catch (final RuntimeException ex) { 429 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 430 } 431 } 432 433 /** 434 * Unlocks the camera device. 435 * 436 * @see android.hardware.Camera#unlock() 437 */ 438 public void unlock() { 439 // Don't bother to wait since camera is in bad state. 440 if (getCameraState().isInvalid()) { 441 return; 442 } 443 final WaitDoneBundle bundle = new WaitDoneBundle(); 444 try { 445 getDispatchThread().runJobSync(new Runnable() { 446 @Override 447 public void run() { 448 getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK); 449 getCameraHandler().post(bundle.mUnlockRunnable); 450 } 451 }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); 452 } catch (final RuntimeException ex) { 453 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 454 } 455 } 456 457 /** 458 * Locks the camera device. 459 * @see android.hardware.Camera#lock() 460 */ 461 public void lock() { 462 try { 463 getDispatchThread().runJob(new Runnable() { 464 @Override 465 public void run() { 466 getCameraHandler().sendEmptyMessage(CameraActions.LOCK); 467 }}); 468 } catch (final RuntimeException ex) { 469 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 470 } 471 } 472 473 /** 474 * Sets the {@link android.graphics.SurfaceTexture} for preview. 475 * 476 * <p>Note that, once this operation has been performed, it is no longer 477 * possible to change the preview or photo sizes in the 478 * {@link CameraSettings} instance for this camera, and the mutators for 479 * these fields are allowed to ignore all further invocations until the 480 * preview is stopped with {@link #stopPreview}.</p> 481 * 482 * @param surfaceTexture The {@link SurfaceTexture} for preview. 483 * 484 * @see CameraSettings#setPhotoSize 485 * @see CameraSettings#setPreviewSize 486 */ 487 // XXX: Despite the above documentation about locking the sizes, the API 488 // 1 implementation doesn't currently enforce this at all, although the 489 // Camera class warns that preview sizes shouldn't be changed while a 490 // preview is running. Furthermore, the API 2 implementation doesn't yet 491 // unlock the sizes when stopPreview() is invoked (see related FIXME on 492 // the STOP_PREVIEW case in its handler; in the meantime, changing API 2 493 // sizes would require closing and reopening the camera. 494 public void setPreviewTexture(final SurfaceTexture surfaceTexture) { 495 try { 496 getDispatchThread().runJob(new Runnable() { 497 @Override 498 public void run() { 499 getCameraHandler() 500 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 501 .sendToTarget(); 502 }}); 503 } catch (final RuntimeException ex) { 504 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 505 } 506 } 507 508 /** 509 * Blocks until a {@link android.graphics.SurfaceTexture} has been set 510 * for preview. 511 * 512 * <p>Note that, once this operation has been performed, it is no longer 513 * possible to change the preview or photo sizes in the 514 * {@link CameraSettings} instance for this camera, and the mutators for 515 * these fields are allowed to ignore all further invocations.</p> 516 * 517 * @param surfaceTexture The {@link SurfaceTexture} for preview. 518 * 519 * @see CameraSettings#setPhotoSize 520 * @see CameraSettings#setPreviewSize 521 */ 522 public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { 523 // Don't bother to wait since camera is in bad state. 524 if (getCameraState().isInvalid()) { 525 return; 526 } 527 final WaitDoneBundle bundle = new WaitDoneBundle(); 528 try { 529 getDispatchThread().runJobSync(new Runnable() { 530 @Override 531 public void run() { 532 getCameraHandler() 533 .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) 534 .sendToTarget(); 535 getCameraHandler().post(bundle.mUnlockRunnable); 536 }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); 537 } catch (final RuntimeException ex) { 538 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 539 } 540 } 541 542 /** 543 * Sets the {@link android.view.SurfaceHolder} for preview. 544 * 545 * @param surfaceHolder The {@link SurfaceHolder} for preview. 546 */ 547 public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { 548 try { 549 getDispatchThread().runJob(new Runnable() { 550 @Override 551 public void run() { 552 getCameraHandler() 553 .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) 554 .sendToTarget(); 555 }}); 556 } catch (final RuntimeException ex) { 557 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 558 } 559 } 560 561 /** 562 * Starts the camera preview. 563 */ 564 public void startPreview() { 565 try { 566 getDispatchThread().runJob(new Runnable() { 567 @Override 568 public void run() { 569 getCameraHandler() 570 .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); 571 }}); 572 } catch (final RuntimeException ex) { 573 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 574 } 575 } 576 577 /** 578 * Starts the camera preview and executes a callback on a handler once 579 * the preview starts. 580 */ 581 public void startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb) { 582 try { 583 getDispatchThread().runJob(new Runnable() { 584 @Override 585 public void run() { 586 getCameraHandler().obtainMessage(CameraActions.START_PREVIEW_ASYNC, 587 CameraStartPreviewCallbackForward.getNewInstance(h, cb)) 588 .sendToTarget(); 589 }}); 590 } catch (final RuntimeException ex) { 591 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 592 } 593 } 594 595 /** 596 * Stops the camera preview synchronously. 597 * {@code stopPreview()} must be synchronous to ensure that the caller can 598 * continues to release resources related to camera preview. 599 */ 600 public void stopPreview() { 601 // Don't bother to wait since camera is in bad state. 602 if (getCameraState().isInvalid()) { 603 return; 604 } 605 final WaitDoneBundle bundle = new WaitDoneBundle(); 606 try { 607 getDispatchThread().runJobSync(new Runnable() { 608 @Override 609 public void run() { 610 getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle) 611 .sendToTarget(); 612 }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); 613 } catch (final RuntimeException ex) { 614 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 615 } 616 } 617 618 /** 619 * Sets the callback for preview data. 620 * 621 * @param handler The {@link android.os.Handler} in which the callback was handled. 622 * @param cb The callback to be invoked when the preview data is available. 623 * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) 624 */ 625 public abstract void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb); 626 627 /** 628 * Sets the one-time callback for preview data. 629 * 630 * @param handler The {@link android.os.Handler} in which the callback was handled. 631 * @param cb The callback to be invoked when the preview data for 632 * next frame is available. 633 * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) 634 */ 635 public abstract void setOneShotPreviewCallback(Handler handler, 636 CameraPreviewDataCallback cb); 637 638 /** 639 * Sets the callback for preview data. 640 * 641 * @param handler The handler in which the callback will be invoked. 642 * @param cb The callback to be invoked when the preview data is available. 643 * @see android.hardware.Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback) 644 */ 645 public abstract void setPreviewDataCallbackWithBuffer(Handler handler, 646 CameraPreviewDataCallback cb); 647 648 /** 649 * Adds buffer for the preview callback. 650 * 651 * @param callbackBuffer The buffer allocated for the preview data. 652 */ 653 public void addCallbackBuffer(final byte[] callbackBuffer) { 654 try { 655 getDispatchThread().runJob(new Runnable() { 656 @Override 657 public void run() { 658 getCameraHandler() 659 .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) 660 .sendToTarget(); 661 } 662 }); 663 } catch (final RuntimeException ex) { 664 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 665 } 666 } 667 668 /** 669 * Starts the auto-focus process. The result will be returned through the callback. 670 * 671 * @param handler The handler in which the callback will be invoked. 672 * @param cb The auto-focus callback. 673 */ 674 public abstract void autoFocus(Handler handler, CameraAFCallback cb); 675 676 /** 677 * Cancels the auto-focus process. 678 * 679 * <p>This action has the highest priority and will get processed before anything 680 * else that is pending. Moreover, any pending auto-focuses that haven't yet 681 * began will also be ignored.</p> 682 */ 683 public void cancelAutoFocus() { 684 // Do not use the dispatch thread since we want to avoid a wait-cycle 685 // between applySettingsHelper which waits until the state is not FOCUSING. 686 // cancelAutoFocus should get executed asap, set the state back to idle. 687 getCameraHandler().sendMessageAtFrontOfQueue( 688 getCameraHandler().obtainMessage(CameraActions.CANCEL_AUTO_FOCUS)); 689 getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS_FINISH); 690 } 691 692 /** 693 * Sets the auto-focus callback 694 * 695 * @param handler The handler in which the callback will be invoked. 696 * @param cb The callback to be invoked when the preview data is available. 697 */ 698 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 699 public abstract void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb); 700 701 /** 702 * Instrument the camera to take a picture. 703 * 704 * @param handler The handler in which the callback will be invoked. 705 * @param shutter The callback for shutter action, may be null. 706 * @param raw The callback for uncompressed data, may be null. 707 * @param postview The callback for postview image data, may be null. 708 * @param jpeg The callback for jpeg image data, may be null. 709 * @see android.hardware.Camera#takePicture( 710 * android.hardware.Camera.ShutterCallback, 711 * android.hardware.Camera.PictureCallback, 712 * android.hardware.Camera.PictureCallback) 713 */ 714 public abstract void takePicture( 715 Handler handler, 716 CameraShutterCallback shutter, 717 CameraPictureCallback raw, 718 CameraPictureCallback postview, 719 CameraPictureCallback jpeg); 720 721 /** 722 * Sets the display orientation for camera to adjust the preview and JPEG orientation. 723 * 724 * @param degrees The counterclockwise rotation in degrees, relative to the device's natural 725 * orientation. Should be 0, 90, 180 or 270. 726 */ 727 public void setDisplayOrientation(final int degrees) { 728 setDisplayOrientation(degrees, true); 729 } 730 731 /** 732 * Sets the display orientation for camera to adjust the preview—and, optionally, 733 * JPEG—orientations. 734 * <p>If capture rotation is not requested, future captures will be returned in the sensor's 735 * physical rotation, which does not necessarily match the device's natural orientation.</p> 736 * 737 * @param degrees The counterclockwise rotation in degrees, relative to the device's natural 738 * orientation. Should be 0, 90, 180 or 270. 739 * @param capture Whether to adjust the JPEG capture orientation as well as the preview one. 740 */ 741 public void setDisplayOrientation(final int degrees, final boolean capture) { 742 try { 743 getDispatchThread().runJob(new Runnable() { 744 @Override 745 public void run() { 746 getCameraHandler() 747 .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, 748 capture ? 1 : 0) 749 .sendToTarget(); 750 }}); 751 } catch (final RuntimeException ex) { 752 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 753 } 754 } 755 756 public void setJpegOrientation(final int degrees) { 757 try { 758 getDispatchThread().runJob(new Runnable() { 759 @Override 760 public void run() { 761 getCameraHandler() 762 .obtainMessage(CameraActions.SET_JPEG_ORIENTATION, degrees, 0) 763 .sendToTarget(); 764 }}); 765 } catch (final RuntimeException ex) { 766 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 767 } 768 } 769 770 /** 771 * Sets the listener for zoom change. 772 * 773 * @param listener The listener. 774 */ 775 public abstract void setZoomChangeListener(OnZoomChangeListener listener); 776 777 /** 778 * Sets the face detection listener. 779 * 780 * @param handler The handler in which the callback will be invoked. 781 * @param callback The callback for face detection results. 782 */ 783 public abstract void setFaceDetectionCallback(Handler handler, 784 CameraFaceDetectionCallback callback); 785 786 /** 787 * Starts the face detection. 788 */ 789 public void startFaceDetection() { 790 try { 791 getDispatchThread().runJob(new Runnable() { 792 @Override 793 public void run() { 794 getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION); 795 }}); 796 } catch (final RuntimeException ex) { 797 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 798 } 799 } 800 801 /** 802 * Stops the face detection. 803 */ 804 public void stopFaceDetection() { 805 try { 806 getDispatchThread().runJob(new Runnable() { 807 @Override 808 public void run() { 809 getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); 810 }}); 811 } catch (final RuntimeException ex) { 812 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 813 } 814 } 815 816 /** 817 * Sets the camera parameters. 818 * 819 * @param params The camera parameters to use. 820 */ 821 @Deprecated 822 public abstract void setParameters(Camera.Parameters params); 823 824 /** 825 * Gets the current camera parameters synchronously. This method is 826 * synchronous since the caller has to wait for the camera to return 827 * the parameters. If the parameters are already cached, it returns 828 * immediately. 829 */ 830 @Deprecated 831 public abstract Camera.Parameters getParameters(); 832 833 /** 834 * Gets the current camera settings synchronously. 835 * <p>This method is synchronous since the caller has to wait for the 836 * camera to return the parameters. If the parameters are already 837 * cached, it returns immediately.</p> 838 */ 839 public abstract CameraSettings getSettings(); 840 841 /** 842 * Default implementation of {@link #applySettings(CameraSettings)} 843 * that is only missing the set of states it needs to wait for 844 * before applying the settings. 845 * 846 * @param settings The settings to use on the device. 847 * @param statesToAwait Bitwise OR of the required camera states. 848 * @return Whether the settings can be applied. 849 */ 850 protected boolean applySettingsHelper(CameraSettings settings, 851 final int statesToAwait) { 852 if (settings == null) { 853 Log.v(TAG, "null argument in applySettings()"); 854 return false; 855 } 856 if (!getCapabilities().supports(settings)) { 857 Log.w(TAG, "Unsupported settings in applySettings()"); 858 return false; 859 } 860 861 final CameraSettings copyOfSettings = settings.copy(); 862 try { 863 getDispatchThread().runJob(new Runnable() { 864 @Override 865 public void run() { 866 CameraStateHolder cameraState = getCameraState(); 867 // Don't bother to wait since camera is in bad state. 868 if (cameraState.isInvalid()) { 869 return; 870 } 871 cameraState.waitForStates(statesToAwait); 872 getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) 873 .sendToTarget(); 874 }}); 875 } catch (final RuntimeException ex) { 876 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 877 } 878 return true; 879 } 880 881 /** 882 * Applies the settings to the camera device. 883 * 884 * <p>If the camera is either focusing or capturing; settings applications 885 * will be (asynchronously) deferred until those operations complete.</p> 886 * 887 * @param settings The settings to use on the device. 888 * @return Whether the settings can be applied. 889 */ 890 public abstract boolean applySettings(CameraSettings settings); 891 892 /** 893 * Forces {@code CameraProxy} to update the cached version of the camera 894 * settings regardless of the dirty bit. 895 */ 896 public void refreshSettings() { 897 try { 898 getDispatchThread().runJob(new Runnable() { 899 @Override 900 public void run() { 901 getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); 902 }}); 903 } catch (final RuntimeException ex) { 904 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 905 } 906 } 907 908 /** 909 * Enables/Disables the camera shutter sound. 910 * 911 * @param enable {@code true} to enable the shutter sound, 912 * {@code false} to disable it. 913 */ 914 public void enableShutterSound(final boolean enable) { 915 try { 916 getDispatchThread().runJob(new Runnable() { 917 @Override 918 public void run() { 919 getCameraHandler() 920 .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) 921 .sendToTarget(); 922 }}); 923 } catch (final RuntimeException ex) { 924 getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); 925 } 926 } 927 928 /** 929 * Dumps the current settings of the camera device. 930 * 931 * <p>The content varies based on the underlying camera API settings 932 * implementation.</p> 933 * 934 * @return The content of the device settings represented by a string. 935 */ 936 public abstract String dumpDeviceSettings(); 937 938 /** 939 * @return The handler to which camera tasks should be posted. 940 */ 941 public abstract Handler getCameraHandler(); 942 943 /** 944 * @return The thread used on which client callbacks are served. 945 */ 946 public abstract DispatchThread getDispatchThread(); 947 948 /** 949 * @return The state machine tracking the camera API's current mode. 950 */ 951 public abstract CameraStateHolder getCameraState(); 952 } 953 954 public static class WaitDoneBundle { 955 public final Runnable mUnlockRunnable; 956 public final Object mWaitLock; 957 958 WaitDoneBundle() { 959 mWaitLock = new Object(); 960 mUnlockRunnable = new Runnable() { 961 @Override 962 public void run() { 963 synchronized (mWaitLock) { 964 mWaitLock.notifyAll(); 965 } 966 }}; 967 } 968 969 /** 970 * Notify all synchronous waiters waiting on message completion with {@link #mWaitLock}. 971 * 972 * <p>This assumes that the message was sent with {@code this} as the {@code Message#obj}. 973 * Otherwise the message is ignored.</p> 974 */ 975 /*package*/ static void unblockSyncWaiters(Message msg) { 976 if (msg == null) return; 977 978 if (msg.obj instanceof WaitDoneBundle) { 979 WaitDoneBundle bundle = (WaitDoneBundle)msg.obj; 980 bundle.mUnlockRunnable.run(); 981 } 982 } 983 } 984 } 985