blob: c6f5d62e95bead2caa999f0de98fe8c1da26683e [file] [log] [blame]
/*
* Copyright (C) 2013 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.camera.app;
import android.content.Context;
import android.os.Handler;
import com.android.camera.CameraDisabledException;
import com.android.camera.debug.Log;
import com.android.camera.util.CameraUtil;
import com.android.camera.util.GservicesHelper;
import com.android.ex.camera2.portability.CameraAgent;
import com.android.ex.camera2.portability.CameraDeviceInfo;
import com.android.ex.camera2.portability.CameraExceptionHandler;
/**
* A class which implements {@link com.android.camera.app.CameraProvider} used
* by {@link com.android.camera.CameraActivity}.
* TODO: Make this class package private.
*/
public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider {
private static final Log.Tag TAG = new Log.Tag("CameraController");
private static final int EMPTY_REQUEST = -1;
private final Context mContext;
private CameraAgent.CameraOpenCallback mCallbackReceiver;
private final Handler mCallbackHandler;
private final CameraAgent mCameraAgent;
private final CameraAgent mCameraAgentNg;
/** The one for the API that is currently in use (deprecated one by default). */
private CameraDeviceInfo mInfo;
private CameraAgent.CameraProxy mCameraProxy;
private int mRequestingCameraId = EMPTY_REQUEST;
/**
* Determines which of mCameraAgent and mCameraAgentNg is currently in use.
* <p>It's only possible to enable this if the new API is actually
* supported.</p>
*/
private boolean mUsingNewApi = false;
/**
* Constructor.
*
* @param context The {@link android.content.Context} used to check if the
* camera is disabled.
* @param handler The {@link android.os.Handler} to post the camera
* callbacks to.
* @param cameraManager Used for camera open/close.
* @param cameraManagerNg Used for camera open/close with the new API. If
* {@code null} or the same object as
* {@code cameraManager}, the new API will not be
* exposed and requests for it will get the old one.
*/
public CameraController(Context context, CameraAgent.CameraOpenCallback callbackReceiver,
Handler handler, CameraAgent cameraManager, CameraAgent cameraManagerNg) {
mContext = context;
mCallbackReceiver = callbackReceiver;
mCallbackHandler = handler;
mCameraAgent = cameraManager;
// If the new implementation is the same as the old, the
// CameraAgentFactory decided this device doesn't support the new API.
mCameraAgentNg = cameraManagerNg != cameraManager ? cameraManagerNg : null;
mInfo = mCameraAgent.getCameraDeviceInfo();
if (mInfo == null && mCallbackReceiver != null) {
mCallbackReceiver.onDeviceOpenFailure(-1, "GETTING_CAMERA_INFO");
}
}
@Override
public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
mCameraAgent.setCameraExceptionHandler(exceptionHandler);
if (mCameraAgentNg != null) {
mCameraAgentNg.setCameraExceptionHandler(exceptionHandler);
}
}
@Override
public CameraDeviceInfo.Characteristics getCharacteristics(int cameraId) {
if (mInfo == null) {
return null;
}
return mInfo.getCharacteristics(cameraId);
}
@Override
public int getCurrentCameraId() {
if (mCameraProxy != null) {
return mCameraProxy.getCameraId();
} else {
Log.v(TAG, "getCurrentCameraId without an open camera... returning requested id");
return mRequestingCameraId;
}
}
@Override
public int getNumberOfCameras() {
if (mInfo == null) {
return 0;
}
return mInfo.getNumberOfCameras();
}
@Override
public int getFirstBackCameraId() {
if (mInfo == null) {
return -1;
}
return mInfo.getFirstBackCameraId();
}
@Override
public int getFirstFrontCameraId() {
if (mInfo == null) {
return -1;
}
return mInfo.getFirstFrontCameraId();
}
@Override
public boolean isFrontFacingCamera(int id) {
if (mInfo == null) {
return false;
}
if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
Log.e(TAG, "Camera info not available:" + id);
return false;
}
return mInfo.getCharacteristics(id).isFacingFront();
}
@Override
public boolean isBackFacingCamera(int id) {
if (mInfo == null) {
return false;
}
if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
Log.e(TAG, "Camera info not available:" + id);
return false;
}
return mInfo.getCharacteristics(id).isFacingBack();
}
@Override
public void onCameraOpened(CameraAgent.CameraProxy camera) {
Log.v(TAG, "onCameraOpened");
if (mRequestingCameraId != camera.getCameraId()) {
return;
}
mCameraProxy = camera;
mRequestingCameraId = EMPTY_REQUEST;
if (mCallbackReceiver != null) {
mCallbackReceiver.onCameraOpened(camera);
}
}
@Override
public void onCameraDisabled(int cameraId) {
if (mCallbackReceiver != null) {
mCallbackReceiver.onCameraDisabled(cameraId);
}
}
@Override
public void onDeviceOpenFailure(int cameraId, String info) {
if (mCallbackReceiver != null) {
mCallbackReceiver.onDeviceOpenFailure(cameraId, info);
}
}
@Override
public void onDeviceOpenedAlready(int cameraId, String info) {
if (mCallbackReceiver != null) {
mCallbackReceiver.onDeviceOpenedAlready(cameraId, info);
}
}
@Override
public void onReconnectionFailure(CameraAgent mgr, String info) {
if (mCallbackReceiver != null) {
mCallbackReceiver.onReconnectionFailure(mgr, info);
}
}
@Override
public void requestCamera(int id) {
requestCamera(id, false);
}
@Override
public void requestCamera(int id, boolean useNewApi) {
Log.v(TAG, "requestCamera");
// Based on
// (mRequestingCameraId == id, mRequestingCameraId == EMPTY_REQUEST),
// we have (T, T), (T, F), (F, T), (F, F).
// (T, T): implies id == EMPTY_REQUEST. We don't allow this to happen
// here. Return.
// (F, F): A previous request hasn't been fulfilled yet. Return.
// (T, F): Already requested the same camera. No-op. Return.
// (F, T): Nothing is going on. Continue.
if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) {
return;
}
if (mInfo == null) {
return;
}
mRequestingCameraId = id;
// Only actually use the new API if it's supported on this device.
useNewApi = mCameraAgentNg != null && useNewApi;
CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
if (mCameraProxy == null) {
// No camera yet.
checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this);
} else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) {
boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext);
Log.v(TAG, "different camera already opened, closing then reopening");
// Already has camera opened, and is switching cameras and/or APIs.
if (mUsingNewApi) {
mCameraAgentNg.closeCamera(mCameraProxy, true);
} else {
// if using API2 ensure API1 usage is also synced
mCameraAgent.closeCamera(mCameraProxy, syncClose);
}
checkAndOpenCamera(mContext, cameraManager, id, mCallbackHandler, this);
} else {
// The same camera, just do a reconnect.
Log.v(TAG, "reconnecting to use the existing camera");
mCameraProxy.reconnect(mCallbackHandler, this);
mCameraProxy = null;
}
mUsingNewApi = useNewApi;
mInfo = cameraManager.getCameraDeviceInfo();
}
@Override
public boolean waitingForCamera() {
return mRequestingCameraId != EMPTY_REQUEST;
}
@Override
public void releaseCamera(int id) {
if (mCameraProxy == null) {
if (mRequestingCameraId == EMPTY_REQUEST) {
// Camera not requested yet.
Log.w(TAG, "Trying to release the camera before requesting");
}
// Camera requested but not available yet.
mRequestingCameraId = EMPTY_REQUEST;
return;
}
if (mCameraProxy.getCameraId() != id) {
throw new IllegalStateException("Trying to release an unopened camera.");
}
mRequestingCameraId = EMPTY_REQUEST;
}
public void removeCallbackReceiver() {
mCallbackReceiver = null;
}
/**
* Closes the opened camera device.
* TODO: Make this method package private.
*/
public void closeCamera(boolean synced) {
Log.v(TAG, "Closing camera");
mCameraProxy = null;
if (mUsingNewApi) {
mCameraAgentNg.closeCamera(mCameraProxy, synced);
} else {
mCameraAgent.closeCamera(mCameraProxy, synced);
}
mRequestingCameraId = EMPTY_REQUEST;
mUsingNewApi = false;
}
private static void checkAndOpenCamera(Context context, CameraAgent cameraManager,
final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) {
Log.v(TAG, "checkAndOpenCamera");
try {
CameraUtil.throwIfCameraDisabled(context);
cameraManager.openCamera(handler, cameraId, cb);
} catch (CameraDisabledException ex) {
handler.post(new Runnable() {
@Override
public void run() {
cb.onCameraDisabled(cameraId);
}
});
}
}
public void setOneShotPreviewCallback(Handler handler,
CameraAgent.CameraPreviewDataCallback cb) {
mCameraProxy.setOneShotPreviewCallback(handler, cb);
}
}