From f58271e14651c42a6ea2cf1d17dd1281206bf898 Mon Sep 17 00:00:00 2001 From: Sean Owen Date: Mon, 26 Oct 2015 18:02:23 +0000 Subject: [PATCH] Closes issue #484 : back-port almost all the orientaiton logic from BS+ to handle reverse-mounted cameras on devices like the Nexus 5X --- .../zxing/client/android/CaptureActivity.java | 23 +++- .../camera/CameraConfigurationManager.java | 123 +++++++++++++++--- .../client/android/camera/CameraManager.java | 38 +++--- .../android/camera/open/CameraFacing.java | 24 ++++ .../android/camera/open/OpenCamera.java | 52 ++++++++ .../camera/open/OpenCameraInterface.java | 42 +++--- pom.xml | 2 +- 7 files changed, 247 insertions(+), 57 deletions(-) create mode 100755 android/src/com/google/zxing/client/android/camera/open/CameraFacing.java create mode 100755 android/src/com/google/zxing/client/android/camera/open/OpenCamera.java diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java index daa72d7a1..ee33b58c7 100755 --- a/android/src/com/google/zxing/client/android/CaptureActivity.java +++ b/android/src/com/google/zxing/client/android/CaptureActivity.java @@ -37,6 +37,7 @@ import android.app.AlertDialog; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; @@ -266,12 +267,22 @@ public final class CaptureActivity extends Activity implements SurfaceHolder.Cal private int getCurrentOrientation() { int rotation = getWindowManager().getDefaultDisplay().getRotation(); - switch (rotation) { - case Surface.ROTATION_0: - case Surface.ROTATION_90: - return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - default: - return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { + switch (rotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_90: + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + default: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + } + } else { + switch (rotation) { + case Surface.ROTATION_0: + case Surface.ROTATION_270: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + default: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + } } } diff --git a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java index 6faac7ad7..a18e37e22 100644 --- a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java +++ b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java @@ -23,9 +23,12 @@ import android.hardware.Camera; import android.preference.PreferenceManager; import android.util.Log; import android.view.Display; +import android.view.Surface; import android.view.WindowManager; import com.google.zxing.client.android.PreferencesActivity; +import com.google.zxing.client.android.camera.open.CameraFacing; +import com.google.zxing.client.android.camera.open.OpenCamera; /** * A class which deals with reading, parsing, and setting the camera parameters which are used to @@ -36,8 +39,12 @@ final class CameraConfigurationManager { private static final String TAG = "CameraConfiguration"; private final Context context; + private int cwNeededRotation; + private int cwRotationFromDisplayToCamera; private Point screenResolution; private Point cameraResolution; + private Point bestPreviewSize; + private Point previewSizeOnScreen; CameraConfigurationManager(Context context) { this.context = context; @@ -46,20 +53,94 @@ final class CameraConfigurationManager { /** * Reads, one time, values from the camera that are needed by the app. */ - void initFromCameraParameters(Camera camera) { - Camera.Parameters parameters = camera.getParameters(); + void initFromCameraParameters(OpenCamera camera) { + Camera.Parameters parameters = camera.getCamera().getParameters(); WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); Display display = manager.getDefaultDisplay(); + + int displayRotation = display.getRotation(); + int cwRotationFromNaturalToDisplay; + switch (displayRotation) { + case Surface.ROTATION_0: + cwRotationFromNaturalToDisplay = 0; + break; + case Surface.ROTATION_90: + cwRotationFromNaturalToDisplay = 90; + break; + case Surface.ROTATION_180: + cwRotationFromNaturalToDisplay = 180; + break; + case Surface.ROTATION_270: + cwRotationFromNaturalToDisplay = 270; + break; + default: + // Have seen this return incorrect values like -90 + if (displayRotation % 90 == 0) { + cwRotationFromNaturalToDisplay = (360 + displayRotation) % 360; + } else { + throw new IllegalArgumentException("Bad rotation: " + displayRotation); + } + } + Log.i(TAG, "Display at: " + cwRotationFromNaturalToDisplay); + + int cwRotationFromNaturalToCamera = camera.getOrientation(); + Log.i(TAG, "Camera at: " + cwRotationFromNaturalToCamera); + + // Still not 100% sure about this. But acts like we need to flip this: + if (camera.getFacing() == CameraFacing.FRONT) { + cwRotationFromNaturalToCamera = (360 - cwRotationFromNaturalToCamera) % 360; + Log.i(TAG, "Front camera overriden to: " + cwRotationFromNaturalToCamera); + } + + /* + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + String overrideRotationString; + if (camera.getFacing() == CameraFacing.FRONT) { + overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION_FRONT, null); + } else { + overrideRotationString = prefs.getString(PreferencesActivity.KEY_FORCE_CAMERA_ORIENTATION, null); + } + if (overrideRotationString != null && !"-".equals(overrideRotationString)) { + Log.i(TAG, "Overriding camera manually to " + overrideRotationString); + cwRotationFromNaturalToCamera = Integer.parseInt(overrideRotationString); + } + */ + + cwRotationFromDisplayToCamera = + (360 + cwRotationFromNaturalToCamera - cwRotationFromNaturalToDisplay) % 360; + Log.i(TAG, "Final display orientation: " + cwRotationFromDisplayToCamera); + if (camera.getFacing() == CameraFacing.FRONT) { + Log.i(TAG, "Compensating rotation for front camera"); + cwNeededRotation = (360 - cwRotationFromDisplayToCamera) % 360; + } else { + cwNeededRotation = cwRotationFromDisplayToCamera; + } + Log.i(TAG, "Clockwise rotation from display to camera: " + cwNeededRotation); + Point theScreenResolution = new Point(); display.getSize(theScreenResolution); screenResolution = theScreenResolution; - Log.i(TAG, "Screen resolution: " + screenResolution); + Log.i(TAG, "Screen resolution in current orientation: " + screenResolution); cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution); Log.i(TAG, "Camera resolution: " + cameraResolution); + bestPreviewSize = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution); + Log.i(TAG, "Best available preview size: " + bestPreviewSize); + + boolean isScreenPortrait = screenResolution.x < screenResolution.y; + boolean isPreviewSizePortrait = bestPreviewSize.x < bestPreviewSize.y; + + if (isScreenPortrait == isPreviewSizePortrait) { + previewSizeOnScreen = bestPreviewSize; + } else { + previewSizeOnScreen = new Point(bestPreviewSize.y, bestPreviewSize.x); + } + Log.i(TAG, "Preview size on screen: " + previewSizeOnScreen); } - void setDesiredCameraParameters(Camera camera, boolean safeMode) { - Camera.Parameters parameters = camera.getParameters(); + void setDesiredCameraParameters(OpenCamera camera, boolean safeMode) { + + Camera theCamera = camera.getCamera(); + Camera.Parameters parameters = theCamera.getParameters(); if (parameters == null) { Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration."); @@ -99,22 +180,30 @@ final class CameraConfigurationManager { } - parameters.setPreviewSize(cameraResolution.x, cameraResolution.y); + parameters.setPreviewSize(bestPreviewSize.x, bestPreviewSize.y); - Log.i(TAG, "Final camera parameters: " + parameters.flatten()); + theCamera.setParameters(parameters); - camera.setParameters(parameters); + theCamera.setDisplayOrientation(cwRotationFromDisplayToCamera); - Camera.Parameters afterParameters = camera.getParameters(); + Camera.Parameters afterParameters = theCamera.getParameters(); Camera.Size afterSize = afterParameters.getPreviewSize(); - if (afterSize!= null && (cameraResolution.x != afterSize.width || cameraResolution.y != afterSize.height)) { - Log.w(TAG, "Camera said it supported preview size " + cameraResolution.x + 'x' + cameraResolution.y + - ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height); - cameraResolution.x = afterSize.width; - cameraResolution.y = afterSize.height; + if (afterSize != null && (bestPreviewSize.x != afterSize.width || bestPreviewSize.y != afterSize.height)) { + Log.w(TAG, "Camera said it supported preview size " + bestPreviewSize.x + 'x' + bestPreviewSize.y + + ", but after setting it, preview size is " + afterSize.width + 'x' + afterSize.height); + bestPreviewSize.x = afterSize.width; + bestPreviewSize.y = afterSize.height; } } + Point getBestPreviewSize() { + return bestPreviewSize; + } + + Point getPreviewSizeOnScreen() { + return previewSizeOnScreen; + } + Point getCameraResolution() { return cameraResolution; } @@ -123,11 +212,15 @@ final class CameraConfigurationManager { return screenResolution; } + int getCWNeededRotation() { + return cwNeededRotation; + } + boolean getTorchState(Camera camera) { if (camera != null) { Camera.Parameters parameters = camera.getParameters(); if (parameters != null) { - String flashMode = parameters.getFlashMode(); + String flashMode = camera.getParameters().getFlashMode(); return flashMode != null && (Camera.Parameters.FLASH_MODE_ON.equals(flashMode) || Camera.Parameters.FLASH_MODE_TORCH.equals(flashMode)); 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 c0caa2b8d..8a8900f86 100755 --- a/android/src/com/google/zxing/client/android/camera/CameraManager.java +++ b/android/src/com/google/zxing/client/android/camera/CameraManager.java @@ -24,6 +24,7 @@ import android.os.Handler; import android.util.Log; import android.view.SurfaceHolder; import com.google.zxing.PlanarYUVLuminanceSource; +import com.google.zxing.client.android.camera.open.OpenCamera; import com.google.zxing.client.android.camera.open.OpenCameraInterface; import java.io.IOException; @@ -46,7 +47,7 @@ public final class CameraManager { private final Context context; private final CameraConfigurationManager configManager; - private Camera camera; + private OpenCamera camera; private AutoFocusManager autoFocusManager; private Rect framingRect; private Rect framingRectInPreview; @@ -74,16 +75,14 @@ public final class CameraManager { * @throws IOException Indicates the camera driver failed to open. */ public synchronized void openDriver(SurfaceHolder holder) throws IOException { - Camera theCamera = camera; + OpenCamera theCamera = camera; if (theCamera == null) { - theCamera = OpenCameraInterface.open(requestedCameraId); if (theCamera == null) { - throw new IOException(); + throw new IOException("Camera.open() failed to return object from driver"); } camera = theCamera; } - theCamera.setPreviewDisplay(holder); if (!initialized) { initialized = true; @@ -95,7 +94,8 @@ public final class CameraManager { } } - Camera.Parameters parameters = theCamera.getParameters(); + Camera cameraObject = theCamera.getCamera(); + Camera.Parameters parameters = cameraObject.getParameters(); String parametersFlattened = parameters == null ? null : parameters.flatten(); // Save these, temporarily try { configManager.setDesiredCameraParameters(theCamera, false); @@ -105,10 +105,10 @@ public final class CameraManager { Log.i(TAG, "Resetting to saved camera params: " + parametersFlattened); // Reset: if (parametersFlattened != null) { - parameters = theCamera.getParameters(); + parameters = cameraObject.getParameters(); parameters.unflatten(parametersFlattened); try { - theCamera.setParameters(parameters); + cameraObject.setParameters(parameters); configManager.setDesiredCameraParameters(theCamera, true); } catch (RuntimeException re2) { // Well, darn. Give up @@ -116,6 +116,7 @@ public final class CameraManager { } } } + cameraObject.setPreviewDisplay(holder); } @@ -128,7 +129,7 @@ public final class CameraManager { */ public synchronized void closeDriver() { if (camera != null) { - camera.release(); + camera.getCamera().release(); camera = null; // Make sure to clear these each time we close the camera, so that any scanning rect // requested by intent is forgotten. @@ -141,11 +142,11 @@ public final class CameraManager { * Asks the camera hardware to begin drawing preview frames to the screen. */ public synchronized void startPreview() { - Camera theCamera = camera; + OpenCamera theCamera = camera; if (theCamera != null && !previewing) { - theCamera.startPreview(); + theCamera.getCamera().startPreview(); previewing = true; - autoFocusManager = new AutoFocusManager(context, camera); + autoFocusManager = new AutoFocusManager(context, theCamera.getCamera()); } } @@ -158,7 +159,7 @@ public final class CameraManager { autoFocusManager = null; } if (camera != null && previewing) { - camera.stopPreview(); + camera.getCamera().stopPreview(); previewCallback.setHandler(null, 0); previewing = false; } @@ -170,12 +171,13 @@ public final class CameraManager { * @param newSetting if {@code true}, light should be turned on if currently off. And vice versa. */ public synchronized void setTorch(boolean newSetting) { - if (newSetting != configManager.getTorchState(camera)) { - if (camera != null) { + OpenCamera theCamera = camera; + if (theCamera != null) { + if (newSetting != configManager.getTorchState(theCamera.getCamera())) { if (autoFocusManager != null) { autoFocusManager.stop(); } - configManager.setTorch(camera, newSetting); + configManager.setTorch(theCamera.getCamera(), newSetting); if (autoFocusManager != null) { autoFocusManager.start(); } @@ -192,10 +194,10 @@ public final class CameraManager { * @param message The what field of the message to be sent. */ public synchronized void requestPreviewFrame(Handler handler, int message) { - Camera theCamera = camera; + OpenCamera theCamera = camera; if (theCamera != null && previewing) { previewCallback.setHandler(handler, message); - theCamera.setOneShotPreviewCallback(previewCallback); + theCamera.getCamera().setOneShotPreviewCallback(previewCallback); } } diff --git a/android/src/com/google/zxing/client/android/camera/open/CameraFacing.java b/android/src/com/google/zxing/client/android/camera/open/CameraFacing.java new file mode 100755 index 000000000..20fd4e3c5 --- /dev/null +++ b/android/src/com/google/zxing/client/android/camera/open/CameraFacing.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2015 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.android.camera.open; + +public enum CameraFacing { + + BACK, // must be value 0! + FRONT, // must be value 1! + +} diff --git a/android/src/com/google/zxing/client/android/camera/open/OpenCamera.java b/android/src/com/google/zxing/client/android/camera/open/OpenCamera.java new file mode 100755 index 000000000..ddac7345b --- /dev/null +++ b/android/src/com/google/zxing/client/android/camera/open/OpenCamera.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2015 ZXing authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.zxing.client.android.camera.open; + +import android.hardware.Camera; + +public final class OpenCamera { + + private final int index; + private final Camera camera; + private final CameraFacing facing; + private final int orientation; + + public OpenCamera(int index, Camera camera, CameraFacing facing, int orientation) { + this.index = index; + this.camera = camera; + this.facing = facing; + this.orientation = orientation; + } + + public Camera getCamera() { + return camera; + } + + public CameraFacing getFacing() { + return facing; + } + + public int getOrientation() { + return orientation; + } + + @Override + public String toString() { + return "Camera #" + index + " : " + facing + ',' + orientation; + } + +} 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 1f53bdbc2..925e70844 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 @@ -36,8 +36,8 @@ public final class OpenCameraInterface { * or {@link #NO_REQUESTED_CAMERA} means "no preference" * @return handle to {@link Camera} that was opened */ - public static Camera open(int cameraId) { - + public static OpenCamera open(int cameraId) { + int numCameras = Camera.getNumberOfCameras(); if (numCameras == 0) { Log.w(TAG, "No cameras!"); @@ -46,36 +46,44 @@ public final class OpenCameraInterface { boolean explicitRequest = cameraId >= 0; - if (!explicitRequest) { - // Select a camera if no explicit camera requested - int index = 0; - while (index < numCameras) { + Camera.CameraInfo selectedCameraInfo = null; + int index = 0; + while (index < numCameras) { + if (cameraId == NO_REQUESTED_CAMERA || index == cameraId) { Camera.CameraInfo cameraInfo = new Camera.CameraInfo(); Camera.getCameraInfo(index, cameraInfo); - if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) { + CameraFacing reportedFacing = CameraFacing.values()[cameraInfo.facing]; + if (reportedFacing == CameraFacing.BACK) { + selectedCameraInfo = cameraInfo; break; } - index++; } - - cameraId = index; + index++; } Camera camera; - if (cameraId < numCameras) { - Log.i(TAG, "Opening camera #" + cameraId); - camera = Camera.open(cameraId); + if (index < numCameras) { + Log.i(TAG, "Opening camera #" + index); + camera = Camera.open(index); } 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"); + Log.i(TAG, "No camera facing " + CameraFacing.BACK + "; returning camera #0"); camera = Camera.open(0); + selectedCameraInfo = new Camera.CameraInfo(); + Camera.getCameraInfo(0, selectedCameraInfo); } } - - return camera; + + if (camera == null) { + return null; + } + return new OpenCamera(index, + camera, + CameraFacing.values()[selectedCameraInfo.facing], + selectedCameraInfo.orientation); } - + } diff --git a/pom.xml b/pom.xml index 1f239978e..64a92092e 100644 --- a/pom.xml +++ b/pom.xml @@ -349,7 +349,7 @@ com.simpligility.maven.plugins android-maven-plugin - 4.2.1 + 4.3.0 true