summaryrefslogtreecommitdiff
path: root/android-core/src/main/java/com
diff options
context:
space:
mode:
authorSean Owen <srowen@gmail.com>2014-04-30 18:12:13 +0400
committerSean Owen <srowen@gmail.com>2014-04-30 18:12:13 +0400
commitc1b1b631705922cfdf3e50a1f7b9d3f92ff7ba70 (patch)
tree7fe5edb08ed29335ce36303d63b91f08299b7ed3 /android-core/src/main/java/com
parent49e1127e064af44da8dade70af1e2ef59cd23fe0 (diff)
downloadzxing-c1b1b631705922cfdf3e50a1f7b9d3f92ff7ba70.tar.xz
Refactor common code from across Android apps into module
Diffstat (limited to 'android-core/src/main/java/com')
-rw-r--r--android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java383
1 files changed, 383 insertions, 0 deletions
diff --git a/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java b/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java
new file mode 100644
index 000000000..4fd44f6cf
--- /dev/null
+++ b/android-core/src/main/java/com/google/zxing/client/android/camera/CameraConfigurationUtils.java
@@ -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();
+ }
+
+}