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