/* * 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.device.ActiveCameraDeviceTracker; import com.android.camera.device.CameraId; 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; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * 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 final Handler mCallbackHandler; private final CameraAgent mCameraAgent; private final CameraAgent mCameraAgentNg; private final ActiveCameraDeviceTracker mActiveCameraDeviceTracker; private CameraAgent.CameraOpenCallback mCallbackReceiver; /** 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. *

It's only possible to enable this if the new API is actually * supported.

*/ 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. * @param activeCameraDeviceTracker Tracks the active device across multiple * api versions and implementations. */ public CameraController(@Nonnull Context context, @Nullable CameraAgent.CameraOpenCallback callbackReceiver, @Nonnull Handler handler, @Nonnull CameraAgent cameraManager, @Nonnull CameraAgent cameraManagerNg, @Nonnull ActiveCameraDeviceTracker activeCameraDeviceTracker) { 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; mActiveCameraDeviceTracker = activeCameraDeviceTracker; 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 @Deprecated public CameraId getCurrentCameraId() { return mActiveCameraDeviceTracker.getActiveOrPreviousCamera(); } @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; mActiveCameraDeviceTracker.onCameraOpening(CameraId.fromLegacyId(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(cameraManager, id, mCallbackHandler, this); } else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) { boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext .getContentResolver()); 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(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; } int currentId = mCameraProxy.getCameraId(); if (currentId != id) { if (mRequestingCameraId == id) { Log.w(TAG, "Releasing camera which was requested but not yet " + "opened (current:requested): " + currentId + ":" + id); } else { throw new IllegalStateException("Trying to release a camera neither opened" + "nor requested (current:requested:for-release): " + currentId + ":" + mRequestingCameraId + ":" + id); } } mActiveCameraDeviceTracker.onCameraClosed(CameraId.fromLegacyId(id)); 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(CameraAgent cameraManager, final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) { Log.v(TAG, "checkAndOpenCamera"); try { CameraUtil.throwIfCameraDisabled(); 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); } }