Home | History | Annotate | Download | only in com.example.android.hdrviewfinder
      1 /*
      2  * Copyright (C) 2014 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.example.android.hdrviewfinder;
     18 
     19 import android.hardware.camera2.CameraAccessException;
     20 import android.hardware.camera2.CameraCaptureSession;
     21 import android.hardware.camera2.CameraDevice;
     22 import android.hardware.camera2.CameraManager;
     23 import android.hardware.camera2.CaptureRequest;
     24 import android.os.ConditionVariable;
     25 import android.os.Handler;
     26 import android.os.HandlerThread;
     27 import android.util.Log;
     28 import android.view.Surface;
     29 
     30 import java.util.List;
     31 
     32 /**
     33  * Simple interface for operating the camera, with major camera operations
     34  * all performed on a background handler thread.
     35  */
     36 public class CameraOps {
     37 
     38     private static final String TAG = "CameraOps";
     39 
     40     public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
     41 
     42     private final CameraManager mCameraManager;
     43     private CameraDevice mCameraDevice;
     44     private CameraCaptureSession mCameraSession;
     45     private List<Surface> mSurfaces;
     46 
     47     private final ConditionVariable mCloseWaiter = new ConditionVariable();
     48 
     49     private HandlerThread mCameraThread;
     50     private Handler mCameraHandler;
     51 
     52     private final ErrorDisplayer mErrorDisplayer;
     53 
     54     private final CameraReadyListener mReadyListener;
     55     private final Handler mReadyHandler;
     56 
     57     /**
     58      * Create a new camera ops thread.
     59      *
     60      * @param errorDisplayer listener for displaying error messages
     61      * @param readyListener  listener for notifying when camera is ready for requests
     62      * @param readyHandler   the handler for calling readyListener methods on
     63      */
     64     CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
     65               CameraReadyListener readyListener, Handler readyHandler) {
     66         mCameraThread = new HandlerThread("CameraOpsThread");
     67         mCameraThread.start();
     68 
     69         if (manager == null || errorDisplayer == null ||
     70                 readyListener == null || readyHandler == null) {
     71             throw new IllegalArgumentException("Need valid displayer, listener, handler");
     72         }
     73 
     74         mCameraManager = manager;
     75         mErrorDisplayer = errorDisplayer;
     76         mReadyListener = readyListener;
     77         mReadyHandler = readyHandler;
     78     }
     79 
     80     /**
     81      * Open the first back-facing camera listed by the camera manager.
     82      * Displays a dialog if it cannot open a camera.
     83      */
     84     public void openCamera(final String cameraId) {
     85         mCameraHandler = new Handler(mCameraThread.getLooper());
     86 
     87         mCameraHandler.post(new Runnable() {
     88             public void run() {
     89                 if (mCameraDevice != null) {
     90                     throw new IllegalStateException("Camera already open");
     91                 }
     92                 try {
     93                     mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
     94                 } catch (CameraAccessException e) {
     95                     String errorMessage = mErrorDisplayer.getErrorString(e);
     96                     mErrorDisplayer.showErrorDialog(errorMessage);
     97                 }
     98             }
     99         });
    100     }
    101 
    102     /**
    103      * Close the camera and wait for the close callback to be called in the camera thread.
    104      * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
    105      */
    106     public void closeCameraAndWait() {
    107         mCloseWaiter.close();
    108         mCameraHandler.post(mCloseCameraRunnable);
    109         boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
    110         if (!closed) {
    111             Log.e(TAG, "Timeout closing camera");
    112         }
    113     }
    114 
    115     private Runnable mCloseCameraRunnable = new Runnable() {
    116         public void run() {
    117             if (mCameraDevice != null) {
    118                 mCameraDevice.close();
    119             }
    120             mCameraDevice = null;
    121             mCameraSession = null;
    122             mSurfaces = null;
    123         }
    124     };
    125 
    126     /**
    127      * Set the output Surfaces, and finish configuration if otherwise ready.
    128      */
    129     public void setSurfaces(final List<Surface> surfaces) {
    130         mCameraHandler.post(new Runnable() {
    131             public void run() {
    132                 mSurfaces = surfaces;
    133                 startCameraSession();
    134             }
    135         });
    136     }
    137 
    138     /**
    139      * Get a request builder for the current camera.
    140      */
    141     public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
    142         CameraDevice device = mCameraDevice;
    143         if (device == null) {
    144             throw new IllegalStateException("Can't get requests when no camera is open");
    145         }
    146         return device.createCaptureRequest(template);
    147     }
    148 
    149     /**
    150      * Set a repeating request.
    151      */
    152     public void setRepeatingRequest(final CaptureRequest request,
    153                                     final CameraCaptureSession.CaptureCallback listener,
    154                                     final Handler handler) {
    155         mCameraHandler.post(new Runnable() {
    156             public void run() {
    157                 try {
    158                     mCameraSession.setRepeatingRequest(request, listener, handler);
    159                 } catch (CameraAccessException e) {
    160                     String errorMessage = mErrorDisplayer.getErrorString(e);
    161                     mErrorDisplayer.showErrorDialog(errorMessage);
    162                 }
    163             }
    164         });
    165     }
    166 
    167     /**
    168      * Set a repeating request.
    169      */
    170     public void setRepeatingBurst(final List<CaptureRequest> requests,
    171                                   final CameraCaptureSession.CaptureCallback listener,
    172                                   final Handler handler) {
    173         mCameraHandler.post(new Runnable() {
    174             public void run() {
    175                 try {
    176                     mCameraSession.setRepeatingBurst(requests, listener, handler);
    177                 } catch (CameraAccessException e) {
    178                     String errorMessage = mErrorDisplayer.getErrorString(e);
    179                     mErrorDisplayer.showErrorDialog(errorMessage);
    180                 }
    181             }
    182         });
    183     }
    184 
    185     /**
    186      * Configure the camera session.
    187      */
    188     private void startCameraSession() {
    189         // Wait until both the camera device is open and the SurfaceView is ready
    190         if (mCameraDevice == null || mSurfaces == null) return;
    191 
    192         try {
    193             mCameraDevice.createCaptureSession(
    194                     mSurfaces, mCameraSessionListener, mCameraHandler);
    195         } catch (CameraAccessException e) {
    196             String errorMessage = mErrorDisplayer.getErrorString(e);
    197             mErrorDisplayer.showErrorDialog(errorMessage);
    198             mCameraDevice.close();
    199             mCameraDevice = null;
    200         }
    201     }
    202 
    203     /**
    204      * Main listener for camera session events
    205      * Invoked on mCameraThread
    206      */
    207     private CameraCaptureSession.StateCallback mCameraSessionListener =
    208             new CameraCaptureSession.StateCallback() {
    209 
    210                 @Override
    211                 public void onConfigured(CameraCaptureSession session) {
    212                     mCameraSession = session;
    213                     mReadyHandler.post(new Runnable() {
    214                         public void run() {
    215                             // This can happen when the screen is turned off and turned back on.
    216                             if (null == mCameraDevice) {
    217                                 return;
    218                             }
    219 
    220                             mReadyListener.onCameraReady();
    221                         }
    222                     });
    223 
    224                 }
    225 
    226                 @Override
    227                 public void onConfigureFailed(CameraCaptureSession session) {
    228                     mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
    229                     mCameraDevice.close();
    230                     mCameraDevice = null;
    231                 }
    232             };
    233 
    234     /**
    235      * Main listener for camera device events.
    236      * Invoked on mCameraThread
    237      */
    238     private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
    239 
    240         @Override
    241         public void onOpened(CameraDevice camera) {
    242             mCameraDevice = camera;
    243             startCameraSession();
    244         }
    245 
    246         @Override
    247         public void onClosed(CameraDevice camera) {
    248             mCloseWaiter.open();
    249         }
    250 
    251         @Override
    252         public void onDisconnected(CameraDevice camera) {
    253             mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
    254             camera.close();
    255             mCameraDevice = null;
    256         }
    257 
    258         @Override
    259         public void onError(CameraDevice camera, int error) {
    260             mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
    261             camera.close();
    262             mCameraDevice = null;
    263         }
    264 
    265     };
    266 
    267     /**
    268      * Simple listener for main code to know the camera is ready for requests, or failed to
    269      * start.
    270      */
    271     public interface CameraReadyListener {
    272         public void onCameraReady();
    273     }
    274 
    275     /**
    276      * Simple listener for displaying error messages
    277      */
    278     public interface ErrorDisplayer {
    279         public void showErrorDialog(String errorMessage);
    280 
    281         public String getErrorString(CameraAccessException e);
    282     }
    283 
    284 }
    285