diff --git a/android-integration/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java b/android-integration/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java index 761b430a5..040b20600 100644 --- a/android-integration/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java +++ b/android-integration/src/main/java/com/google/zxing/integration/android/IntentIntegrator.java @@ -249,25 +249,50 @@ public class IntentIntegrator { } /** - * Initiates a scan for all known barcode types. + * Initiates a scan for all known barcode types with the default camera. * * @return the {@link AlertDialog} that was shown to the user prompting them to download the app - * if a prompt was needed, or null otherwiseo + * if a prompt was needed, or null otherwise. */ public final AlertDialog initiateScan() { - return initiateScan(ALL_CODE_TYPES); + return initiateScan(ALL_CODE_TYPES, -1); + } + + /** + * Initiates a scan for all known barcode types with the specified camera. + * + * @param cameraId camera ID of the camera to use. A negative value means "no preference". + * @return the {@link AlertDialog} that was shown to the user prompting them to download the app + * if a prompt was needed, or null otherwise. + */ + public final AlertDialog initiateScan(int cameraId) { + return initiateScan(ALL_CODE_TYPES, cameraId); } /** - * Initiates a scan only for a certain set of barcode types, given as strings corresponding + * Initiates a scan, using the default camera, only for a certain set of barcode types, given as strings corresponding * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants * like {@link #PRODUCT_CODE_TYPES} for example. * * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for * @return the {@link AlertDialog} that was shown to the user prompting them to download the app - * if a prompt was needed, or null otherwise + * if a prompt was needed, or null otherwise. */ public final AlertDialog initiateScan(Collection desiredBarcodeFormats) { + return initiateScan(desiredBarcodeFormats, -1); + } + + /** + * Initiates a scan, using the specified camera, only for a certain set of barcode types, given as strings corresponding + * to their names in ZXing's {@code BarcodeFormat} class like "UPC_A". You can supply constants + * like {@link #PRODUCT_CODE_TYPES} for example. + * + * @param desiredBarcodeFormats names of {@code BarcodeFormat}s to scan for + * @param cameraId camera ID of the camera to use. A negative value means "no preference". + * @return the {@link AlertDialog} that was shown to the user prompting them to download the app + * if a prompt was needed, or null otherwise + */ + public final AlertDialog initiateScan(Collection desiredBarcodeFormats, int cameraId) { Intent intentScan = new Intent(BS_PACKAGE + ".SCAN"); intentScan.addCategory(Intent.CATEGORY_DEFAULT); @@ -283,6 +308,11 @@ public class IntentIntegrator { } intentScan.putExtra("SCAN_FORMATS", joinedByComma.toString()); } + + // check requested camera ID + if (cameraId >= 0) { + intentScan.putExtra("SCAN_CAMERA_ID", cameraId); + } String targetAppPackage = findTargetAppPackage(intentScan); if (targetAppPackage == null) { diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java index 1e7e69fbc..d5a04b146 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivity.java +++ b/android/src/com/google/zxing/client/android/CaptureActivity.java @@ -218,6 +218,13 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal cameraManager.setManualFramingRect(width, height); } } + + if (intent.hasExtra(Intents.Scan.CAMERA_ID)) { + int cameraId = intent.getIntExtra(Intents.Scan.CAMERA_ID, -1); + if (cameraId >= 0) { + cameraManager.setManualCameraId(cameraId); + } + } String customPromptMessage = intent.getStringExtra(Intents.Scan.PROMPT_MESSAGE); if (customPromptMessage != null) { diff --git a/android/src/com/google/zxing/client/android/Intents.java b/android/src/com/google/zxing/client/android/Intents.java index 2bf5a9d26..4c8f5e613 100755 --- a/android/src/com/google/zxing/client/android/Intents.java +++ b/android/src/com/google/zxing/client/android/Intents.java @@ -80,6 +80,13 @@ public final class Intents { * Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}. */ public static final String FORMATS = "SCAN_FORMATS"; + + /** + * Optional parameter to specify the id of the camera from which to recognize barcodes. + * Overrides the default camera that would otherwise would have been selected. + * If provided, should be an int. + */ + public static final String CAMERA_ID = "SCAN_CAMERA_ID"; /** * @see com.google.zxing.DecodeHintType#CHARACTER_SET diff --git a/android/src/com/google/zxing/client/android/camera/CameraManager.java b/android/src/com/google/zxing/client/android/camera/CameraManager.java index 9349b252f..b9e8b2ca7 100755 --- a/android/src/com/google/zxing/client/android/camera/CameraManager.java +++ b/android/src/com/google/zxing/client/android/camera/CameraManager.java @@ -52,6 +52,7 @@ public final class CameraManager { private Rect framingRectInPreview; private boolean initialized; private boolean previewing; + private int requestedCameraId = -1; private int requestedFramingRectWidth; private int requestedFramingRectHeight; /** @@ -65,7 +66,7 @@ public final class CameraManager { this.configManager = new CameraConfigurationManager(context); previewCallback = new PreviewCallback(configManager); } - + /** * Opens the camera driver and initializes the hardware parameters. * @@ -75,7 +76,13 @@ public final class CameraManager { public synchronized void openDriver(SurfaceHolder holder) throws IOException { Camera theCamera = camera; if (theCamera == null) { - theCamera = OpenCameraInterface.open(); + + if (requestedCameraId >= 0) { + theCamera = OpenCameraInterface.open(requestedCameraId); + } else { + theCamera = OpenCameraInterface.open(); + } + if (theCamera == null) { throw new IOException(); } @@ -265,6 +272,21 @@ public final class CameraManager { return framingRectInPreview; } + + /** + * Allows third party apps to specify the camera ID, rather than determine + * it automatically based on available cameras and their orientation. + * + * @param cameraId camera ID of the camera to use. A negative value means "no preference". + */ + public synchronized void setManualCameraId(int cameraId) { + if (initialized) { + throw new IllegalStateException(); + } else { + requestedCameraId = cameraId; + } + } + /** * Allows third party apps to specify the scanning rectangle dimensions, rather than determine * them automatically based on screen resolution. diff --git a/android/src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java b/android/src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java index 20340cfb4..752b13a77 100644 --- a/android/src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java +++ b/android/src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java @@ -26,39 +26,63 @@ public final class OpenCameraInterface { private OpenCameraInterface() { } + /** - * Opens a rear-facing camera with {@link Camera#open(int)}, if one exists, or opens camera 0. + * Opens the requested camera with {@link Camera#open(int)}, if one exists. * + * @param cameraId camera ID of the camera to use. A negative value means "no preference" * @return handle to {@link Camera} that was opened */ - public static Camera open() { + public static Camera open(int cameraId) { int numCameras = Camera.getNumberOfCameras(); if (numCameras == 0) { Log.w(TAG, "No cameras!"); return null; } - - int index = 0; - while (index < numCameras) { - Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); - Camera.getCameraInfo(index, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { - break; + + boolean explicitRequest = cameraId >= 0; + + if (!explicitRequest) { + // Select a camera if no explicit camera requested + int index = 0; + while (index < numCameras) { + Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); + Camera.getCameraInfo(index, cameraInfo); + if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + break; + } + index++; + } + + cameraId = index; + } + + Camera camera; + if (cameraId < numCameras) { + Log.i(TAG, "Opening camera #" + cameraId); + camera = Camera.open(cameraId); + } else { + if (explicitRequest) { + Log.w(TAG, "Requested camera does not exist: " + cameraId); + camera = null; + } else { + Log.i(TAG, "No camera facing back; returning camera #0"); + camera = Camera.open(0); } - index++; } - Camera camera; - if (index < numCameras) { - Log.i(TAG, "Opening camera #" + index); - camera = Camera.open(index); - } else { - Log.i(TAG, "No camera facing back; returning camera #0"); - camera = Camera.open(0); - } - return camera; } + + + /** + * Opens a rear-facing camera with {@link Camera#open(int)}, if one exists, or opens camera 0. + * + * @return handle to {@link Camera} that was opened + */ + public static Camera open() { + return open(-1); + } }