diff options
Diffstat (limited to 'src/com/android/camera/CameraManager.java')
-rw-r--r-- | src/com/android/camera/CameraManager.java | 670 |
1 files changed, 246 insertions, 424 deletions
diff --git a/src/com/android/camera/CameraManager.java b/src/com/android/camera/CameraManager.java index c7005cf54..e4a066701 100644 --- a/src/com/android/camera/CameraManager.java +++ b/src/com/android/camera/CameraManager.java @@ -16,466 +16,288 @@ package com.android.camera; -import static com.android.camera.Util.Assert; - import android.annotation.TargetApi; import android.graphics.SurfaceTexture; -import android.hardware.Camera.AutoFocusCallback; -import android.hardware.Camera.AutoFocusMoveCallback; import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.FaceDetectionListener; import android.hardware.Camera.OnZoomChangeListener; import android.hardware.Camera.Parameters; -import android.hardware.Camera.PictureCallback; -import android.hardware.Camera.PreviewCallback; -import android.hardware.Camera.ShutterCallback; import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.util.Log; import android.view.SurfaceHolder; import com.android.gallery3d.common.ApiHelper; import java.io.IOException; -public class CameraManager { - private static final String TAG = "CameraManager"; - private static CameraManager sCameraManager = new CameraManager(); - - private Parameters mParameters; - private boolean mParametersIsDirty; - private IOException mReconnectIOException; - - private static final int RELEASE = 1; - private static final int RECONNECT = 2; - private static final int UNLOCK = 3; - private static final int LOCK = 4; - private static final int SET_PREVIEW_TEXTURE_ASYNC = 5; - private static final int START_PREVIEW_ASYNC = 6; - private static final int STOP_PREVIEW = 7; - private static final int SET_PREVIEW_CALLBACK_WITH_BUFFER = 8; - private static final int ADD_CALLBACK_BUFFER = 9; - private static final int AUTO_FOCUS = 10; - private static final int CANCEL_AUTO_FOCUS = 11; - private static final int SET_AUTO_FOCUS_MOVE_CALLBACK = 12; - private static final int SET_DISPLAY_ORIENTATION = 13; - private static final int SET_ZOOM_CHANGE_LISTENER = 14; - private static final int SET_FACE_DETECTION_LISTENER = 15; - private static final int START_FACE_DETECTION = 16; - private static final int STOP_FACE_DETECTION = 17; - private static final int SET_ERROR_CALLBACK = 18; - private static final int SET_PARAMETERS = 19; - private static final int GET_PARAMETERS = 20; - private static final int SET_PREVIEW_DISPLAY_ASYNC = 21; - private static final int SET_PREVIEW_CALLBACK = 22; - private static final int ENABLE_SHUTTER_SOUND = 23; - private static final int REFRESH_PARAMETERS = 24; - - private Handler mCameraHandler; - private android.hardware.Camera mCamera; - - // Used to retain a copy of Parameters for setting parameters. - private Parameters mParamsToSet; - - - // This holder is used when we need to pass the exception - // back to the calling thread. SynchornousQueue doesn't - // allow we to pass a null object thus a holder is needed. - private class IOExceptionHolder { - public IOException ex; +/** + * An interface which provides possible camera device operations. + * + * The client should call {@code CameraManager.cameraOpen} to get an instance + * of {@link CameraManager.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.FaceDetectionListener}, + * {@code android.hardware.Camera.OnZoomChangeListener}, and + * {@code android.hardware.Camera.Parameters}. + */ +public interface CameraManager { + + /** + * An interface which wraps + * {@link android.hardware.Camera.AutoFocusCallback}. + */ + public interface CameraAFCallback { + public void onAutoFocus(boolean focused, CameraProxy camera); } - public static CameraManager instance() { - return sCameraManager; + /** + * An interface which wraps + * {@link android.hardware.Camera.AutoFocusMoveCallback}. + */ + public interface CameraAFMoveCallback { + public void onAutoFocusMoving(boolean moving, CameraProxy camera); } - private CameraManager() { - HandlerThread ht = new HandlerThread("Camera Handler Thread"); - ht.start(); - mCameraHandler = new CameraHandler(ht.getLooper()); + /** + * An interface which wraps + * {@link android.hardware.Camera.ShutterCallback}. + */ + public interface CameraShutterCallback { + public void onShutter(CameraProxy camera); } - private class CameraHandler extends Handler { - CameraHandler(Looper looper) { - super(looper); - } - - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void startFaceDetection() { - mCamera.startFaceDetection(); - } + /** + * An interface which wraps + * {@link android.hardware.Camera.PictureCallback}. + */ + public interface CameraPictureCallback { + public void onPictureTaken(byte[] data, CameraProxy camera); + } - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void stopFaceDetection() { - mCamera.stopFaceDetection(); - } + /** + * An interface which wraps + * {@link android.hardware.Camera.PreviewCallback}. + */ + public interface CameraPreviewDataCallback { + public void onPreviewFrame(byte[] data, CameraProxy camera); + } - @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - private void setFaceDetectionListener(FaceDetectionListener listener) { - mCamera.setFaceDetectionListener(listener); - } + /** + * Opens the camera of the specified ID synchronously. + * + * @param cameraId The camera ID to open. + * @return An instance of {@link CameraProxy} on success. null on failure. + */ + public CameraProxy cameraOpen(int cameraId); + + /** + * 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 interface 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. + */ + public android.hardware.Camera getCamera(); - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) - private void setPreviewTexture(Object surfaceTexture) { - try { - mCamera.setPreviewTexture((SurfaceTexture) surfaceTexture); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN_MR1) - private void enableShutterSound(boolean enable) { - mCamera.enableShutterSound(enable); - } - - /* - * This method does not deal with the build version check. Everyone should - * check first before sending message to this handler. + /** + * Releases the camera device synchronously. + * This function must be synchronous so the caller knows exactly when the camera + * is released and can continue on. */ - @Override - public void handleMessage(final Message msg) { - try { - switch (msg.what) { - case RELEASE: - mCamera.release(); - mCamera = null; - return; - - case RECONNECT: - mReconnectIOException = null; - try { - mCamera.reconnect(); - } catch (IOException ex) { - mReconnectIOException = ex; - } - return; - - case UNLOCK: - mCamera.unlock(); - return; - - case LOCK: - mCamera.lock(); - return; - - case SET_PREVIEW_TEXTURE_ASYNC: - setPreviewTexture(msg.obj); - return; - - case SET_PREVIEW_DISPLAY_ASYNC: - try { - mCamera.setPreviewDisplay((SurfaceHolder) msg.obj); - } catch (IOException e) { - throw new RuntimeException(e); - } - return; - - case START_PREVIEW_ASYNC: - mCamera.startPreview(); - return; - - case STOP_PREVIEW: - mCamera.stopPreview(); - return; - - case SET_PREVIEW_CALLBACK_WITH_BUFFER: - mCamera.setPreviewCallbackWithBuffer( - (PreviewCallback) msg.obj); - return; - - case ADD_CALLBACK_BUFFER: - mCamera.addCallbackBuffer((byte[]) msg.obj); - return; - - case AUTO_FOCUS: - mCamera.autoFocus((AutoFocusCallback) msg.obj); - return; - - case CANCEL_AUTO_FOCUS: - mCamera.cancelAutoFocus(); - return; - - case SET_AUTO_FOCUS_MOVE_CALLBACK: - setAutoFocusMoveCallback(mCamera, msg.obj); - return; - - case SET_DISPLAY_ORIENTATION: - mCamera.setDisplayOrientation(msg.arg1); - return; - - case SET_ZOOM_CHANGE_LISTENER: - mCamera.setZoomChangeListener( - (OnZoomChangeListener) msg.obj); - return; - - case SET_FACE_DETECTION_LISTENER: - setFaceDetectionListener((FaceDetectionListener) msg.obj); - return; - - case START_FACE_DETECTION: - startFaceDetection(); - return; - - case STOP_FACE_DETECTION: - stopFaceDetection(); - return; - - case SET_ERROR_CALLBACK: - mCamera.setErrorCallback((ErrorCallback) msg.obj); - return; - - case SET_PARAMETERS: - mParametersIsDirty = true; - mParamsToSet.unflatten((String) msg.obj); - mCamera.setParameters(mParamsToSet); - return; - - case GET_PARAMETERS: - if (mParametersIsDirty) { - mParameters = mCamera.getParameters(); - mParametersIsDirty = false; - } - return; - - case SET_PREVIEW_CALLBACK: - mCamera.setPreviewCallback((PreviewCallback) msg.obj); - return; - - case ENABLE_SHUTTER_SOUND: - enableShutterSound((msg.arg1 == 1) ? true : false); - return; - - case REFRESH_PARAMETERS: - mParametersIsDirty = true; - return; - - default: - throw new RuntimeException("Invalid CameraProxy message=" + msg.what); - } - } catch (RuntimeException e) { - if (msg.what != RELEASE && mCamera != null) { - try { - mCamera.release(); - } catch (Exception ex) { - Log.e(TAG, "Fail to release the camera."); - } - mCamera = null; - } - throw e; - } - } - } + public void release(); - @TargetApi(ApiHelper.VERSION_CODES.JELLY_BEAN) - private void setAutoFocusMoveCallback(android.hardware.Camera camera, - Object cb) { - camera.setAutoFocusMoveCallback((AutoFocusMoveCallback) cb); - } + /** + * Reconnects to the camera device. + * + * @see android.hardware.Camera#reconnect() + */ + public void reconnect() throws IOException; - // Open camera synchronously. This method is invoked in the context of a - // background thread. - CameraProxy cameraOpen(int cameraId) { - // Cannot open camera in mCameraHandler, otherwise all camera events - // will be routed to mCameraHandler looper, which in turn will call - // event handler like Camera.onFaceDetection, which in turn will modify - // UI and cause exception like this: - // CalledFromWrongThreadException: Only the original thread that created - // a view hierarchy can touch its views. - mCamera = android.hardware.Camera.open(cameraId); - if (mCamera != null) { - mParametersIsDirty = true; - if (mParamsToSet == null) { - mParamsToSet = mCamera.getParameters(); - } - return new CameraProxy(); - } else { - return null; - } - } + /** + * Unlocks the camera device. + * + * @see android.hardware.Camera#unlock() + */ + public void unlock(); - public class CameraProxy { + /** + * Locks the camera device. + * @see android.hardware.Camera#lock() + */ + public void lock(); - private CameraProxy() { - Assert(mCamera != null); - } + /** + * Sets the {@link android.graphics.SurfaceTexture} for preview. + * + * @param surfaceTexture The {@link SurfaceTexture} for preview. + */ + @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) + public void setPreviewTexture(final SurfaceTexture surfaceTexture); - public android.hardware.Camera getCamera() { - return mCamera; - } + /** + * Sets the {@link android.view.SurfaceHolder} for preview. + * + * @param surfaceHolder The {@link SurfaceHolder} for preview. + */ + public void setPreviewDisplay(final SurfaceHolder surfaceHolder); - public void release() { - // release() must be synchronous so we know exactly when the camera - // is released and can continue on. - mCameraHandler.sendEmptyMessage(RELEASE); - waitDone(); - } + /** + * Starts the camera preview. + */ + public void startPreview(); - public void reconnect() throws IOException { - mCameraHandler.sendEmptyMessage(RECONNECT); - waitDone(); - if (mReconnectIOException != null) { - throw mReconnectIOException; - } - } + /** + * 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(); + + /** + * Sets the callback for preview data. + * + * @param handler 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 void setPreviewDataCallback(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 void setPreviewDataCallbackWithBuffer(Handler handler, CameraPreviewDataCallback cb); - public void unlock() { - mCameraHandler.sendEmptyMessage(UNLOCK); - } + /** + * Adds buffer for the preview callback. + * + * @param callbackBuffer The buffer allocated for the preview data. + */ + public void addCallbackBuffer(byte[] callbackBuffer); - public void lock() { - mCameraHandler.sendEmptyMessage(LOCK); - } + /** + * 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 void autoFocus(Handler handler, CameraAFCallback cb); - @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB) - public void setPreviewTextureAsync(final SurfaceTexture surfaceTexture) { - mCameraHandler.obtainMessage(SET_PREVIEW_TEXTURE_ASYNC, surfaceTexture).sendToTarget(); - } - - public void setPreviewDisplayAsync(final SurfaceHolder surfaceHolder) { - mCameraHandler.obtainMessage(SET_PREVIEW_DISPLAY_ASYNC, surfaceHolder).sendToTarget(); - } - - public void startPreviewAsync() { - mCameraHandler.sendEmptyMessage(START_PREVIEW_ASYNC); - } - - // stopPreview() is synchronous because many resources should be released after - // the preview is stopped. - public void stopPreview() { - mCameraHandler.sendEmptyMessage(STOP_PREVIEW); - waitDone(); - } - - public void setPreviewCallback(final PreviewCallback cb) { - mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK, cb).sendToTarget(); - } - - public void setPreviewCallbackWithBuffer(final PreviewCallback cb) { - mCameraHandler.obtainMessage(SET_PREVIEW_CALLBACK_WITH_BUFFER, cb).sendToTarget(); - } - - public void addCallbackBuffer(byte[] callbackBuffer) { - mCameraHandler.obtainMessage(ADD_CALLBACK_BUFFER, callbackBuffer).sendToTarget(); - } - - public void autoFocus(AutoFocusCallback cb) { - mCameraHandler.obtainMessage(AUTO_FOCUS, cb).sendToTarget(); - } - - public void cancelAutoFocus() { - mCameraHandler.removeMessages(AUTO_FOCUS); - mCameraHandler.sendEmptyMessage(CANCEL_AUTO_FOCUS); - } + /** + * Cancels the auto-focus process. + */ + public void cancelAutoFocus(); + /** + * 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(ApiHelper.VERSION_CODES.JELLY_BEAN) - public void setAutoFocusMoveCallback(AutoFocusMoveCallback cb) { - mCameraHandler.obtainMessage(SET_AUTO_FOCUS_MOVE_CALLBACK, cb).sendToTarget(); - } - - public void takePicture(final ShutterCallback shutter, final PictureCallback raw, - final PictureCallback postview, final PictureCallback jpeg) { - // Too many parameters, so use post for simplicity - mCameraHandler.post(new Runnable() { - @Override - public void run() { - mCamera.takePicture(shutter, raw, postview, jpeg); - } - }); - } - - public void takePicture2(final ShutterCallback shutter, final PictureCallback raw, - final PictureCallback postview, final PictureCallback jpeg, - final int cameraState, final int focusState) { - // Too many parameters, so use post for simplicity - mCameraHandler.post(new Runnable() { - @Override - public void run() { - try { - mCamera.takePicture(shutter, raw, postview, jpeg); - } catch (RuntimeException e) { - Log.w(TAG, "take picture failed; cameraState:" + cameraState - + ", focusState:" + focusState); - throw e; - } - } - }); - } - - public void setDisplayOrientation(int degrees) { - mCameraHandler.obtainMessage(SET_DISPLAY_ORIENTATION, degrees, 0) - .sendToTarget(); - } - - public void setZoomChangeListener(OnZoomChangeListener listener) { - mCameraHandler.obtainMessage(SET_ZOOM_CHANGE_LISTENER, listener).sendToTarget(); - } + public 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 void takePicture( + Handler handler, + CameraShutterCallback shutter, + CameraPictureCallback raw, + CameraPictureCallback postview, + CameraPictureCallback jpeg); + + /** + * Sets the display orientation for camera to adjust the preview orientation. + * + * @param degrees The rotation in degrees. Should be 0, 90, 180 or 270. + */ + public void setDisplayOrientation(int degrees); + + /** + * Sets the listener for zoom change. + * + * @param listener The listener. + */ + public void setZoomChangeListener(OnZoomChangeListener listener); + /** + * Sets the face detection listener. + * + * @param listener The listener for face detection results. + */ @TargetApi(ApiHelper.VERSION_CODES.ICE_CREAM_SANDWICH) - public void setFaceDetectionListener(FaceDetectionListener listener) { - mCameraHandler.obtainMessage(SET_FACE_DETECTION_LISTENER, listener).sendToTarget(); - } - - public void startFaceDetection() { - mCameraHandler.sendEmptyMessage(START_FACE_DETECTION); - } - - public void stopFaceDetection() { - mCameraHandler.sendEmptyMessage(STOP_FACE_DETECTION); - } - - public void setErrorCallback(ErrorCallback cb) { - mCameraHandler.obtainMessage(SET_ERROR_CALLBACK, cb).sendToTarget(); - } - - public void setParameters(Parameters params) { - if (params == null) { - Log.v(TAG, "null parameters in setParameters()"); - return; - } - mCameraHandler.obtainMessage(SET_PARAMETERS, params.flatten()) - .sendToTarget(); - } - - public Parameters getParameters() { - mCameraHandler.sendEmptyMessage(GET_PARAMETERS); - waitDone(); - return mParameters; - } - - public void refreshParameters() { - mCameraHandler.sendEmptyMessage(REFRESH_PARAMETERS); - } - - public void enableShutterSound(boolean enable) { - mCameraHandler.obtainMessage( - ENABLE_SHUTTER_SOUND, (enable ? 1 : 0), 0).sendToTarget(); - } - - // return false if cancelled. - public boolean waitDone() { - final Object waitDoneLock = new Object(); - final Runnable unlockRunnable = new Runnable() { - @Override - public void run() { - synchronized (waitDoneLock) { - waitDoneLock.notifyAll(); - } - } - }; - - synchronized (waitDoneLock) { - mCameraHandler.post(unlockRunnable); - try { - waitDoneLock.wait(); - } catch (InterruptedException ex) { - Log.v(TAG, "waitDone interrupted"); - return false; - } - } - return true; - } + public void setFaceDetectionListener(FaceDetectionListener listener); + + /** + * Starts the face detection. + */ + public void startFaceDetection(); + + /** + * Stops the face detection. + */ + public void stopFaceDetection(); + + /** + * Registers an error callback. + * + * @param cb The error callback. + * @see android.hardware.Camera#setErrorCallback(android.hardware.Camera.ErrorCallback) + */ + public void setErrorCallback(ErrorCallback cb); + + /** + * Sets the camera parameters. + * + * @param params The camera parameters to use. + */ + public void setParameters(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. + */ + public Parameters getParameters(); + + /** + * Forces {@code CameraProxy} to update the cached version of the camera + * parameters regardless of the dirty bit. + */ + public void refreshParameters(); + + /** + * Enables/Disables the camera shutter sound. + * + * @param enable {@code true} to enable the shutter sound, + * {@code false} to disable it. + */ + public void enableShutterSound(boolean enable); } } |