Home | History | Annotate | Download | only in cannylive
      1 /*
      2  * Copyright (C) 2015 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.example.cannylive;
     18 
     19 import android.content.Context;
     20 import android.graphics.ImageFormat;
     21 import android.hardware.camera2.CameraAccessException;
     22 import android.hardware.camera2.CameraCaptureSession;
     23 import android.hardware.camera2.CameraCharacteristics;
     24 import android.hardware.camera2.CameraDevice;
     25 import android.hardware.camera2.CameraManager;
     26 import android.hardware.camera2.CaptureRequest;
     27 import android.hardware.camera2.TotalCaptureResult;
     28 import android.hardware.camera2.params.StreamConfigurationMap;
     29 import android.media.Image;
     30 import android.media.ImageReader;
     31 import android.os.ConditionVariable;
     32 import android.os.Handler;
     33 import android.os.HandlerThread;
     34 import android.os.Looper;
     35 import android.util.Log;
     36 import android.util.Range;
     37 import android.util.Size;
     38 import android.view.Surface;
     39 import android.view.SurfaceHolder;
     40 
     41 import java.io.IOException;
     42 import java.io.OutputStream;
     43 import java.nio.ByteBuffer;
     44 import java.util.ArrayList;
     45 import java.util.Arrays;
     46 import java.util.Collections;
     47 import java.util.Comparator;
     48 import java.util.List;
     49 
     50 /**
     51  * Simple interface for operating the camera, with major camera operations
     52  * all performed on a background handler thread.
     53  */
     54 public class CameraOps {
     55 
     56     private static final String TAG = "CameraOps";
     57     private static final long ONE_SECOND = 1000000000;
     58     public static final long CAMERA_CLOSE_TIMEOUT = 2000; // ms
     59 
     60     private final CameraManager mCameraManager;
     61     private CameraDevice mCameraDevice;
     62     private CameraCaptureSession mCameraSession;
     63     private List<Surface> mSurfaces;
     64 
     65     private final ConditionVariable mCloseWaiter = new ConditionVariable();
     66 
     67     private HandlerThread mCameraThread;
     68     private Handler mCameraHandler;
     69 
     70     private final ErrorDisplayer mErrorDisplayer;
     71 
     72     private final CameraReadyListener mReadyListener;
     73     private final Handler mReadyHandler;
     74 
     75     private int mISOmax;
     76     private int mISOmin;
     77     private long mExpMax;
     78     private long mExpMin;
     79     private float mFocusMin;
     80     private float mFocusDist = 0;
     81     private int mIso;
     82     boolean mAutoExposure = true;
     83     boolean mAutoFocus = true;
     84     private long mExposure = ONE_SECOND / 33;
     85 
     86     private Object mAutoExposureTag = new Object();
     87 
     88     private ImageReader mImageReader;
     89     private Handler mBackgroundHandler;
     90     private CameraCharacteristics mCameraInfo;
     91     private HandlerThread mBackgroundThread;
     92     CaptureRequest.Builder mHdrBuilder;
     93     private Surface mProcessingNormalSurface;
     94     CaptureRequest mPreviewRequest;
     95     private String mSaveFileName;
     96     private Context mContext;
     97     private int mCaptureMode;
     98 
     99     public String resume() {
    100         String errorMessage = "Unknown error";
    101         boolean foundCamera = false;
    102         try {
    103             // Find first back-facing camera that has necessary capability
    104             String[] cameraIds = mCameraManager.getCameraIdList();
    105             for (String id : cameraIds) {
    106                 CameraCharacteristics info = mCameraManager.getCameraCharacteristics(id);
    107                 int facing = info.get(CameraCharacteristics.LENS_FACING);
    108 
    109                 int level = info.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
    110                 boolean hasFullLevel
    111                         = (level == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
    112 
    113                 int[] capabilities = info.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
    114                 int syncLatency = info.get(CameraCharacteristics.SYNC_MAX_LATENCY);
    115                 boolean hasManualControl = hasCapability(capabilities,
    116                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR);
    117                 boolean hasEnoughCapability = hasManualControl &&
    118                         syncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
    119                 Range<Integer> irange;
    120                 Range<Long> lrange;
    121 
    122                 irange = info.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
    123                 if (irange != null) {
    124                     mISOmax = irange.getUpper();
    125                     mISOmin = irange.getLower();
    126                     lrange = info.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
    127                     mExpMax = lrange.getUpper();
    128                     mExpMin = lrange.getLower();
    129                     mFocusMin = info.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
    130                 } else {
    131                     mISOmax = 200;
    132                     mISOmin = 100;
    133                     mExpMax = 1000;
    134                 }
    135                 mFocusDist = mFocusMin;
    136                 StreamConfigurationMap map = info.get(
    137                         CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    138                 Size[] sizes = map.getOutputSizes(ImageFormat.JPEG);
    139                 List<Size> sizeList = Arrays.asList(sizes);
    140                 Collections.sort(sizeList, new Comparator<Size>() {
    141                     @Override
    142                     public int compare(Size lhs, Size rhs) {
    143                         int leftArea = lhs.getHeight() * lhs.getWidth();
    144                         int rightArea = lhs.getHeight() * lhs.getWidth();
    145                         return Integer.compare(leftArea, rightArea);
    146                     }
    147                 });
    148                 Size max = sizeList.get(0);
    149                 int check = 1;
    150                 Size big = sizeList.get(check);
    151                 float aspect = 16/9f;
    152                 Log.v(TAG,"max big "+max.getWidth()+" x "+max.getHeight());
    153                 for (int i = 0; i < sizeList.size(); i++) {
    154                     Size s = sizeList.get(i);
    155                     if (s.getHeight() == 720) {
    156                         big = s;
    157                         break;
    158                     }
    159                 }
    160                 Log.v(TAG,"BIG wil be "+big.getWidth()+" x "+big.getHeight());
    161                 mImageReader = ImageReader.newInstance(big.getWidth(), big.getHeight(),
    162                         ImageFormat.JPEG, /*maxImages*/2);
    163                 mImageReader.setOnImageAvailableListener(
    164                         mOnImageAvailableListener, mBackgroundHandler);
    165 
    166                 if (facing == CameraCharacteristics.LENS_FACING_BACK &&
    167                         (hasFullLevel || hasEnoughCapability)) {
    168                     // Found suitable camera - get info, open, and set up outputs
    169                     mCameraInfo = info;
    170                     openCamera(id);
    171                     foundCamera = true;
    172                     break;
    173                 }
    174             }
    175             if (!foundCamera) {
    176                 errorMessage = "no back camera";
    177             }
    178         } catch (CameraAccessException e) {
    179             errorMessage = e.getMessage();
    180         }
    181         // startBackgroundThread
    182         mBackgroundThread = new HandlerThread("CameraBackground");
    183         mBackgroundThread.start();
    184         mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    185         return (foundCamera) ? null : errorMessage;
    186     }
    187 
    188 
    189     private boolean hasCapability(int[] capabilities, int capability) {
    190         for (int c : capabilities) {
    191             if (c == capability) return true;
    192         }
    193         return false;
    194     }
    195 
    196     private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
    197             = new ImageReader.OnImageAvailableListener() {
    198 
    199         @Override
    200         public void onImageAvailable(ImageReader reader) {
    201             mBackgroundHandler.post(new ImageSaver(reader.acquireNextImage(),
    202                     mSaveFileName, mContext,mCaptureMode));
    203         }
    204 
    205     };
    206 
    207     /**
    208      * Saves a JPEG {@link android.media.Image} into the specified {@link java.io.File}.
    209      */
    210     private static class ImageSaver implements Runnable {
    211         private final Image mImage;
    212         private final String mName;
    213         Context mContext;
    214         private int mMode;
    215 
    216         public ImageSaver(Image image, String fileName, Context context,int mode) {
    217             mImage = image;
    218             mName = fileName;
    219             mContext = context;
    220             mMode = mode;
    221         }
    222 
    223         @Override
    224         public void run() {
    225             Log.v(TAG, "S>> SAVING...");
    226             String url = MediaStoreSaver.insertImage(mContext.getContentResolver(),
    227                     new MediaStoreSaver.StreamWriter() {
    228                         @Override
    229                         public void write(OutputStream imageOut) throws IOException {
    230                             try {
    231                                 ByteBuffer buffer = mImage.getPlanes()[0].getBuffer();
    232                                 byte[] bytes = new byte[buffer.remaining()];
    233                                 Log.v(TAG, "S>> size=" + mImage.getWidth() +
    234                                         "," + mImage.getHeight());
    235                                 Log.v(TAG, "S>> bytes " + bytes.length +
    236                                         " (" + bytes.length / (1024 * 1024) + "MB");
    237                                 Log.v(TAG, "S>> bytes out " + bytes.length / mImage.getWidth());
    238                                 buffer.get(bytes);
    239                                 imageOut.write(bytes);
    240                             } finally {
    241                                 mImage.close();
    242                             }
    243                         }
    244                     }, mName, "Saved from Simple Camera Demo");
    245             ViewfinderProcessor.reProcessImage(mContext, url, mMode);
    246         }
    247     }
    248 
    249     /**
    250      * Create a new camera ops thread.
    251      *
    252      * @param errorDisplayer listener for displaying error messages
    253      * @param readyListener  listener for notifying when camera is ready for requests
    254      */
    255     CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer,
    256               CameraReadyListener readyListener) {
    257         mReadyHandler = new Handler(Looper.getMainLooper());
    258 
    259         mCameraThread = new HandlerThread("CameraOpsThread");
    260         mCameraThread.start();
    261 
    262         if (manager == null || errorDisplayer == null ||
    263                 readyListener == null || mReadyHandler == null) {
    264             throw new IllegalArgumentException("Need valid displayer, listener, handler");
    265         }
    266 
    267         mCameraManager = manager;
    268         mErrorDisplayer = errorDisplayer;
    269         mReadyListener = readyListener;
    270 
    271     }
    272 
    273     /**
    274      * Open the first backfacing camera listed by the camera manager.
    275      * Displays a dialog if it cannot open a camera.
    276      */
    277     public void openCamera(final String cameraId) {
    278         mCameraHandler = new Handler(mCameraThread.getLooper());
    279 
    280         mCameraHandler.post(new Runnable() {
    281             public void run() {
    282                 if (mCameraDevice != null) {
    283                     throw new IllegalStateException("Camera already open");
    284                 }
    285                 try {
    286 
    287                     mCameraManager.openCamera(cameraId, mCameraDeviceListener, mCameraHandler);
    288                 } catch (CameraAccessException e) {
    289                     String errorMessage = mErrorDisplayer.getErrorString(e);
    290                     mErrorDisplayer.showErrorDialog(errorMessage);
    291                 }
    292             }
    293         });
    294     }
    295 
    296     public void pause() {
    297 
    298         closeCameraAndWait();
    299         mBackgroundThread.quitSafely();
    300         try {
    301             mBackgroundThread.join();
    302             mBackgroundThread = null;
    303             mBackgroundHandler = null;
    304         } catch (InterruptedException e) {
    305             e.printStackTrace();
    306         }
    307     }
    308 
    309     public Size getBestSize() {
    310         // Find a good size for output - largest 16:9 aspect ratio that's less than 720p
    311         final int MAX_WIDTH = 640;
    312         final float TARGET_ASPECT = 16.f / 9.f;
    313 
    314 
    315         StreamConfigurationMap configs =
    316                 mCameraInfo.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    317 
    318         Size[] outputSizes = configs.getOutputSizes(SurfaceHolder.class);
    319 
    320         Size outputSize = null;
    321         ArrayList<Size> smallEnough = new ArrayList<Size>();
    322         for (Size candidateSize : outputSizes) {
    323             if (candidateSize.getWidth() <= MAX_WIDTH) {
    324                 Log.v(TAG, "consider " + candidateSize);
    325                 smallEnough.add(candidateSize);
    326             }
    327         }
    328         if (smallEnough.size() == 0) {
    329             return outputSizes[outputSizes.length - 1]; //pick the smallest
    330         }
    331         Size maxSize = smallEnough.get(0);
    332         double aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
    333         for (Size candidateSize : smallEnough) {
    334             if (maxSize.getWidth() < candidateSize.getWidth()) {
    335                 maxSize = candidateSize;
    336                 aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
    337             }
    338             if (maxSize.getWidth() == candidateSize.getWidth()) {
    339                 if (aspectDelta > Math.abs(candidateSize.getWidth() / candidateSize.getHeight() - TARGET_ASPECT)) {
    340                     maxSize = candidateSize;
    341                     aspectDelta = Math.abs(maxSize.getWidth() / maxSize.getHeight() - TARGET_ASPECT);
    342                 }
    343             }
    344         }
    345 
    346         return maxSize;
    347     }
    348 
    349     /**
    350      * Close the camera and wait for the close callback to be called in the camera thread.
    351      * Times out after @{value CAMERA_CLOSE_TIMEOUT} ms.
    352      */
    353     public void closeCameraAndWait() {
    354         mCloseWaiter.close();
    355         mCameraHandler.post(mCloseCameraRunnable);
    356         boolean closed = mCloseWaiter.block(CAMERA_CLOSE_TIMEOUT);
    357         if (!closed) {
    358             Log.e(TAG, "Timeout closing camera");
    359         }
    360     }
    361 
    362     private Runnable mCloseCameraRunnable = new Runnable() {
    363         public void run() {
    364             if (mCameraDevice != null) {
    365                 mCameraDevice.close();
    366             }
    367             mCameraDevice = null;
    368             mCameraSession = null;
    369             mSurfaces = null;
    370         }
    371     };
    372 
    373     /**
    374      * Set the output Surfaces, and finish configuration if otherwise ready.
    375      */
    376     public void setSurface(Surface surface) {
    377         final List<Surface> surfaceList = new ArrayList<Surface>();
    378         surfaceList.add(surface);
    379         surfaceList.add(mImageReader.getSurface());
    380 
    381         mCameraHandler.post(new Runnable() {
    382             public void run() {
    383                 mSurfaces = surfaceList;
    384                 startCameraSession();
    385             }
    386         });
    387     }
    388 
    389     /**
    390      * Get a request builder for the current camera.
    391      */
    392     public CaptureRequest.Builder createCaptureRequest(int template) throws CameraAccessException {
    393         CameraDevice device = mCameraDevice;
    394         if (device == null) {
    395             throw new IllegalStateException("Can't get requests when no camera is open");
    396         }
    397         return device.createCaptureRequest(template);
    398     }
    399 
    400     /**
    401      * Set a repeating request.
    402      */
    403     public void setRepeatingRequest(final CaptureRequest request,
    404                                     final CameraCaptureSession.CaptureCallback listener,
    405                                     final Handler handler) {
    406         mCameraHandler.post(new Runnable() {
    407             public void run() {
    408                 try {
    409                     mCameraSession.setRepeatingRequest(request, listener, handler);
    410                 } catch (CameraAccessException e) {
    411                     String errorMessage = mErrorDisplayer.getErrorString(e);
    412                     mErrorDisplayer.showErrorDialog(errorMessage);
    413                 }
    414             }
    415         });
    416     }
    417 
    418     /**
    419      * Set a repeating request.
    420      */
    421     public void setRepeatingBurst(final List<CaptureRequest> requests,
    422                                   final CameraCaptureSession.CaptureCallback listener,
    423                                   final Handler handler) {
    424         mCameraHandler.post(new Runnable() {
    425             public void run() {
    426                 try {
    427                     mCameraSession.setRepeatingBurst(requests, listener, handler);
    428 
    429                 } catch (CameraAccessException e) {
    430                     String errorMessage = mErrorDisplayer.getErrorString(e);
    431                     mErrorDisplayer.showErrorDialog(errorMessage);
    432                 }
    433             }
    434         });
    435     }
    436 
    437     /**
    438      * Configure the camera session.
    439      */
    440     private void startCameraSession() {
    441         // Wait until both the camera device is open and the SurfaceView is ready
    442         if (mCameraDevice == null || mSurfaces == null) return;
    443 
    444         try {
    445 
    446             mCameraDevice.createCaptureSession(
    447                     mSurfaces, mCameraSessionListener, mCameraHandler);
    448         } catch (CameraAccessException e) {
    449             String errorMessage = mErrorDisplayer.getErrorString(e);
    450             mErrorDisplayer.showErrorDialog(errorMessage);
    451             mCameraDevice.close();
    452             mCameraDevice = null;
    453         }
    454     }
    455 
    456     /**
    457      * Main listener for camera session events
    458      * Invoked on mCameraThread
    459      */
    460     private CameraCaptureSession.StateCallback mCameraSessionListener =
    461             new CameraCaptureSession.StateCallback() {
    462 
    463                 @Override
    464                 public void onConfigured(CameraCaptureSession session) {
    465                     mCameraSession = session;
    466                     mReadyHandler.post(new Runnable() {
    467                         public void run() {
    468                             // This can happen when the screen is turned off and turned back on.
    469                             if (null == mCameraDevice) {
    470                                 return;
    471                             }
    472 
    473                             mReadyListener.onCameraReady();
    474                         }
    475                     });
    476 
    477                 }
    478 
    479                 @Override
    480                 public void onConfigureFailed(CameraCaptureSession session) {
    481                     mErrorDisplayer.showErrorDialog("Unable to configure the capture session");
    482                     mCameraDevice.close();
    483                     mCameraDevice = null;
    484                 }
    485             };
    486 
    487     /**
    488      * Main listener for camera device events.
    489      * Invoked on mCameraThread
    490      */
    491     private CameraDevice.StateCallback mCameraDeviceListener = new CameraDevice.StateCallback() {
    492 
    493         @Override
    494         public void onOpened(CameraDevice camera) {
    495             mCameraDevice = camera;
    496             startCameraSession();
    497         }
    498 
    499         @Override
    500         public void onClosed(CameraDevice camera) {
    501             mCloseWaiter.open();
    502         }
    503 
    504         @Override
    505         public void onDisconnected(CameraDevice camera) {
    506             mErrorDisplayer.showErrorDialog("The camera device has been disconnected.");
    507             camera.close();
    508             mCameraDevice = null;
    509         }
    510 
    511         @Override
    512         public void onError(CameraDevice camera, int error) {
    513             mErrorDisplayer.showErrorDialog("The camera encountered an error:" + error);
    514             camera.close();
    515             mCameraDevice = null;
    516         }
    517 
    518     };
    519 
    520     public void captureStillPicture(int currentJpegRotation, String name, Context context, int mode) {
    521         mSaveFileName = name;
    522         mContext = context;
    523         mCaptureMode = mode;
    524         try {
    525             // TODO call lock focus if we are in "AF-S(One-Shot AF) mode"
    526             // TODO call precapture if we are using flash
    527             // This is the CaptureRequest.Builder that we use to take a picture.
    528             final CaptureRequest.Builder captureBuilder =
    529                     createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
    530             Log.v(TAG, "S>>  Target " + mImageReader.getWidth() + "," + mImageReader.getHeight());
    531 
    532             captureBuilder.addTarget(mImageReader.getSurface());
    533 
    534             captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, currentJpegRotation);
    535 
    536             CameraCaptureSession.CaptureCallback captureCallback
    537                     = new CameraCaptureSession.CaptureCallback() {
    538 
    539                 @Override
    540                 public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
    541                                                TotalCaptureResult result) {
    542                     Log.v(TAG, "S>>  onCaptureCompleted");
    543                     setParameters();
    544                 }
    545             };
    546 
    547 
    548             setRequest(captureBuilder.build(), captureCallback, null);
    549         } catch (CameraAccessException e) {
    550             e.printStackTrace();
    551         }
    552     }
    553 
    554     /**
    555      * Set a repeating request.
    556      */
    557     private void setRequest(final CaptureRequest request,
    558                             final CameraCaptureSession.CaptureCallback listener,
    559                             final Handler handler) {
    560         mCameraHandler.post(new Runnable() {
    561             public void run() {
    562                 try {
    563                     mCameraSession.stopRepeating();
    564                     mCameraSession.capture(request, listener, handler);
    565                 } catch (CameraAccessException e) {
    566                     String errorMessage = mErrorDisplayer.getErrorString(e);
    567                     mErrorDisplayer.showErrorDialog(errorMessage);
    568                 }
    569             }
    570         });
    571     }
    572 
    573     public void setUpCamera(Surface processingNormalSurface) {
    574         mProcessingNormalSurface = processingNormalSurface;
    575         // Ready to send requests in, so set them up
    576         try {
    577             CaptureRequest.Builder previewBuilder =
    578                     createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    579             previewBuilder.addTarget(mProcessingNormalSurface);
    580             previewBuilder.setTag(mAutoExposureTag);
    581             mPreviewRequest = previewBuilder.build();
    582             mHdrBuilder = createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
    583             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE,
    584                     CaptureRequest.CONTROL_AE_MODE_OFF);
    585             mHdrBuilder.addTarget(mProcessingNormalSurface);
    586             setParameters();
    587 
    588         } catch (CameraAccessException e) {
    589             String errorMessage = e.getMessage();
    590             // MessageDialogFragment.newInstance(errorMessage).show(getFragmentManager(), FRAGMENT_DIALOG);
    591         }
    592     }
    593 
    594     /**
    595      * Start running an HDR burst on a configured camera session
    596      */
    597     public void setParameters() {
    598         if (mHdrBuilder == null) {
    599             Log.v(TAG, " Camera not set up");
    600             return;
    601         }
    602         if (mAutoExposure) {
    603             mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
    604             mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
    605             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    606         } else {
    607             mHdrBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
    608             mHdrBuilder.set(CaptureRequest.SENSOR_FRAME_DURATION, ONE_SECOND / 30);
    609             mHdrBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, getExposure());
    610             mHdrBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, getIso());
    611         }
    612         if (mAutoFocus) {
    613             mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO);
    614             mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    615         } else {
    616             mHdrBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
    617             mHdrBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, getFocusDistance());
    618             mHdrBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START);
    619         }
    620 
    621         setRepeatingRequest(mHdrBuilder.build(), mCaptureCallback, mReadyHandler);
    622     }
    623 
    624     private CameraCaptureSession.CaptureCallback mCaptureCallback
    625             = new CameraCaptureSession.CaptureCallback() {
    626 
    627         public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
    628                                        TotalCaptureResult result) {
    629         }
    630     };
    631 
    632     /**
    633      * Simple listener for main code to know the camera is ready for requests, or failed to
    634      * start.
    635      */
    636     public interface CameraReadyListener {
    637         public void onCameraReady();
    638     }
    639 
    640     /**
    641      * Simple listener for displaying error messages
    642      */
    643     public interface ErrorDisplayer {
    644         public void showErrorDialog(String errorMessage);
    645 
    646         public String getErrorString(CameraAccessException e);
    647     }
    648 
    649     public float getFocusDistance() {
    650         return mFocusDist;
    651     }
    652 
    653     public void setFocusDistance(float focusDistance) {
    654         mFocusDist = focusDistance;
    655     }
    656 
    657     public void setIso(int iso) {
    658         mIso = iso;
    659     }
    660 
    661     public boolean isAutoExposure() {
    662         return mAutoExposure;
    663     }
    664 
    665     public void setAutoExposure(boolean autoExposure) {
    666         mAutoExposure = autoExposure;
    667     }
    668 
    669     public boolean isAutoFocus() {
    670         return mAutoFocus;
    671     }
    672 
    673     public void setAutoFocus(boolean autoFocus) {
    674         mAutoFocus = autoFocus;
    675     }
    676 
    677     public int getIso() {
    678         return mIso;
    679     }
    680 
    681     public long getExposure() {
    682         return mExposure;
    683     }
    684 
    685     public void setExposure(long exposure) {
    686         mExposure = exposure;
    687     }
    688 
    689     public int getIsoMax() {
    690         return mISOmax;
    691     }
    692 
    693     public int getIsoMin() {
    694         return mISOmin;
    695     }
    696 
    697     public long getExpMax() {
    698         return mExpMax;
    699     }
    700 
    701     public long getExpMin() {
    702         return mExpMin;
    703     }
    704 
    705     public float getFocusMin() {
    706         return mFocusMin;
    707     }
    708 }
    709