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 
resume()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 
hasCapability(int[] capabilities, int capability)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 
ImageSaver(Image image, String fileName, Context context,int mode)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
run()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      */
CameraOps(CameraManager manager, ErrorDisplayer errorDisplayer, CameraReadyListener readyListener)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      */
openCamera(final String cameraId)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 
pause()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 
getBestSize()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      */
closeCameraAndWait()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      */
setSurface(Surface surface)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      */
createCaptureRequest(int template)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      */
setRepeatingRequest(final CaptureRequest request, final CameraCaptureSession.CaptureCallback listener, final Handler handler)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      */
setRepeatingBurst(final List<CaptureRequest> requests, final CameraCaptureSession.CaptureCallback listener, final Handler handler)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      */
startCameraSession()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 
captureStillPicture(int currentJpegRotation, String name, Context context, int mode)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      */
setRequest(final CaptureRequest request, final CameraCaptureSession.CaptureCallback listener, final Handler handler)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 
setUpCamera(Surface processingNormalSurface)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      */
setParameters()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 {
onCameraReady()637         public void onCameraReady();
638     }
639 
640     /**
641      * Simple listener for displaying error messages
642      */
643     public interface ErrorDisplayer {
showErrorDialog(String errorMessage)644         public void showErrorDialog(String errorMessage);
645 
getErrorString(CameraAccessException e)646         public String getErrorString(CameraAccessException e);
647     }
648 
getFocusDistance()649     public float getFocusDistance() {
650         return mFocusDist;
651     }
652 
setFocusDistance(float focusDistance)653     public void setFocusDistance(float focusDistance) {
654         mFocusDist = focusDistance;
655     }
656 
setIso(int iso)657     public void setIso(int iso) {
658         mIso = iso;
659     }
660 
isAutoExposure()661     public boolean isAutoExposure() {
662         return mAutoExposure;
663     }
664 
setAutoExposure(boolean autoExposure)665     public void setAutoExposure(boolean autoExposure) {
666         mAutoExposure = autoExposure;
667     }
668 
isAutoFocus()669     public boolean isAutoFocus() {
670         return mAutoFocus;
671     }
672 
setAutoFocus(boolean autoFocus)673     public void setAutoFocus(boolean autoFocus) {
674         mAutoFocus = autoFocus;
675     }
676 
getIso()677     public int getIso() {
678         return mIso;
679     }
680 
getExposure()681     public long getExposure() {
682         return mExposure;
683     }
684 
setExposure(long exposure)685     public void setExposure(long exposure) {
686         mExposure = exposure;
687     }
688 
getIsoMax()689     public int getIsoMax() {
690         return mISOmax;
691     }
692 
getIsoMin()693     public int getIsoMin() {
694         return mISOmin;
695     }
696 
getExpMax()697     public long getExpMax() {
698         return mExpMax;
699     }
700 
getExpMin()701     public long getExpMin() {
702         return mExpMin;
703     }
704 
getFocusMin()705     public float getFocusMin() {
706         return mFocusMin;
707     }
708 }
709