Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.camera.app;
     18 
     19 import android.content.Context;
     20 import android.os.Handler;
     21 
     22 import com.android.camera.CameraDisabledException;
     23 import com.android.camera.debug.Log;
     24 import com.android.camera.device.ActiveCameraDeviceTracker;
     25 import com.android.camera.device.CameraId;
     26 import com.android.camera.util.CameraUtil;
     27 import com.android.camera.util.GservicesHelper;
     28 import com.android.ex.camera2.portability.CameraAgent;
     29 import com.android.ex.camera2.portability.CameraDeviceInfo;
     30 import com.android.ex.camera2.portability.CameraExceptionHandler;
     31 
     32 import javax.annotation.Nonnull;
     33 import javax.annotation.Nullable;
     34 
     35 /**
     36  * A class which implements {@link com.android.camera.app.CameraProvider} used
     37  * by {@link com.android.camera.CameraActivity}.
     38  * TODO: Make this class package private.
     39  */
     40 public class CameraController implements CameraAgent.CameraOpenCallback, CameraProvider {
     41     private static final Log.Tag TAG = new Log.Tag("CameraController");
     42     private static final int EMPTY_REQUEST = -1;
     43     private final Context mContext;
     44     private final Handler mCallbackHandler;
     45     private final CameraAgent mCameraAgent;
     46     private final CameraAgent mCameraAgentNg;
     47     private final ActiveCameraDeviceTracker mActiveCameraDeviceTracker;
     48 
     49     private CameraAgent.CameraOpenCallback mCallbackReceiver;
     50 
     51     /** The one for the API that is currently in use (deprecated one by default). */
     52     private CameraDeviceInfo mInfo;
     53 
     54     private CameraAgent.CameraProxy mCameraProxy;
     55     private int mRequestingCameraId = EMPTY_REQUEST;
     56 
     57     /**
     58      * Determines which of mCameraAgent and mCameraAgentNg is currently in use.
     59      * <p>It's only possible to enable this if the new API is actually
     60      * supported.</p>
     61      */
     62     private boolean mUsingNewApi = false;
     63 
     64     /**
     65      * Constructor.
     66      *
     67      * @param context The {@link android.content.Context} used to check if the
     68      *                camera is disabled.
     69      * @param handler The {@link android.os.Handler} to post the camera
     70      *                callbacks to.
     71      * @param cameraManager Used for camera open/close.
     72      * @param cameraManagerNg Used for camera open/close with the new API. If
     73      *                        {@code null} or the same object as
     74      *                        {@code cameraManager}, the new API will not be
     75      *                        exposed and requests for it will get the old one.
     76      * @param activeCameraDeviceTracker Tracks the active device across multiple
     77      *                                  api versions and implementations.
     78      */
     79     public CameraController(@Nonnull Context context,
     80           @Nullable CameraAgent.CameraOpenCallback callbackReceiver,
     81           @Nonnull Handler handler,
     82           @Nonnull CameraAgent cameraManager,
     83           @Nonnull CameraAgent cameraManagerNg,
     84           @Nonnull ActiveCameraDeviceTracker activeCameraDeviceTracker) {
     85         mContext = context;
     86         mCallbackReceiver = callbackReceiver;
     87         mCallbackHandler = handler;
     88         mCameraAgent = cameraManager;
     89         // If the new implementation is the same as the old, the
     90         // CameraAgentFactory decided this device doesn't support the new API.
     91         mCameraAgentNg = cameraManagerNg != cameraManager ? cameraManagerNg : null;
     92         mActiveCameraDeviceTracker = activeCameraDeviceTracker;
     93         mInfo = mCameraAgent.getCameraDeviceInfo();
     94         if (mInfo == null && mCallbackReceiver != null) {
     95             mCallbackReceiver.onDeviceOpenFailure(-1, "GETTING_CAMERA_INFO");
     96         }
     97     }
     98 
     99     @Override
    100     public void setCameraExceptionHandler(CameraExceptionHandler exceptionHandler) {
    101         mCameraAgent.setCameraExceptionHandler(exceptionHandler);
    102         if (mCameraAgentNg != null) {
    103             mCameraAgentNg.setCameraExceptionHandler(exceptionHandler);
    104         }
    105     }
    106 
    107     @Override
    108     public CameraDeviceInfo.Characteristics getCharacteristics(int cameraId) {
    109         if (mInfo == null) {
    110             return null;
    111         }
    112         return mInfo.getCharacteristics(cameraId);
    113     }
    114 
    115     @Override
    116     @Deprecated
    117     public CameraId getCurrentCameraId() {
    118         return mActiveCameraDeviceTracker.getActiveOrPreviousCamera();
    119     }
    120 
    121     @Override
    122     public int getNumberOfCameras() {
    123         if (mInfo == null) {
    124             return 0;
    125         }
    126         return mInfo.getNumberOfCameras();
    127     }
    128 
    129     @Override
    130     public int getFirstBackCameraId() {
    131         if (mInfo == null) {
    132             return -1;
    133         }
    134         return mInfo.getFirstBackCameraId();
    135     }
    136 
    137     @Override
    138     public int getFirstFrontCameraId() {
    139         if (mInfo == null) {
    140             return -1;
    141         }
    142         return mInfo.getFirstFrontCameraId();
    143     }
    144 
    145     @Override
    146     public boolean isFrontFacingCamera(int id) {
    147         if (mInfo == null) {
    148             return false;
    149         }
    150         if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
    151             Log.e(TAG, "Camera info not available:" + id);
    152             return false;
    153         }
    154         return mInfo.getCharacteristics(id).isFacingFront();
    155     }
    156 
    157     @Override
    158     public boolean isBackFacingCamera(int id) {
    159         if (mInfo == null) {
    160             return false;
    161         }
    162         if (id >= mInfo.getNumberOfCameras() || mInfo.getCharacteristics(id) == null) {
    163             Log.e(TAG, "Camera info not available:" + id);
    164             return false;
    165         }
    166         return mInfo.getCharacteristics(id).isFacingBack();
    167     }
    168 
    169     @Override
    170     public void onCameraOpened(CameraAgent.CameraProxy camera) {
    171         Log.v(TAG, "onCameraOpened");
    172         if (mRequestingCameraId != camera.getCameraId()) {
    173             return;
    174         }
    175         mCameraProxy = camera;
    176         mRequestingCameraId = EMPTY_REQUEST;
    177         if (mCallbackReceiver != null) {
    178             mCallbackReceiver.onCameraOpened(camera);
    179         }
    180     }
    181 
    182     @Override
    183     public void onCameraDisabled(int cameraId) {
    184         if (mCallbackReceiver != null) {
    185             mCallbackReceiver.onCameraDisabled(cameraId);
    186         }
    187     }
    188 
    189     @Override
    190     public void onDeviceOpenFailure(int cameraId, String info) {
    191         if (mCallbackReceiver != null) {
    192             mCallbackReceiver.onDeviceOpenFailure(cameraId, info);
    193         }
    194     }
    195 
    196     @Override
    197     public void onDeviceOpenedAlready(int cameraId, String info) {
    198         if (mCallbackReceiver != null) {
    199             mCallbackReceiver.onDeviceOpenedAlready(cameraId, info);
    200         }
    201     }
    202 
    203     @Override
    204     public void onReconnectionFailure(CameraAgent mgr, String info) {
    205         if (mCallbackReceiver != null) {
    206             mCallbackReceiver.onReconnectionFailure(mgr, info);
    207         }
    208     }
    209 
    210     @Override
    211     public void requestCamera(int id) {
    212         requestCamera(id, false);
    213     }
    214 
    215     @Override
    216     public void requestCamera(int id, boolean useNewApi) {
    217         Log.v(TAG, "requestCamera");
    218         // Based on
    219         // (mRequestingCameraId == id, mRequestingCameraId == EMPTY_REQUEST),
    220         // we have (T, T), (T, F), (F, T), (F, F).
    221         // (T, T): implies id == EMPTY_REQUEST. We don't allow this to happen
    222         //         here. Return.
    223         // (F, F): A previous request hasn't been fulfilled yet. Return.
    224         // (T, F): Already requested the same camera. No-op. Return.
    225         // (F, T): Nothing is going on. Continue.
    226         if (mRequestingCameraId != EMPTY_REQUEST || mRequestingCameraId == id) {
    227             return;
    228         }
    229         if (mInfo == null) {
    230             return;
    231         }
    232         mRequestingCameraId = id;
    233         mActiveCameraDeviceTracker.onCameraOpening(CameraId.fromLegacyId(id));
    234 
    235         // Only actually use the new API if it's supported on this device.
    236         useNewApi = mCameraAgentNg != null && useNewApi;
    237         CameraAgent cameraManager = useNewApi ? mCameraAgentNg : mCameraAgent;
    238 
    239         if (mCameraProxy == null) {
    240             // No camera yet.
    241             checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
    242         } else if (mCameraProxy.getCameraId() != id || mUsingNewApi != useNewApi) {
    243             boolean syncClose = GservicesHelper.useCamera2ApiThroughPortabilityLayer(mContext
    244                     .getContentResolver());
    245             Log.v(TAG, "different camera already opened, closing then reopening");
    246             // Already has camera opened, and is switching cameras and/or APIs.
    247             if (mUsingNewApi) {
    248                 mCameraAgentNg.closeCamera(mCameraProxy, true);
    249             } else {
    250                 // if using API2 ensure API1 usage is also synced
    251                 mCameraAgent.closeCamera(mCameraProxy, syncClose);
    252             }
    253             checkAndOpenCamera(cameraManager, id, mCallbackHandler, this);
    254         } else {
    255             // The same camera, just do a reconnect.
    256             Log.v(TAG, "reconnecting to use the existing camera");
    257             mCameraProxy.reconnect(mCallbackHandler, this);
    258             mCameraProxy = null;
    259         }
    260 
    261         mUsingNewApi = useNewApi;
    262         mInfo = cameraManager.getCameraDeviceInfo();
    263     }
    264 
    265     @Override
    266     public boolean waitingForCamera() {
    267         return mRequestingCameraId != EMPTY_REQUEST;
    268     }
    269 
    270     @Override
    271     public void releaseCamera(int id) {
    272         if (mCameraProxy == null) {
    273             if (mRequestingCameraId == EMPTY_REQUEST) {
    274                 // Camera not requested yet.
    275                 Log.w(TAG, "Trying to release the camera before requesting");
    276             }
    277             // Camera requested but not available yet.
    278             mRequestingCameraId = EMPTY_REQUEST;
    279             return;
    280         }
    281         int currentId = mCameraProxy.getCameraId();
    282         if (currentId != id) {
    283             if (mRequestingCameraId == id) {
    284                 Log.w(TAG, "Releasing camera which was requested but not yet "
    285                         + "opened (current:requested): " + currentId + ":" + id);
    286             } else {
    287                 throw new IllegalStateException("Trying to release a camera neither opened"
    288                         + "nor requested (current:requested:for-release): "
    289                         + currentId + ":" + mRequestingCameraId + ":" + id);
    290             }
    291         }
    292 
    293         mActiveCameraDeviceTracker.onCameraClosed(CameraId.fromLegacyId(id));
    294         mRequestingCameraId = EMPTY_REQUEST;
    295     }
    296 
    297     public void removeCallbackReceiver() {
    298         mCallbackReceiver = null;
    299     }
    300 
    301     /**
    302      * Closes the opened camera device.
    303      * TODO: Make this method package private.
    304      */
    305     public void closeCamera(boolean synced) {
    306         Log.v(TAG, "Closing camera");
    307         mCameraProxy = null;
    308         if (mUsingNewApi) {
    309             mCameraAgentNg.closeCamera(mCameraProxy, synced);
    310         } else {
    311             mCameraAgent.closeCamera(mCameraProxy, synced);
    312         }
    313         mRequestingCameraId = EMPTY_REQUEST;
    314         mUsingNewApi = false;
    315     }
    316 
    317     private static void checkAndOpenCamera(CameraAgent cameraManager,
    318             final int cameraId, Handler handler, final CameraAgent.CameraOpenCallback cb) {
    319         Log.v(TAG, "checkAndOpenCamera");
    320         try {
    321             CameraUtil.throwIfCameraDisabled();
    322             cameraManager.openCamera(handler, cameraId, cb);
    323         } catch (CameraDisabledException ex) {
    324             handler.post(new Runnable() {
    325                 @Override
    326                 public void run() {
    327                     cb.onCameraDisabled(cameraId);
    328                 }
    329             });
    330         }
    331     }
    332 
    333     public void setOneShotPreviewCallback(Handler handler,
    334             CameraAgent.CameraPreviewDataCallback cb) {
    335         mCameraProxy.setOneShotPreviewCallback(handler, cb);
    336     }
    337 }
    338