@@ -91,6 +91,14 @@ public class CameraSource {
9191 */
9292 private static final float ASPECT_RATIO_TOLERANCE = 0.01f ;
9393
94+ public CameraSourceStateListener getStateListener () {
95+ return mStateListener ;
96+ }
97+
98+ public void setStateListener (CameraSourceStateListener stateListener ) {
99+ this .mStateListener = stateListener ;
100+ }
101+
94102 @ StringDef ({
95103 Camera .Parameters .FOCUS_MODE_CONTINUOUS_PICTURE ,
96104 Camera .Parameters .FOCUS_MODE_CONTINUOUS_VIDEO ,
@@ -122,11 +130,16 @@ public class CameraSource {
122130
123131 private int mFacing = CAMERA_FACING_BACK ;
124132
133+ private boolean mCameraFallbackAllowed = true ;
134+
135+ private CameraSourceStateListener mStateListener = null ;
136+
125137 /**
126138 * Rotation of the device, and thus the associated preview images captured from the device.
127139 * See {@link Frame.Metadata#getRotation()}.
128140 */
129141 private int mRotation ;
142+ private int mRequestedCameraId ;
130143
131144 private Size mPreviewSize ;
132145
@@ -143,7 +156,9 @@ public class CameraSource {
143156 // These instances need to be held onto to avoid GC of their underlying resources. Even though
144157 // these aren't used outside of the method that creates them, they still must have hard
145158 // references maintained to them.
159+ @ SuppressWarnings ("FieldCanBeLocal" )
146160 private SurfaceView mDummySurfaceView ;
161+ @ SuppressWarnings ("FieldCanBeLocal" )
147162 private SurfaceTexture mDummySurfaceTexture ;
148163
149164 /**
@@ -153,6 +168,8 @@ public class CameraSource {
153168 private Thread mProcessingThread ;
154169 private FrameProcessingRunnable mFrameProcessor ;
155170
171+ private boolean mCanTakePicture = false ;
172+
156173 /**
157174 * Map to convert between a byte array, received from the camera, and its associated byte
158175 * buffer. We use byte buffers internally because this is a more efficient way to call into
@@ -240,6 +257,16 @@ public Builder setFacing(int facing) {
240257 return this ;
241258 }
242259
260+ /**
261+ * Sets whether fallback from front to back or vice versa is allowed.
262+ * Used in case the requested camera was not available.
263+ * Default: true.
264+ */
265+ public Builder setCameraFallbackAllowed (boolean allowed ) {
266+ mCameraSource .mCameraFallbackAllowed = allowed ;
267+ return this ;
268+ }
269+
243270 /**
244271 * Creates an instance of the camera source.
245272 */
@@ -356,6 +383,11 @@ public CameraSource start() throws IOException {
356383 mFrameProcessor .setActive (true );
357384 mProcessingThread .start ();
358385 }
386+
387+ if (mStateListener != null ) {
388+ mStateListener .onCameraSourceStarted ();
389+ }
390+
359391 return this ;
360392 }
361393
@@ -377,10 +409,17 @@ public CameraSource start(SurfaceHolder surfaceHolder) throws IOException {
377409 mCamera .setPreviewDisplay (surfaceHolder );
378410 mCamera .startPreview ();
379411
412+ mCanTakePicture = true ;
413+
380414 mProcessingThread = new Thread (mFrameProcessor );
381415 mFrameProcessor .setActive (true );
382416 mProcessingThread .start ();
383417 }
418+
419+ if (mStateListener != null ) {
420+ mStateListener .onCameraSourceStarted ();
421+ }
422+
384423 return this ;
385424 }
386425
@@ -411,6 +450,8 @@ public void stop() {
411450 // clear the buffer to prevent oom exceptions
412451 mBytesToByteBuffer .clear ();
413452
453+ mCanTakePicture = false ;
454+
414455 if (mCamera != null ) {
415456 mCamera .stopPreview ();
416457 mCamera .setPreviewCallbackWithBuffer (null );
@@ -433,6 +474,10 @@ public void stop() {
433474 mCamera = null ;
434475 }
435476 }
477+
478+ if (mStateListener != null ) {
479+ mStateListener .onCameraSourceStopped ();
480+ }
436481 }
437482
438483 /**
@@ -450,6 +495,22 @@ public int getCameraFacing() {
450495 return mFacing ;
451496 }
452497
498+ /**
499+ * Sets whether fallback from front to back or vice versa is allowed.
500+ * Used in case the requested camera was not available.
501+ */
502+ public boolean isCameraFallbackAllowed () {
503+ return mCameraFallbackAllowed ;
504+ }
505+
506+ public boolean isCameraFacingBackAvailable () {
507+ return getIdForRequestedCamera (CAMERA_FACING_BACK ) != -1 ;
508+ }
509+
510+ public boolean isCameraFacingFrontAvailable () {
511+ return getIdForRequestedCamera (CAMERA_FACING_FRONT ) != -1 ;
512+ }
513+
453514 public int doZoom (float scale ) {
454515 synchronized (mCameraLock ) {
455516 if (mCamera == null ) {
@@ -494,7 +555,10 @@ public int doZoom(float scale) {
494555 */
495556 public void takePicture (ShutterCallback shutter , PictureCallback jpeg ) {
496557 synchronized (mCameraLock ) {
497- if (mCamera != null ) {
558+ if (mCamera != null && mCanTakePicture ) {
559+
560+ mCanTakePicture = false ; // Preview is suspended until we're done
561+
498562 PictureStartCallback startCallback = new PictureStartCallback ();
499563 startCallback .mDelegate = shutter ;
500564 PictureDoneCallback doneCallback = new PictureDoneCallback ();
@@ -535,7 +599,8 @@ public boolean setFocusMode(@FocusMode String mode) {
535599 synchronized (mCameraLock ) {
536600 if (mCamera != null && mode != null ) {
537601 Camera .Parameters parameters = mCamera .getParameters ();
538- if (parameters .getSupportedFocusModes ().contains (mode )) {
602+ final List <String > supportedFocusModes = parameters .getSupportedFocusModes ();
603+ if (supportedFocusModes != null && supportedFocusModes .contains (mode )) {
539604 parameters .setFocusMode (mode );
540605 mCamera .setParameters (parameters );
541606 mFocusMode = mode ;
@@ -575,7 +640,8 @@ public boolean setFlashMode(@FlashMode String mode) {
575640 synchronized (mCameraLock ) {
576641 if (mCamera != null && mode != null ) {
577642 Camera .Parameters parameters = mCamera .getParameters ();
578- if (parameters .getSupportedFlashModes ().contains (mode )) {
643+ final List <String > supportedFlashModes = parameters .getSupportedFlashModes ();
644+ if (supportedFlashModes != null && supportedFlashModes .contains (mode )) {
579645 parameters .setFlashMode (mode );
580646 mCamera .setParameters (parameters );
581647 mFlashMode = mode ;
@@ -587,6 +653,36 @@ public boolean setFlashMode(@FlashMode String mode) {
587653 }
588654 }
589655
656+ /**
657+ * Checks whether a specific flash mode is supported.
658+ * If the camera source is not initialized yet - then it will return false.
659+ * @param mode
660+ * @return true only if camera is initialized and mode is supported.
661+ */
662+ public boolean isFlashModeSupported (String mode ) {
663+ if (mCamera != null ) {
664+ Camera .Parameters parameters = mCamera .getParameters ();
665+ final List <String > supportedModes = parameters .getSupportedFlashModes ();
666+ return supportedModes != null && supportedModes .contains (mode );
667+ }
668+ return false ;
669+ }
670+
671+ /**
672+ * Checks whether a specific focus mode is supported.
673+ * If the camera source is not initialized yet - then it will return false.
674+ * @param mode
675+ * @return true only if camera is initialized and mode is supported.
676+ */
677+ public boolean isFocusModeSupported (String mode ) {
678+ if (mCamera != null ) {
679+ Camera .Parameters parameters = mCamera .getParameters ();
680+ final List <String > supportedModes = parameters .getSupportedFocusModes ();
681+ return supportedModes != null && supportedModes .contains (mode );
682+ }
683+ return false ;
684+ }
685+
590686 /**
591687 * Starts camera auto-focus and registers a callback function to run when
592688 * the camera is focused. This method is only valid when preview is active
@@ -600,7 +696,7 @@ public boolean setFlashMode(@FlashMode String mode) {
600696 * <p/>
601697 * <p>If the current flash mode is not
602698 * {@link Camera.Parameters#FLASH_MODE_OFF}, flash may be
603- * fired during auto-focus, depending on the driver and camera hardware.<p>
699+ * fired during auto-focus, depending on the driver and camera hardware.</ p>
604700 *
605701 * @param cb the callback to run
606702 * @see #cancelAutoFocus()
@@ -699,6 +795,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
699795 synchronized (mCameraLock ) {
700796 if (mCamera != null ) {
701797 mCamera .startPreview ();
798+ mCanTakePicture = true ;
702799 }
703800 }
704801 }
@@ -740,11 +837,23 @@ public void onAutoFocusMoving(boolean start, Camera camera) {
740837 */
741838 @ SuppressLint ("InlinedApi" )
742839 private Camera createCamera () {
743- int requestedCameraId = getIdForRequestedCamera (mFacing );
744- if (requestedCameraId == -1 ) {
840+ mRequestedCameraId = getIdForRequestedCamera (mFacing );
841+
842+ if (mRequestedCameraId == -1 && mCameraFallbackAllowed ) {
843+ if (mFacing == CAMERA_FACING_BACK ) {
844+ mFacing = CAMERA_FACING_FRONT ;
845+ } else {
846+ mFacing = CAMERA_FACING_BACK ;
847+ }
848+
849+ mRequestedCameraId = getIdForRequestedCamera (mFacing );
850+ }
851+
852+ if (mRequestedCameraId == -1 ) {
745853 throw new RuntimeException ("Could not find requested camera." );
746854 }
747- Camera camera = Camera .open (requestedCameraId );
855+
856+ Camera camera = Camera .open (mRequestedCameraId );
748857
749858 SizePair sizePair = selectSizePair (camera , mRequestedPreviewWidth , mRequestedPreviewHeight );
750859 if (sizePair == null ) {
@@ -770,11 +879,11 @@ private Camera createCamera() {
770879 previewFpsRange [Camera .Parameters .PREVIEW_FPS_MAX_INDEX ]);
771880 parameters .setPreviewFormat (ImageFormat .NV21 );
772881
773- setRotation (camera , parameters , requestedCameraId );
882+ setRotation (camera , parameters , mRequestedCameraId );
774883
775884 if (mFocusMode != null ) {
776- if ( parameters .getSupportedFocusModes (). contains (
777- mFocusMode )) {
885+ final List < String > supportedFocusModes = parameters .getSupportedFocusModes ();
886+ if ( supportedFocusModes != null && supportedFocusModes . contains ( mFocusMode )) {
778887 parameters .setFocusMode (mFocusMode );
779888 } else {
780889 Log .i (TAG , "Camera focus mode: " + mFocusMode + " is not supported on this device." );
@@ -785,13 +894,11 @@ private Camera createCamera() {
785894 mFocusMode = parameters .getFocusMode ();
786895
787896 if (mFlashMode != null ) {
788- if (parameters .getSupportedFlashModes () != null ) {
789- if (parameters .getSupportedFlashModes ().contains (
790- mFlashMode )) {
791- parameters .setFlashMode (mFlashMode );
792- } else {
793- Log .i (TAG , "Camera flash mode: " + mFlashMode + " is not supported on this device." );
794- }
897+ final List <String > supportedFlashModes = parameters .getSupportedFlashModes ();
898+ if (supportedFlashModes != null && supportedFlashModes .contains (mFlashMode )) {
899+ parameters .setFlashMode (mFlashMode );
900+ } else {
901+ Log .i (TAG , "Camera flash mode: " + mFlashMode + " is not supported on this device." );
795902 }
796903 }
797904
@@ -972,6 +1079,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) {
9721079 return selectedFpsRange ;
9731080 }
9741081
1082+ public void updateRotation () {
1083+ if (mCamera != null ) {
1084+ setRotation (mCamera , mCamera .getParameters (), mRequestedCameraId );
1085+ }
1086+ }
1087+
9751088 /**
9761089 * Calculates the correct rotation for the given camera id and sets the rotation in the
9771090 * parameters. It also sets the camera's display orientation and rotation.
@@ -1211,4 +1324,10 @@ public void run() {
12111324 }
12121325 }
12131326 }
1327+
1328+ public interface CameraSourceStateListener
1329+ {
1330+ void onCameraSourceStarted ();
1331+ void onCameraSourceStopped ();
1332+ }
12141333}
0 commit comments