/*
|
* 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();
|
}
|
}
|
}
|
}
|