mirror of
https://github.com/zxing/zxing.git
synced 2025-01-12 11:47:26 -08:00
Closes issue #484 : back-port almost all the orientaiton logic from BS+ to handle reverse-mounted cameras on devices like the Nexus 5X
This commit is contained in:
parent
f8623ccb12
commit
f58271e146
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
24
android/src/com/google/zxing/client/android/camera/open/CameraFacing.java
Executable file
24
android/src/com/google/zxing/client/android/camera/open/CameraFacing.java
Executable file
|
@ -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!
|
||||
|
||||
}
|
52
android/src/com/google/zxing/client/android/camera/open/OpenCamera.java
Executable file
52
android/src/com/google/zxing/client/android/camera/open/OpenCamera.java
Executable file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue