@@ -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 .getSupportedFlashModes ();
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,15 @@ public boolean setFlashMode(@FlashMode String mode) {
587653 }
588654 }
589655
656+ public boolean isModeSupported (String mode ) {
657+ if (mCamera != null ) {
658+ Camera .Parameters parameters = mCamera .getParameters ();
659+ final List <String > supportedModes = parameters .getSupportedFlashModes ();
660+ return supportedModes != null && supportedModes .contains (mode );
661+ }
662+ return false ;
663+ }
664+
590665 /**
591666 * Starts camera auto-focus and registers a callback function to run when
592667 * the camera is focused. This method is only valid when preview is active
@@ -600,7 +675,7 @@ public boolean setFlashMode(@FlashMode String mode) {
600675 * <p/>
601676 * <p>If the current flash mode is not
602677 * {@link Camera.Parameters#FLASH_MODE_OFF}, flash may be
603- * fired during auto-focus, depending on the driver and camera hardware.<p>
678+ * fired during auto-focus, depending on the driver and camera hardware.</ p>
604679 *
605680 * @param cb the callback to run
606681 * @see #cancelAutoFocus()
@@ -699,6 +774,7 @@ public void onPictureTaken(byte[] data, Camera camera) {
699774 synchronized (mCameraLock ) {
700775 if (mCamera != null ) {
701776 mCamera .startPreview ();
777+ mCanTakePicture = true ;
702778 }
703779 }
704780 }
@@ -740,11 +816,23 @@ public void onAutoFocusMoving(boolean start, Camera camera) {
740816 */
741817 @ SuppressLint ("InlinedApi" )
742818 private Camera createCamera () {
743- int requestedCameraId = getIdForRequestedCamera (mFacing );
744- if (requestedCameraId == -1 ) {
819+ mRequestedCameraId = getIdForRequestedCamera (mFacing );
820+
821+ if (mRequestedCameraId == -1 && mCameraFallbackAllowed ) {
822+ if (mFacing == CAMERA_FACING_BACK ) {
823+ mFacing = CAMERA_FACING_FRONT ;
824+ } else {
825+ mFacing = CAMERA_FACING_BACK ;
826+ }
827+
828+ mRequestedCameraId = getIdForRequestedCamera (mFacing );
829+ }
830+
831+ if (mRequestedCameraId == -1 ) {
745832 throw new RuntimeException ("Could not find requested camera." );
746833 }
747- Camera camera = Camera .open (requestedCameraId );
834+
835+ Camera camera = Camera .open (mRequestedCameraId );
748836
749837 SizePair sizePair = selectSizePair (camera , mRequestedPreviewWidth , mRequestedPreviewHeight );
750838 if (sizePair == null ) {
@@ -770,11 +858,11 @@ private Camera createCamera() {
770858 previewFpsRange [Camera .Parameters .PREVIEW_FPS_MAX_INDEX ]);
771859 parameters .setPreviewFormat (ImageFormat .NV21 );
772860
773- setRotation (camera , parameters , requestedCameraId );
861+ setRotation (camera , parameters , mRequestedCameraId );
774862
775863 if (mFocusMode != null ) {
776- if ( parameters .getSupportedFocusModes (). contains (
777- mFocusMode )) {
864+ final List < String > supportedFocusModes = parameters .getSupportedFlashModes ();
865+ if ( supportedFocusModes != null && supportedFocusModes . contains ( mFocusMode )) {
778866 parameters .setFocusMode (mFocusMode );
779867 } else {
780868 Log .i (TAG , "Camera focus mode: " + mFocusMode + " is not supported on this device." );
@@ -785,13 +873,11 @@ private Camera createCamera() {
785873 mFocusMode = parameters .getFocusMode ();
786874
787875 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- }
876+ final List <String > supportedFlashModes = parameters .getSupportedFlashModes ();
877+ if (supportedFlashModes != null && supportedFlashModes .contains (mFlashMode )) {
878+ parameters .setFlashMode (mFlashMode );
879+ } else {
880+ Log .i (TAG , "Camera flash mode: " + mFlashMode + " is not supported on this device." );
795881 }
796882 }
797883
@@ -972,6 +1058,12 @@ private int[] selectPreviewFpsRange(Camera camera, float desiredPreviewFps) {
9721058 return selectedFpsRange ;
9731059 }
9741060
1061+ public void updateRotation () {
1062+ if (mCamera != null ) {
1063+ setRotation (mCamera , mCamera .getParameters (), mRequestedCameraId );
1064+ }
1065+ }
1066+
9751067 /**
9761068 * Calculates the correct rotation for the given camera id and sets the rotation in the
9771069 * parameters. It also sets the camera's display orientation and rotation.
@@ -1211,4 +1303,10 @@ public void run() {
12111303 }
12121304 }
12131305 }
1306+
1307+ public interface CameraSourceStateListener
1308+ {
1309+ void onCameraSourceStarted ();
1310+ void onCameraSourceStopped ();
1311+ }
12141312}
0 commit comments