Refactor common code from across Android apps into module

This commit is contained in:
Sean Owen 2014-04-30 15:12:13 +01:00
parent 49e1127e06
commit c1b1b63170
8 changed files with 455 additions and 396 deletions

40
android-core/pom.xml Normal file
View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2014 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>android-core</artifactId>
<version>3.0.2-SNAPSHOT</version>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>
</dependency>
</dependencies>
<parent>
<groupId>com.google.zxing</groupId>
<artifactId>zxing-parent</artifactId>
<version>3.0.2-SNAPSHOT</version>
</parent>
<name>ZXing Android Core</name>
<description>Code common to Android applications</description>
</project>

View file

@ -0,0 +1,383 @@
/*
* Copyright (C) 2014 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;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.Log;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* Utility methods for configuring the Android camera.
*
* @author Sean Owen
*/
public final class CameraConfigurationUtils {
private static final String TAG = "CameraConfiguration";
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
private static final double MAX_ASPECT_DISTORTION = 0.15;
private static final int MIN_FPS = 10;
private static final int AREA_PER_1000 = 400;
private CameraConfigurationUtils() {
}
public static void setFocus(Camera.Parameters parameters,
boolean autoFocus,
boolean disableContinuous,
boolean safeMode) {
List<String> supportedFocusModes = parameters.getSupportedFocusModes();
String focusMode = null;
if (autoFocus) {
if (safeMode || disableContinuous) {
focusMode = CameraConfigurationUtils.findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_AUTO);
} else {
focusMode = CameraConfigurationUtils.findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
}
}
// Maybe selected auto-focus but not available, so fall through here:
if (!safeMode && focusMode == null) {
focusMode = CameraConfigurationUtils.findSettableValue("focus mode",
supportedFocusModes,
Camera.Parameters.FOCUS_MODE_MACRO,
Camera.Parameters.FOCUS_MODE_EDOF);
}
if (focusMode != null) {
if (focusMode.equals(parameters.getFocusMode())) {
Log.i(TAG, "Focus mode already set to " + focusMode);
} else {
parameters.setFocusMode(focusMode);
}
}
}
public static void setTorch(Camera.Parameters parameters, boolean on) {
List<String> supportedFlashModes = parameters.getSupportedFlashModes();
String flashMode;
if (on) {
flashMode = CameraConfigurationUtils.findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_TORCH,
Camera.Parameters.FLASH_MODE_ON);
} else {
flashMode = CameraConfigurationUtils.findSettableValue("flash mode",
supportedFlashModes,
Camera.Parameters.FLASH_MODE_OFF);
}
if (flashMode != null) {
if (flashMode.equals(parameters.getFlashMode())) {
Log.i(TAG, "Flash mode already set to " + flashMode);
} else {
Log.i(TAG, "Setting flash mode to " + flashMode);
parameters.setFlashMode(flashMode);
}
}
}
public static void setBestExposure(Camera.Parameters parameters, boolean lightOn) {
int minExposure = parameters.getMinExposureCompensation();
int maxExposure = parameters.getMaxExposureCompensation();
float step = parameters.getExposureCompensationStep();
if ((minExposure != 0 || maxExposure != 0) && step > 0.0f) {
// Set low when light is on
float targetCompensation = lightOn ? MIN_EXPOSURE_COMPENSATION : MAX_EXPOSURE_COMPENSATION;
int compensationSteps = Math.round(targetCompensation / step);
float actualCompensation = step * compensationSteps;
// Clamp value:
compensationSteps = Math.max(Math.min(compensationSteps, maxExposure), minExposure);
if (parameters.getExposureCompensation() == compensationSteps) {
Log.i(TAG, "Exposure compensation already set to " + compensationSteps + " / " + actualCompensation);
} else {
Log.i(TAG, "Setting exposure compensation to " + compensationSteps + " / " + actualCompensation);
parameters.setExposureCompensation(compensationSteps);
}
} else {
Log.i(TAG, "Camera does not support exposure compensation");
}
}
public static void setBestPreviewFPS(Camera.Parameters parameters) {
setBestPreviewFPS(parameters, MIN_FPS);
}
public static void setBestPreviewFPS(Camera.Parameters parameters, int minFPS) {
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
int[] minimumSuitableFpsRange = null;
for (int[] fpsRange : supportedPreviewFpsRanges) {
int fpsMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (fpsMax >= minFPS * 1000 &&
(minimumSuitableFpsRange == null ||
fpsMax > minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])) {
minimumSuitableFpsRange = fpsRange;
}
}
if (minimumSuitableFpsRange == null) {
Log.i(TAG, "No suitable FPS range?");
} else {
int[] currentFpsRange = new int[2];
parameters.getPreviewFpsRange(currentFpsRange);
if (Arrays.equals(currentFpsRange, minimumSuitableFpsRange)) {
Log.i(TAG, "FPS range already set to " + Arrays.toString(minimumSuitableFpsRange));
} else {
Log.i(TAG, "Setting FPS range to " + Arrays.toString(minimumSuitableFpsRange));
parameters.setPreviewFpsRange(minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
public static void setFocusArea(Camera.Parameters parameters) {
if (parameters.getMaxNumFocusAreas() > 0) {
Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
Log.i(TAG, "Setting focus area to : " + toString(middleArea));
parameters.setFocusAreas(middleArea);
} else {
Log.i(TAG, "Device does not support focus areas");
}
}
public static void setMetering(Camera.Parameters parameters) {
if (parameters.getMaxNumMeteringAreas() > 0) {
Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
List<Camera.Area> middleArea = buildMiddleArea(AREA_PER_1000);
Log.i(TAG, "Setting metering area to : " + toString(middleArea));
parameters.setMeteringAreas(middleArea);
} else {
Log.i(TAG, "Device does not support metering areas");
}
}
private static List<Camera.Area> buildMiddleArea(int areaPer1000) {
return Collections.singletonList(
new Camera.Area(new Rect(-areaPer1000, -areaPer1000, areaPer1000, areaPer1000), 1));
}
public static void setVideoStabilization(Camera.Parameters parameters) {
if (parameters.isVideoStabilizationSupported()) {
if (parameters.getVideoStabilization()) {
Log.i(TAG, "Video stabilization already enabled");
} else {
Log.i(TAG, "Enabling video stabilization...");
parameters.setVideoStabilization(true);
}
} else {
Log.i(TAG, "This device does not support video stabilization");
}
}
public static void setBarcodeSceneMode(Camera.Parameters parameters) {
if (Camera.Parameters.SCENE_MODE_BARCODE.equals(parameters.getSceneMode())) {
Log.i(TAG, "Barcode scene mode already set");
return;
}
String sceneMode = findSettableValue("scene mode",
parameters.getSupportedSceneModes(),
Camera.Parameters.SCENE_MODE_BARCODE);
if (sceneMode != null) {
parameters.setSceneMode(sceneMode);
}
}
public static void setMaxZoom(Camera.Parameters parameters) {
int maxZoom = parameters.getMaxZoom();
Log.i(TAG, "Setting zoom to max of " + maxZoom);
setZoom(parameters, maxZoom);
}
public static void setZoom(Camera.Parameters parameters, int zoom) {
if (parameters.isZoomSupported()) {
if (parameters.getZoom() == zoom) {
Log.i(TAG, "Zoom is already set to " + zoom);
} else {
Log.i(TAG, "Setting zoom to " + zoom);
parameters.setZoom(zoom);
}
} else {
Log.i(TAG, "Zoom is not supported");
}
}
public static void setInvertColor(Camera.Parameters parameters) {
if (Camera.Parameters.EFFECT_NEGATIVE.equals(parameters.getColorEffect())) {
Log.i(TAG, "Negative effect already set");
return;
}
String colorMode =
CameraConfigurationUtils.findSettableValue("color effect",
parameters.getSupportedColorEffects(),
Camera.Parameters.EFFECT_NEGATIVE);
if (colorMode != null) {
parameters.setColorEffect(colorMode);
}
}
public static Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
Log.w(TAG, "Device returned no supported preview sizes; using default");
Camera.Size defaultSize = parameters.getPreviewSize();
if (defaultSize == null) {
throw new IllegalStateException("Parameters contained no preview size!");
}
return new Point(defaultSize.width, defaultSize.height);
}
// Sort by size, descending
List<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes);
Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
if (Log.isLoggable(TAG, Log.INFO)) {
StringBuilder previewSizesString = new StringBuilder();
for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
previewSizesString.append(supportedPreviewSize.width).append('x')
.append(supportedPreviewSize.height).append(' ');
}
Log.i(TAG, "Supported preview sizes: " + previewSizesString);
}
double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
// Remove sizes that are unsuitable
Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewSize = it.next();
int realWidth = supportedPreviewSize.width;
int realHeight = supportedPreviewSize.height;
if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
it.remove();
continue;
}
boolean isCandidatePortrait = realWidth < realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
Point exactPoint = new Point(realWidth, realHeight);
Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
return exactPoint;
}
}
// If no exact match, use largest preview size. This was not a great idea on older devices because
// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
// the CPU is much more powerful.
if (!supportedPreviewSizes.isEmpty()) {
Camera.Size largestPreview = supportedPreviewSizes.get(0);
Point largestSize = new Point(largestPreview.width, largestPreview.height);
Log.i(TAG, "Using largest suitable preview size: " + largestSize);
return largestSize;
}
// If there is nothing at all suitable, return current preview size
Camera.Size defaultPreview = parameters.getPreviewSize();
if (defaultPreview == null) {
throw new IllegalStateException("Parameters contained no preview size!");
}
Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
return defaultSize;
}
private static String findSettableValue(String name,
Collection<String> supportedValues,
String... desiredValues) {
Log.i(TAG, "Requesting " + name + " value from among: " + Arrays.toString(desiredValues));
Log.i(TAG, "Supported " + name + " values: " + supportedValues);
if (supportedValues != null) {
for (String desiredValue : desiredValues) {
if (supportedValues.contains(desiredValue)) {
Log.i(TAG, "Can set " + name + " to: " + desiredValue);
return desiredValue;
}
}
}
Log.i(TAG, "No supported values match");
return null;
}
private static String toString(Collection<int[]> arrays) {
if (arrays == null || arrays.isEmpty()) {
return "[]";
}
StringBuilder buffer = new StringBuilder();
buffer.append('[');
Iterator<int[]> it = arrays.iterator();
while (it.hasNext()) {
buffer.append(Arrays.toString(it.next()));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append(']');
return buffer.toString();
}
private static String toString(Iterable<Camera.Area> areas) {
if (areas == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (Camera.Area area : areas) {
result.append(area.rect).append(':').append(area.weight).append(' ');
}
return result.toString();
}
}

View file

@ -27,6 +27,11 @@
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>android-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>

View file

@ -26,15 +26,6 @@ import android.view.Display;
import android.view.WindowManager;
import com.google.zxing.client.android.PreferencesActivity;
import com.google.zxing.client.android.camera.metering.MeteringInterface;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
/**
* A class which deals with reading, parsing, and setting the camera parameters which are used to
@ -44,15 +35,6 @@ final class CameraConfigurationManager {
private static final String TAG = "CameraConfiguration";
// This is bigger than the size of a small screen, which is still supported. The routine
// below will still select the default (presumably 320x240) size for these. This prevents
// accidental selection of very low resolution on some devices.
private static final int MIN_PREVIEW_PIXELS = 480 * 320; // normal screen
private static final float MAX_EXPOSURE_COMPENSATION = 1.5f;
private static final float MIN_EXPOSURE_COMPENSATION = 0.0f;
private static final double MAX_ASPECT_DISTORTION = 0.15;
private static final int MIN_FPS = 5;
private final Context context;
private Point screenResolution;
private Point cameraResolution;
@ -72,7 +54,7 @@ final class CameraConfigurationManager {
display.getSize(theScreenResolution);
screenResolution = theScreenResolution;
Log.i(TAG, "Screen resolution: " + screenResolution);
cameraResolution = findBestPreviewSizeValue(parameters, screenResolution);
cameraResolution = CameraConfigurationUtils.findBestPreviewSizeValue(parameters, screenResolution);
Log.i(TAG, "Camera resolution: " + cameraResolution);
}
@ -94,57 +76,27 @@ final class CameraConfigurationManager {
initializeTorch(parameters, prefs, safeMode);
setBestPreviewFPS(parameters);
CameraConfigurationUtils.setBestPreviewFPS(parameters);
String focusMode = null;
if (prefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true)) {
if (safeMode || prefs.getBoolean(PreferencesActivity.KEY_DISABLE_CONTINUOUS_FOCUS, true)) {
focusMode = findSettableValue(parameters.getSupportedFocusModes(),
Camera.Parameters.FOCUS_MODE_AUTO);
} else {
focusMode = findSettableValue(parameters.getSupportedFocusModes(),
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE,
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO,
Camera.Parameters.FOCUS_MODE_AUTO);
}
}
// Maybe selected auto-focus but not available, so fall through here:
if (!safeMode && focusMode == null) {
focusMode = findSettableValue(parameters.getSupportedFocusModes(),
Camera.Parameters.FOCUS_MODE_MACRO,
Camera.Parameters.FOCUS_MODE_EDOF);
}
if (focusMode != null) {
parameters.setFocusMode(focusMode);
}
CameraConfigurationUtils.setFocus(
parameters,
prefs.getBoolean(PreferencesActivity.KEY_AUTO_FOCUS, true),
prefs.getBoolean(PreferencesActivity.KEY_DISABLE_CONTINUOUS_FOCUS, true),
safeMode);
if (!safeMode) {
if (prefs.getBoolean(PreferencesActivity.KEY_INVERT_SCAN, false)) {
String colorMode = findSettableValue(parameters.getSupportedColorEffects(),
Camera.Parameters.EFFECT_NEGATIVE);
if (colorMode != null) {
parameters.setColorEffect(colorMode);
}
CameraConfigurationUtils.setInvertColor(parameters);
}
if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_BARCODE_SCENE_MODE, true)) {
String sceneMode = findSettableValue(parameters.getSupportedSceneModes(),
Camera.Parameters.SCENE_MODE_BARCODE);
if (sceneMode != null) {
parameters.setSceneMode(sceneMode);
}
CameraConfigurationUtils.setBarcodeSceneMode(parameters);
}
if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_METERING, true)) {
if (parameters.isVideoStabilizationSupported()) {
Log.i(TAG, "Enabling video stabilization...");
parameters.setVideoStabilization(true);
} else {
Log.i(TAG, "This device does not support video stabilization");
}
MeteringInterface.setFocusArea(parameters);
MeteringInterface.setMetering(parameters);
CameraConfigurationUtils.setVideoStabilization(parameters);
CameraConfigurationUtils.setFocusArea(parameters);
CameraConfigurationUtils.setMetering(parameters);
}
}
@ -195,185 +147,11 @@ final class CameraConfigurationManager {
}
private void doSetTorch(Camera.Parameters parameters, boolean newSetting, boolean safeMode) {
String flashMode;
if (newSetting) {
flashMode = findSettableValue(parameters.getSupportedFlashModes(),
Camera.Parameters.FLASH_MODE_TORCH,
Camera.Parameters.FLASH_MODE_ON);
} else {
flashMode = findSettableValue(parameters.getSupportedFlashModes(),
Camera.Parameters.FLASH_MODE_OFF);
}
if (flashMode != null) {
parameters.setFlashMode(flashMode);
}
CameraConfigurationUtils.setTorch(parameters, newSetting);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, true)) {
if (!safeMode) {
int minExposure = parameters.getMinExposureCompensation();
int maxExposure = parameters.getMaxExposureCompensation();
if (minExposure != 0 || maxExposure != 0) {
float step = parameters.getExposureCompensationStep();
int desiredCompensation;
if (newSetting) {
// Light on; set low exposure compensation
desiredCompensation = Math.max((int) (MIN_EXPOSURE_COMPENSATION / step), minExposure);
} else {
// Light off; set high compensation
desiredCompensation = Math.min((int) (MAX_EXPOSURE_COMPENSATION / step), maxExposure);
}
Log.i(TAG, "Setting exposure compensation to " + desiredCompensation + " / " + (step * desiredCompensation));
parameters.setExposureCompensation(desiredCompensation);
} else {
Log.i(TAG, "Camera does not support exposure compensation");
}
}
if (!safeMode && !prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, true)) {
CameraConfigurationUtils.setBestExposure(parameters, newSetting);
}
}
private static void setBestPreviewFPS(Camera.Parameters parameters) {
// Required for Glass compatibility; also improves battery/CPU performance a tad
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
Log.i(TAG, "Supported FPS ranges: " + toString(supportedPreviewFpsRanges));
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
int[] minimumSuitableFpsRange = null;
for (int[] fpsRange : supportedPreviewFpsRanges) {
int fpsMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (fpsMax >= MIN_FPS * 1000 &&
(minimumSuitableFpsRange == null ||
fpsMax > minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])) {
minimumSuitableFpsRange = fpsRange;
}
}
if (minimumSuitableFpsRange == null) {
Log.i(TAG, "No suitable FPS range?");
} else {
int[] currentFpsRange = new int[2];
parameters.getPreviewFpsRange(currentFpsRange);
if (!Arrays.equals(currentFpsRange, minimumSuitableFpsRange)) {
Log.i(TAG, "Setting FPS range to " + Arrays.toString(minimumSuitableFpsRange));
parameters.setPreviewFpsRange(minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
}
// Actually prints the arrays properly:
private static String toString(Collection<int[]> arrays) {
if (arrays == null || arrays.isEmpty()) {
return "[]";
}
StringBuilder buffer = new StringBuilder();
buffer.append('[');
Iterator<int[]> it = arrays.iterator();
while (it.hasNext()) {
buffer.append(Arrays.toString(it.next()));
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append(']');
return buffer.toString();
}
private Point findBestPreviewSizeValue(Camera.Parameters parameters, Point screenResolution) {
List<Camera.Size> rawSupportedSizes = parameters.getSupportedPreviewSizes();
if (rawSupportedSizes == null) {
Log.w(TAG, "Device returned no supported preview sizes; using default");
Camera.Size defaultSize = parameters.getPreviewSize();
return new Point(defaultSize.width, defaultSize.height);
}
// Sort by size, descending
List<Camera.Size> supportedPreviewSizes = new ArrayList<>(rawSupportedSizes);
Collections.sort(supportedPreviewSizes, new Comparator<Camera.Size>() {
@Override
public int compare(Camera.Size a, Camera.Size b) {
int aPixels = a.height * a.width;
int bPixels = b.height * b.width;
if (bPixels < aPixels) {
return -1;
}
if (bPixels > aPixels) {
return 1;
}
return 0;
}
});
if (Log.isLoggable(TAG, Log.INFO)) {
StringBuilder previewSizesString = new StringBuilder();
for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
previewSizesString.append(supportedPreviewSize.width).append('x')
.append(supportedPreviewSize.height).append(' ');
}
Log.i(TAG, "Supported preview sizes: " + previewSizesString);
}
double screenAspectRatio = (double) screenResolution.x / (double) screenResolution.y;
// Remove sizes that are unsuitable
Iterator<Camera.Size> it = supportedPreviewSizes.iterator();
while (it.hasNext()) {
Camera.Size supportedPreviewSize = it.next();
int realWidth = supportedPreviewSize.width;
int realHeight = supportedPreviewSize.height;
if (realWidth * realHeight < MIN_PREVIEW_PIXELS) {
it.remove();
continue;
}
boolean isCandidatePortrait = realWidth < realHeight;
int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
double aspectRatio = (double) maybeFlippedWidth / (double) maybeFlippedHeight;
double distortion = Math.abs(aspectRatio - screenAspectRatio);
if (distortion > MAX_ASPECT_DISTORTION) {
it.remove();
continue;
}
if (maybeFlippedWidth == screenResolution.x && maybeFlippedHeight == screenResolution.y) {
Point exactPoint = new Point(realWidth, realHeight);
Log.i(TAG, "Found preview size exactly matching screen size: " + exactPoint);
return exactPoint;
}
}
// If no exact match, use largest preview size. This was not a great idea on older devices because
// of the additional computation needed. We're likely to get here on newer Android 4+ devices, where
// the CPU is much more powerful.
if (!supportedPreviewSizes.isEmpty()) {
Camera.Size largestPreview = supportedPreviewSizes.get(0);
Point largestSize = new Point(largestPreview.width, largestPreview.height);
Log.i(TAG, "Using largest suitable preview size: " + largestSize);
return largestSize;
}
// If there is nothing at all suitable, return current preview size
Camera.Size defaultPreview = parameters.getPreviewSize();
Point defaultSize = new Point(defaultPreview.width, defaultPreview.height);
Log.i(TAG, "No suitable preview sizes, using default: " + defaultSize);
return defaultSize;
}
private static String findSettableValue(Collection<String> supportedValues,
String... desiredValues) {
Log.i(TAG, "Supported values: " + supportedValues);
String result = null;
if (supportedValues != null) {
for (String desiredValue : desiredValues) {
if (supportedValues.contains(desiredValue)) {
result = desiredValue;
break;
}
}
}
Log.i(TAG, "Settable value: " + result);
return result;
}
}

View file

@ -1,72 +0,0 @@
/*
* Copyright (C) 2014 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.metering;
import java.util.Collections;
import java.util.List;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.Log;
public final class MeteringInterface {
private static final String TAG = MeteringInterface.class.getSimpleName();
private static final int AREA_PER_1000 = 400;
private MeteringInterface() {
}
public static void setFocusArea(Camera.Parameters parameters) {
if (parameters.getMaxNumFocusAreas() > 0) {
Log.i(TAG, "Old focus areas: " + toString(parameters.getFocusAreas()));
List<Camera.Area> middleArea = buildMiddleArea();
Log.i(TAG, "Setting focus area to : " + toString(middleArea));
parameters.setFocusAreas(middleArea);
} else {
Log.i(TAG, "Device does not support focus areas");
}
}
public static void setMetering(Camera.Parameters parameters) {
if (parameters.getMaxNumMeteringAreas() > 0) {
Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
List<Camera.Area> middleArea = buildMiddleArea();
Log.i(TAG, "Setting metering area to : " + toString(middleArea));
parameters.setMeteringAreas(middleArea);
} else {
Log.i(TAG, "Device does not support metering areas");
}
}
private static List<Camera.Area> buildMiddleArea() {
return Collections.singletonList(
new Camera.Area(new Rect(-AREA_PER_1000, -AREA_PER_1000, AREA_PER_1000, AREA_PER_1000), 1));
}
private static String toString(Iterable<Camera.Area> areas) {
if (areas == null) {
return null;
}
StringBuilder result = new StringBuilder();
for (Camera.Area area : areas) {
result.append(area.rect).append(':').append(area.weight).append(' ');
}
return result.toString();
}
}

View file

@ -27,6 +27,11 @@
<artifactId>core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>android-core</artifactId>
<version>${project.parent.version}</version>
</dependency>
<dependency>
<groupId>com.google.android</groupId>
<artifactId>android</artifactId>

View file

@ -16,24 +16,15 @@
package com.google.zxing.client.glass;
import android.graphics.Rect;
import android.hardware.Camera;
import android.util.Log;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.zxing.client.android.camera.CameraConfigurationUtils;
/**
* @author Sean Owen
*/
final class CameraConfigurationManager {
private static final String TAG = "CameraConfiguration";
private static final int AREA_PER_1000 = 400;
private static final int MIN_FPS = 10;
private CameraConfigurationManager() {
}
@ -46,83 +37,11 @@ final class CameraConfigurationManager {
}
private static void configureAdvanced(Camera.Parameters parameters) {
setBestPreviewFPS(parameters);
String sceneMode = findSettableValue(parameters.getSupportedSceneModes(),
Camera.Parameters.SCENE_MODE_BARCODE);
if (sceneMode != null) {
parameters.setSceneMode(sceneMode);
} else {
Log.i(TAG, "Scene mode is not supported");
}
if (parameters.isVideoStabilizationSupported()) {
Log.i(TAG, "Enabling video stabilization...");
parameters.setVideoStabilization(true);
} else {
Log.i(TAG, "This device does not support video stabilization");
}
if (parameters.getMaxNumMeteringAreas() > 0) {
Log.i(TAG, "Old metering areas: " + parameters.getMeteringAreas());
List<Camera.Area> middleArea = Collections.singletonList(
new Camera.Area(new Rect(-AREA_PER_1000, -AREA_PER_1000, AREA_PER_1000, AREA_PER_1000), 1));
parameters.setMeteringAreas(middleArea);
} else {
Log.i(TAG, "Device does not support metering areas");
}
if (parameters.isZoomSupported()) {
Log.i(TAG, "Setting to max zoom");
parameters.setZoom(parameters.getMaxZoom());
} else {
Log.i(TAG, "Zoom is not supported");
}
}
private static String findSettableValue(Collection<String> supportedValues,
String... desiredValues) {
Log.i(TAG, "Supported values: " + supportedValues);
String result = null;
if (supportedValues != null) {
for (String desiredValue : desiredValues) {
if (supportedValues.contains(desiredValue)) {
result = desiredValue;
break;
}
}
}
Log.i(TAG, "Settable value: " + result);
return result;
}
private static void setBestPreviewFPS(Camera.Parameters parameters) {
// Required for Glass compatibility; also improves battery/CPU performance a tad
List<int[]> supportedPreviewFpsRanges = parameters.getSupportedPreviewFpsRange();
if (supportedPreviewFpsRanges != null && !supportedPreviewFpsRanges.isEmpty()) {
int[] minimumSuitableFpsRange = null;
for (int[] fpsRange : supportedPreviewFpsRanges) {
int fpsMax = fpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX];
if (fpsMax >= MIN_FPS * 1000 &&
(minimumSuitableFpsRange == null ||
fpsMax > minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX])) {
minimumSuitableFpsRange = fpsRange;
}
}
if (minimumSuitableFpsRange == null) {
Log.i(TAG, "No suitable FPS range?");
} else {
int[] currentFpsRange = new int[2];
parameters.getPreviewFpsRange(currentFpsRange);
if (!Arrays.equals(currentFpsRange, minimumSuitableFpsRange)) {
Log.i(TAG, "Setting FPS range to " + Arrays.toString(minimumSuitableFpsRange));
parameters.setPreviewFpsRange(minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
minimumSuitableFpsRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
}
}
}
CameraConfigurationUtils.setBestPreviewFPS(parameters);
CameraConfigurationUtils.setBarcodeSceneMode(parameters);
CameraConfigurationUtils.setVideoStabilization(parameters);
CameraConfigurationUtils.setMetering(parameters);
CameraConfigurationUtils.setMaxZoom(parameters);
}
}

View file

@ -507,6 +507,7 @@
</property>
</activation>
<modules>
<module>android-core</module>
<module>android</module>
<module>androidtest</module>
<module>glass</module>