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