Skip to content
This repository was archived by the owner on Apr 19, 2026. It is now read-only.

Commit cef27f5

Browse files
committed
Make things safer
1. Fallback for requested camera 2. Not crashing when taking pictures at the wrong time 3. Callback for state changes - to be able to take actions only when actually ready 4. Query for modes, like is flash available?
1 parent a558930 commit cef27f5

1 file changed

Lines changed: 136 additions & 17 deletions

File tree

  • visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera

visionSamples/barcode-reader/app/src/main/java/com/google/android/gms/samples/vision/barcodereader/ui/camera/CameraSource.java

Lines changed: 136 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)