| /* |
| * Copyright (C) 2012 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.ex.camera2.portability; |
| |
| import android.annotation.TargetApi; |
| import android.graphics.SurfaceTexture; |
| import android.hardware.Camera; |
| import android.hardware.Camera.OnZoomChangeListener; |
| import android.os.Build; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.view.SurfaceHolder; |
| |
| import com.android.ex.camera2.portability.debug.Log; |
| |
| /** |
| * An interface which provides possible camera device operations. |
| * |
| * The client should call {@code CameraAgent.openCamera} to get an instance |
| * of {@link CameraAgent.CameraProxy} to control the camera. Classes |
| * implementing this interface should have its own one unique {@code Thread} |
| * other than the main thread for camera operations. Camera device callbacks |
| * are wrapped since the client should not deal with |
| * {@code android.hardware.Camera} directly. |
| * |
| * TODO: provide callback interfaces for: |
| * {@code android.hardware.Camera.ErrorCallback}, |
| * {@code android.hardware.Camera.OnZoomChangeListener}, and |
| */ |
| public abstract class CameraAgent { |
| public static final long CAMERA_OPERATION_TIMEOUT_MS = 3500; |
| |
| private static final Log.Tag TAG = new Log.Tag("CamAgnt"); |
| |
| public static class CameraStartPreviewCallbackForward |
| implements CameraStartPreviewCallback { |
| private final Handler mHandler; |
| private final CameraStartPreviewCallback mCallback; |
| |
| public static CameraStartPreviewCallbackForward getNewInstance( |
| Handler handler, CameraStartPreviewCallback cb) { |
| if (handler == null || cb == null) { |
| return null; |
| } |
| return new CameraStartPreviewCallbackForward(handler, cb); |
| } |
| |
| private CameraStartPreviewCallbackForward(Handler h, |
| CameraStartPreviewCallback cb) { |
| mHandler = h; |
| mCallback = cb; |
| } |
| |
| @Override |
| public void onPreviewStarted() { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onPreviewStarted(); |
| }}); |
| } |
| } |
| |
| /** |
| * A callback helps to invoke the original callback on another |
| * {@link android.os.Handler}. |
| */ |
| public static class CameraOpenCallbackForward implements CameraOpenCallback { |
| private final Handler mHandler; |
| private final CameraOpenCallback mCallback; |
| |
| /** |
| * Returns a new instance of {@link FaceDetectionCallbackForward}. |
| * |
| * @param handler The handler in which the callback will be invoked in. |
| * @param cb The callback to be invoked. |
| * @return The instance of the {@link FaceDetectionCallbackForward}, or |
| * null if any parameter is null. |
| */ |
| public static CameraOpenCallbackForward getNewInstance( |
| Handler handler, CameraOpenCallback cb) { |
| if (handler == null || cb == null) { |
| return null; |
| } |
| return new CameraOpenCallbackForward(handler, cb); |
| } |
| |
| private CameraOpenCallbackForward(Handler h, CameraOpenCallback cb) { |
| // Given that we are using the main thread handler, we can create it |
| // here instead of holding onto the PhotoModule objects. In this |
| // way, we can avoid memory leak. |
| mHandler = new Handler(Looper.getMainLooper()); |
| mCallback = cb; |
| } |
| |
| @Override |
| public void onCameraOpened(final CameraProxy camera) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onCameraOpened(camera); |
| }}); |
| } |
| |
| @Override |
| public void onCameraDisabled(final int cameraId) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onCameraDisabled(cameraId); |
| }}); |
| } |
| |
| @Override |
| public void onDeviceOpenFailure(final int cameraId, final String info) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onDeviceOpenFailure(cameraId, info); |
| }}); |
| } |
| |
| @Override |
| public void onDeviceOpenedAlready(final int cameraId, final String info) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onDeviceOpenedAlready(cameraId, info); |
| }}); |
| } |
| |
| @Override |
| public void onReconnectionFailure(final CameraAgent mgr, final String info) { |
| mHandler.post(new Runnable() { |
| @Override |
| public void run() { |
| mCallback.onReconnectionFailure(mgr, info); |
| }}); |
| } |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.ErrorCallback} |
| */ |
| public static interface CameraErrorCallback { |
| public void onError(int error, CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.AutoFocusCallback}. |
| */ |
| public static interface CameraAFCallback { |
| public void onAutoFocus(boolean focused, CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.AutoFocusMoveCallback}. |
| */ |
| public static interface CameraAFMoveCallback { |
| public void onAutoFocusMoving(boolean moving, CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.ShutterCallback}. |
| */ |
| public static interface CameraShutterCallback { |
| public void onShutter(CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.PictureCallback}. |
| */ |
| public static interface CameraPictureCallback { |
| public void onPictureTaken(byte[] data, CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.PreviewCallback}. |
| */ |
| public static interface CameraPreviewDataCallback { |
| public void onPreviewFrame(byte[] data, CameraProxy camera); |
| } |
| |
| /** |
| * An interface which wraps |
| * {@link android.hardware.Camera.FaceDetectionListener}. |
| */ |
| public static interface CameraFaceDetectionCallback { |
| /** |
| * Callback for face detection. |
| * |
| * @param faces Recognized face in the preview. |
| * @param camera The camera which the preview image comes from. |
| */ |
| public void onFaceDetection(Camera.Face[] faces, CameraProxy camera); |
| } |
| |
| /** |
| * An interface to be called when the camera preview has started. |
| */ |
| public static interface CameraStartPreviewCallback { |
| /** |
| * Callback when the preview starts. |
| */ |
| public void onPreviewStarted(); |
| } |
| |
| /** |
| * An interface to be called for any events when opening or closing the |
| * camera device. This error callback is different from the one defined |
| * in the framework, {@link android.hardware.Camera.ErrorCallback}, which |
| * is used after the camera is opened. |
| */ |
| public static interface CameraOpenCallback { |
| /** |
| * Callback when camera open succeeds. |
| */ |
| public void onCameraOpened(CameraProxy camera); |
| |
| /** |
| * Callback when {@link com.android.camera.CameraDisabledException} is |
| * caught. |
| * |
| * @param cameraId The disabled camera. |
| */ |
| public void onCameraDisabled(int cameraId); |
| |
| /** |
| * Callback when {@link com.android.camera.CameraHardwareException} is |
| * caught. |
| * |
| * @param cameraId The camera with the hardware failure. |
| * @param info The extra info regarding this failure. |
| */ |
| public void onDeviceOpenFailure(int cameraId, String info); |
| |
| /** |
| * Callback when trying to open the camera which is already opened. |
| * |
| * @param cameraId The camera which is causing the open error. |
| */ |
| public void onDeviceOpenedAlready(int cameraId, String info); |
| |
| /** |
| * Callback when {@link java.io.IOException} is caught during |
| * {@link android.hardware.Camera#reconnect()}. |
| * |
| * @param mgr The {@link CameraAgent} |
| * with the reconnect failure. |
| */ |
| public void onReconnectionFailure(CameraAgent mgr, String info); |
| } |
| |
| /** |
| * Opens the camera of the specified ID asynchronously. The camera device |
| * will be opened in the camera handler thread and will be returned through |
| * the {@link CameraAgent.CameraOpenCallback# |
| * onCameraOpened(com.android.camera.cameradevice.CameraAgent.CameraProxy)}. |
| * |
| * @param handler The {@link android.os.Handler} in which the callback |
| * was handled. |
| * @param callback The callback for the result. |
| * @param cameraId The camera ID to open. |
| */ |
| public void openCamera(final Handler handler, final int cameraId, |
| final CameraOpenCallback callback) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().obtainMessage(CameraActions.OPEN_CAMERA, cameraId, 0, |
| CameraOpenCallbackForward.getNewInstance(handler, callback)).sendToTarget(); |
| } |
| }); |
| } catch (final RuntimeException ex) { |
| getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Closes the camera device. |
| * |
| * @param camera The camera to close. {@code null} means all. |
| * @param synced Whether this call should be synchronous. |
| */ |
| public void closeCamera(CameraProxy camera, boolean synced) { |
| try { |
| if (synced) { |
| // Don't bother to wait since camera is in bad state. |
| if (getCameraState().isInvalid()) { |
| return; |
| } |
| final WaitDoneBundle bundle = new WaitDoneBundle(); |
| |
| getDispatchThread().runJobSync(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); |
| getCameraHandler().post(bundle.mUnlockRunnable); |
| }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera release"); |
| } else { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().removeCallbacksAndMessages(null); |
| getCameraHandler().obtainMessage(CameraActions.RELEASE).sendToTarget(); |
| }}); |
| } |
| } catch (final RuntimeException ex) { |
| getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets a callback for handling camera api runtime exceptions on |
| * a handler. |
| */ |
| public abstract void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler); |
| |
| /** |
| * Recycles the resources used by this instance. CameraAgent will be in |
| * an unusable state after calling this. |
| */ |
| public abstract void recycle(); |
| |
| /** |
| * @return The camera devices info. |
| */ |
| public abstract CameraDeviceInfo getCameraDeviceInfo(); |
| |
| /** |
| * @return The handler to which camera tasks should be posted. |
| */ |
| protected abstract Handler getCameraHandler(); |
| |
| /** |
| * @return The thread used on which client callbacks are served. |
| */ |
| protected abstract DispatchThread getDispatchThread(); |
| |
| /** |
| * @return The state machine tracking the camera API's current status. |
| */ |
| protected abstract CameraStateHolder getCameraState(); |
| |
| /** |
| * @return The exception handler. |
| */ |
| protected abstract CameraExceptionHandler getCameraExceptionHandler(); |
| |
| /** |
| * An interface that takes camera operation requests and post messages to the |
| * camera handler thread. All camera operations made through this interface is |
| * asynchronous by default except those mentioned specifically. |
| */ |
| public abstract static class CameraProxy { |
| |
| /** |
| * Returns the underlying {@link android.hardware.Camera} object used |
| * by this proxy. This method should only be used when handing the |
| * camera device over to {@link android.media.MediaRecorder} for |
| * recording. |
| */ |
| @Deprecated |
| public abstract android.hardware.Camera getCamera(); |
| |
| /** |
| * @return The camera ID associated to by this |
| * {@link CameraAgent.CameraProxy}. |
| */ |
| public abstract int getCameraId(); |
| |
| /** |
| * @return The camera characteristics. |
| */ |
| public abstract CameraDeviceInfo.Characteristics getCharacteristics(); |
| |
| /** |
| * @return The camera capabilities. |
| */ |
| public abstract CameraCapabilities getCapabilities(); |
| |
| /** |
| * @return The camera agent which creates this proxy. |
| */ |
| public abstract CameraAgent getAgent(); |
| |
| /** |
| * Reconnects to the camera device. On success, the camera device will |
| * be returned through {@link CameraAgent |
| * .CameraOpenCallback#onCameraOpened(com.android.camera.cameradevice.CameraAgent |
| * .CameraProxy)}. |
| * @see android.hardware.Camera#reconnect() |
| * |
| * @param handler The {@link android.os.Handler} in which the callback |
| * was handled. |
| * @param cb The callback when any error happens. |
| */ |
| public void reconnect(final Handler handler, final CameraOpenCallback cb) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().obtainMessage(CameraActions.RECONNECT, getCameraId(), 0, |
| CameraOpenCallbackForward.getNewInstance(handler, cb)).sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Unlocks the camera device. |
| * |
| * @see android.hardware.Camera#unlock() |
| */ |
| public void unlock() { |
| // Don't bother to wait since camera is in bad state. |
| if (getCameraState().isInvalid()) { |
| return; |
| } |
| final WaitDoneBundle bundle = new WaitDoneBundle(); |
| try { |
| getDispatchThread().runJobSync(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().sendEmptyMessage(CameraActions.UNLOCK); |
| getCameraHandler().post(bundle.mUnlockRunnable); |
| } |
| }, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "camera unlock"); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Locks the camera device. |
| * @see android.hardware.Camera#lock() |
| */ |
| public void lock() { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().sendEmptyMessage(CameraActions.LOCK); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets the {@link android.graphics.SurfaceTexture} for preview. |
| * |
| * <p>Note that, once this operation has been performed, it is no longer |
| * possible to change the preview or photo sizes in the |
| * {@link CameraSettings} instance for this camera, and the mutators for |
| * these fields are allowed to ignore all further invocations until the |
| * preview is stopped with {@link #stopPreview}.</p> |
| * |
| * @param surfaceTexture The {@link SurfaceTexture} for preview. |
| * |
| * @see CameraSettings#setPhotoSize |
| * @see CameraSettings#setPreviewSize |
| */ |
| // XXX: Despite the above documentation about locking the sizes, the API |
| // 1 implementation doesn't currently enforce this at all, although the |
| // Camera class warns that preview sizes shouldn't be changed while a |
| // preview is running. Furthermore, the API 2 implementation doesn't yet |
| // unlock the sizes when stopPreview() is invoked (see related FIXME on |
| // the STOP_PREVIEW case in its handler; in the meantime, changing API 2 |
| // sizes would require closing and reopening the camera. |
| public void setPreviewTexture(final SurfaceTexture surfaceTexture) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Blocks until a {@link android.graphics.SurfaceTexture} has been set |
| * for preview. |
| * |
| * <p>Note that, once this operation has been performed, it is no longer |
| * possible to change the preview or photo sizes in the |
| * {@link CameraSettings} instance for this camera, and the mutators for |
| * these fields are allowed to ignore all further invocations.</p> |
| * |
| * @param surfaceTexture The {@link SurfaceTexture} for preview. |
| * |
| * @see CameraSettings#setPhotoSize |
| * @see CameraSettings#setPreviewSize |
| */ |
| public void setPreviewTextureSync(final SurfaceTexture surfaceTexture) { |
| // Don't bother to wait since camera is in bad state. |
| if (getCameraState().isInvalid()) { |
| return; |
| } |
| final WaitDoneBundle bundle = new WaitDoneBundle(); |
| try { |
| getDispatchThread().runJobSync(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture) |
| .sendToTarget(); |
| getCameraHandler().post(bundle.mUnlockRunnable); |
| }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "set preview texture"); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets the {@link android.view.SurfaceHolder} for preview. |
| * |
| * @param surfaceHolder The {@link SurfaceHolder} for preview. |
| */ |
| public void setPreviewDisplay(final SurfaceHolder surfaceHolder) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Starts the camera preview. |
| */ |
| public void startPreview() { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.START_PREVIEW_ASYNC, null).sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Starts the camera preview and executes a callback on a handler once |
| * the preview starts. |
| */ |
| public void startPreviewWithCallback(final Handler h, final CameraStartPreviewCallback cb) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().obtainMessage(CameraActions.START_PREVIEW_ASYNC, |
| CameraStartPreviewCallbackForward.getNewInstance(h, cb)) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Stops the camera preview synchronously. |
| * {@code stopPreview()} must be synchronous to ensure that the caller can |
| * continues to release resources related to camera preview. |
| */ |
| public void stopPreview() { |
| // Don't bother to wait since camera is in bad state. |
| if (getCameraState().isInvalid()) { |
| return; |
| } |
| final WaitDoneBundle bundle = new WaitDoneBundle(); |
| try { |
| getDispatchThread().runJobSync(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().obtainMessage(CameraActions.STOP_PREVIEW, bundle) |
| .sendToTarget(); |
| }}, bundle.mWaitLock, CAMERA_OPERATION_TIMEOUT_MS, "stop preview"); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets the callback for preview data. |
| * |
| * @param handler The {@link android.os.Handler} in which the callback was handled. |
| * @param cb The callback to be invoked when the preview data is available. |
| * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) |
| */ |
| public abstract void setPreviewDataCallback(Handler handler, CameraPreviewDataCallback cb); |
| |
| /** |
| * Sets the one-time callback for preview data. |
| * |
| * @param handler The {@link android.os.Handler} in which the callback was handled. |
| * @param cb The callback to be invoked when the preview data for |
| * next frame is available. |
| * @see android.hardware.Camera#setPreviewCallback(android.hardware.Camera.PreviewCallback) |
| */ |
| public abstract void setOneShotPreviewCallback(Handler handler, |
| CameraPreviewDataCallback cb); |
| |
| /** |
| * Sets the callback for preview data. |
| * |
| * @param handler The handler in which the callback will be invoked. |
| * @param cb The callback to be invoked when the preview data is available. |
| * @see android.hardware.Camera#setPreviewCallbackWithBuffer(android.hardware.Camera.PreviewCallback) |
| */ |
| public abstract void setPreviewDataCallbackWithBuffer(Handler handler, |
| CameraPreviewDataCallback cb); |
| |
| /** |
| * Adds buffer for the preview callback. |
| * |
| * @param callbackBuffer The buffer allocated for the preview data. |
| */ |
| public void addCallbackBuffer(final byte[] callbackBuffer) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.ADD_CALLBACK_BUFFER, callbackBuffer) |
| .sendToTarget(); |
| } |
| }); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Starts the auto-focus process. The result will be returned through the callback. |
| * |
| * @param handler The handler in which the callback will be invoked. |
| * @param cb The auto-focus callback. |
| */ |
| public abstract void autoFocus(Handler handler, CameraAFCallback cb); |
| |
| /** |
| * Cancels the auto-focus process. |
| * |
| * <p>This action has the highest priority and will get processed before anything |
| * else that is pending. Moreover, any pending auto-focuses that haven't yet |
| * began will also be ignored.</p> |
| */ |
| public void cancelAutoFocus() { |
| // Do not use the dispatch thread since we want to avoid a wait-cycle |
| // between applySettingsHelper which waits until the state is not FOCUSING. |
| // cancelAutoFocus should get executed asap, set the state back to idle. |
| getCameraHandler().sendMessageAtFrontOfQueue( |
| getCameraHandler().obtainMessage(CameraActions.CANCEL_AUTO_FOCUS)); |
| getCameraHandler().sendEmptyMessage(CameraActions.CANCEL_AUTO_FOCUS_FINISH); |
| } |
| |
| /** |
| * Sets the auto-focus callback |
| * |
| * @param handler The handler in which the callback will be invoked. |
| * @param cb The callback to be invoked when the preview data is available. |
| */ |
| @TargetApi(Build.VERSION_CODES.JELLY_BEAN) |
| public abstract void setAutoFocusMoveCallback(Handler handler, CameraAFMoveCallback cb); |
| |
| /** |
| * Instrument the camera to take a picture. |
| * |
| * @param handler The handler in which the callback will be invoked. |
| * @param shutter The callback for shutter action, may be null. |
| * @param raw The callback for uncompressed data, may be null. |
| * @param postview The callback for postview image data, may be null. |
| * @param jpeg The callback for jpeg image data, may be null. |
| * @see android.hardware.Camera#takePicture( |
| * android.hardware.Camera.ShutterCallback, |
| * android.hardware.Camera.PictureCallback, |
| * android.hardware.Camera.PictureCallback) |
| */ |
| public abstract void takePicture( |
| Handler handler, |
| CameraShutterCallback shutter, |
| CameraPictureCallback raw, |
| CameraPictureCallback postview, |
| CameraPictureCallback jpeg); |
| |
| /** |
| * Sets the display orientation for camera to adjust the preview and JPEG orientation. |
| * |
| * @param degrees The counterclockwise rotation in degrees, relative to the device's natural |
| * orientation. Should be 0, 90, 180 or 270. |
| */ |
| public void setDisplayOrientation(final int degrees) { |
| setDisplayOrientation(degrees, true); |
| } |
| |
| /** |
| * Sets the display orientation for camera to adjust the preview—and, optionally, |
| * JPEG—orientations. |
| * <p>If capture rotation is not requested, future captures will be returned in the sensor's |
| * physical rotation, which does not necessarily match the device's natural orientation.</p> |
| * |
| * @param degrees The counterclockwise rotation in degrees, relative to the device's natural |
| * orientation. Should be 0, 90, 180 or 270. |
| * @param capture Whether to adjust the JPEG capture orientation as well as the preview one. |
| */ |
| public void setDisplayOrientation(final int degrees, final boolean capture) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.SET_DISPLAY_ORIENTATION, degrees, |
| capture ? 1 : 0) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| public void setJpegOrientation(final int degrees) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.SET_JPEG_ORIENTATION, degrees, 0) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets the listener for zoom change. |
| * |
| * @param listener The listener. |
| */ |
| public abstract void setZoomChangeListener(OnZoomChangeListener listener); |
| |
| /** |
| * Sets the face detection listener. |
| * |
| * @param handler The handler in which the callback will be invoked. |
| * @param callback The callback for face detection results. |
| */ |
| public abstract void setFaceDetectionCallback(Handler handler, |
| CameraFaceDetectionCallback callback); |
| |
| /** |
| * Starts the face detection. |
| */ |
| public void startFaceDetection() { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().sendEmptyMessage(CameraActions.START_FACE_DETECTION); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Stops the face detection. |
| */ |
| public void stopFaceDetection() { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().sendEmptyMessage(CameraActions.STOP_FACE_DETECTION); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Sets the camera parameters. |
| * |
| * @param params The camera parameters to use. |
| */ |
| @Deprecated |
| public abstract void setParameters(Camera.Parameters params); |
| |
| /** |
| * Gets the current camera parameters synchronously. This method is |
| * synchronous since the caller has to wait for the camera to return |
| * the parameters. If the parameters are already cached, it returns |
| * immediately. |
| */ |
| @Deprecated |
| public abstract Camera.Parameters getParameters(); |
| |
| /** |
| * Gets the current camera settings synchronously. |
| * <p>This method is synchronous since the caller has to wait for the |
| * camera to return the parameters. If the parameters are already |
| * cached, it returns immediately.</p> |
| */ |
| public abstract CameraSettings getSettings(); |
| |
| /** |
| * Default implementation of {@link #applySettings(CameraSettings)} |
| * that is only missing the set of states it needs to wait for |
| * before applying the settings. |
| * |
| * @param settings The settings to use on the device. |
| * @param statesToAwait Bitwise OR of the required camera states. |
| * @return Whether the settings can be applied. |
| */ |
| protected boolean applySettingsHelper(CameraSettings settings, |
| final int statesToAwait) { |
| if (settings == null) { |
| Log.v(TAG, "null argument in applySettings()"); |
| return false; |
| } |
| if (!getCapabilities().supports(settings)) { |
| Log.w(TAG, "Unsupported settings in applySettings()"); |
| return false; |
| } |
| |
| final CameraSettings copyOfSettings = settings.copy(); |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| CameraStateHolder cameraState = getCameraState(); |
| // Don't bother to wait since camera is in bad state. |
| if (cameraState.isInvalid()) { |
| return; |
| } |
| cameraState.waitForStates(statesToAwait); |
| getCameraHandler().obtainMessage(CameraActions.APPLY_SETTINGS, copyOfSettings) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| return true; |
| } |
| |
| /** |
| * Applies the settings to the camera device. |
| * |
| * <p>If the camera is either focusing or capturing; settings applications |
| * will be (asynchronously) deferred until those operations complete.</p> |
| * |
| * @param settings The settings to use on the device. |
| * @return Whether the settings can be applied. |
| */ |
| public abstract boolean applySettings(CameraSettings settings); |
| |
| /** |
| * Forces {@code CameraProxy} to update the cached version of the camera |
| * settings regardless of the dirty bit. |
| */ |
| public void refreshSettings() { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler().sendEmptyMessage(CameraActions.REFRESH_PARAMETERS); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Enables/Disables the camera shutter sound. |
| * |
| * @param enable {@code true} to enable the shutter sound, |
| * {@code false} to disable it. |
| */ |
| public void enableShutterSound(final boolean enable) { |
| try { |
| getDispatchThread().runJob(new Runnable() { |
| @Override |
| public void run() { |
| getCameraHandler() |
| .obtainMessage(CameraActions.ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0) |
| .sendToTarget(); |
| }}); |
| } catch (final RuntimeException ex) { |
| getAgent().getCameraExceptionHandler().onDispatchThreadException(ex); |
| } |
| } |
| |
| /** |
| * Dumps the current settings of the camera device. |
| * |
| * <p>The content varies based on the underlying camera API settings |
| * implementation.</p> |
| * |
| * @return The content of the device settings represented by a string. |
| */ |
| public abstract String dumpDeviceSettings(); |
| |
| /** |
| * @return The handler to which camera tasks should be posted. |
| */ |
| public abstract Handler getCameraHandler(); |
| |
| /** |
| * @return The thread used on which client callbacks are served. |
| */ |
| public abstract DispatchThread getDispatchThread(); |
| |
| /** |
| * @return The state machine tracking the camera API's current mode. |
| */ |
| public abstract CameraStateHolder getCameraState(); |
| } |
| |
| public static class WaitDoneBundle { |
| public final Runnable mUnlockRunnable; |
| public final Object mWaitLock; |
| |
| WaitDoneBundle() { |
| mWaitLock = new Object(); |
| mUnlockRunnable = new Runnable() { |
| @Override |
| public void run() { |
| synchronized (mWaitLock) { |
| mWaitLock.notifyAll(); |
| } |
| }}; |
| } |
| |
| /** |
| * Notify all synchronous waiters waiting on message completion with {@link #mWaitLock}. |
| * |
| * <p>This assumes that the message was sent with {@code this} as the {@code Message#obj}. |
| * Otherwise the message is ignored.</p> |
| */ |
| /*package*/ static void unblockSyncWaiters(Message msg) { |
| if (msg == null) return; |
| |
| if (msg.obj instanceof WaitDoneBundle) { |
| WaitDoneBundle bundle = (WaitDoneBundle)msg.obj; |
| bundle.mUnlockRunnable.run(); |
| } |
| } |
| } |
| } |