[SCM] Multi-format 1D/2D barcode image processing library branch, upstream, updated. 24d4480bc48cf9eabf7b2bd2f528248b0e458809

srowen srowen at 59b500cc-1b3d-0410-9834-0bbf25fbcc57
Wed Aug 4 01:30:56 UTC 2010


The following commit has been merged in the upstream branch:
commit 4b3bfd8a8f16dad6a1413f75e68a5776e9e308b7
Author: srowen <srowen at 59b500cc-1b3d-0410-9834-0bbf25fbcc57>
Date:   Wed Apr 7 18:03:14 2010 +0000

    Issue 376: re-set camera params after first auto-focus callback to make it work on Droid. Along the way, did a very big reorganization of CameraManager since it was becoming spaghetti code
    
    git-svn-id: http://zxing.googlecode.com/svn/trunk@1292 59b500cc-1b3d-0410-9834-0bbf25fbcc57

diff --git a/android/src/com/google/zxing/client/android/CameraManager.java b/android/src/com/google/zxing/client/android/CameraManager.java
deleted file mode 100755
index c18fc6d..0000000
--- a/android/src/com/google/zxing/client/android/CameraManager.java
+++ /dev/null
@@ -1,530 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import com.google.zxing.ResultPoint;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.graphics.PixelFormat;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.hardware.Camera;
-import android.os.Build;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.PreferenceManager;
-import android.util.Log;
-import android.view.Display;
-import android.view.SurfaceHolder;
-import android.view.WindowManager;
-
-import java.io.IOException;
-import java.util.regex.Pattern;
-
-/**
- * This object wraps the Camera service object and expects to be the only one talking to it. The
- * implementation encapsulates the steps needed to take preview-sized images, which are used for
- * both preview and decoding.
- *
- * @author dswitkin at google.com (Daniel Switkin)
- */
-final class CameraManager {
-
-  private static final String TAG = CameraManager.class.getSimpleName();
-
-  private static final int MIN_FRAME_WIDTH = 240;
-  private static final int MIN_FRAME_HEIGHT = 240;
-  private static final int MAX_FRAME_WIDTH = 480;
-  private static final int MAX_FRAME_HEIGHT = 360;
-
-  private static final int TEN_DESIRED_ZOOM = 27;
-  private static final int DESIRED_SHARPNESS = 30;
-
-  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
-
-  private static CameraManager cameraManager;
-
-  private Camera camera;
-  private final Context context;
-  private Point screenResolution;
-  private Point cameraResolution;
-  private Rect framingRect;
-  private Handler previewHandler;
-  private int previewMessage;
-  private Handler autoFocusHandler;
-  private int autoFocusMessage;
-  private boolean initialized;
-  private boolean previewing;
-  private int previewFormat;
-  private String previewFormatString;
-  private final boolean useOneShotPreviewCallback;
-
-  /**
-   * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
-   * clear the handler so it will only receive one message.
-   */
-  private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
-    public void onPreviewFrame(byte[] data, Camera camera) {
-      if (!useOneShotPreviewCallback) {
-        camera.setPreviewCallback(null);
-      }
-      if (previewHandler != null) {
-        Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
-            cameraResolution.y, data);
-        message.sendToTarget();
-        previewHandler = null;
-      }
-    }
-  };
-
-  /**
-   * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
-   */
-  private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
-    public void onAutoFocus(boolean success, Camera camera) {
-      if (autoFocusHandler != null) {
-        Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
-        // Simulate continuous autofocus by sending a focus request every 1.5 seconds.
-        autoFocusHandler.sendMessageDelayed(message, 1500L);
-        autoFocusHandler = null;
-      }
-    }
-  };
-
-  /**
-   * Initializes this static object with the Context of the calling Activity.
-   *
-   * @param context The Activity which wants to use the camera.
-   */
-  public static void init(Context context) {
-    if (cameraManager == null) {
-      cameraManager = new CameraManager(context);
-    }
-  }
-
-  /**
-   * Gets the CameraManager singleton instance.
-   *
-   * @return A reference to the CameraManager singleton.
-   */
-  public static CameraManager get() {
-    return cameraManager;
-  }
-
-  private CameraManager(Context context) {
-    this.context = context;
-    camera = null;
-    initialized = false;
-    previewing = false;
-
-    // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
-    // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
-    // the more efficient one shot callback, as the older one can swamp the system and cause it
-    // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
-    useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
-  }
-
-  /**
-   * Opens the camera driver and initializes the hardware parameters.
-   *
-   * @param holder The surface object which the camera will draw preview frames into.
-   * @throws IOException Indicates the camera driver failed to open.
-   */
-  public void openDriver(SurfaceHolder holder) throws IOException {
-    if (camera == null) {
-      camera = Camera.open();
-      if (camera == null) {
-        throw new IOException();
-      }
-      camera.setPreviewDisplay(holder);
-
-      if (!initialized) {
-        initialized = true;
-        getScreenResolution();
-      }
-
-      setCameraParameters();
-      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
-      if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
-        FlashlightManager.enableFlashlight();
-      }
-    }
-  }
-
-  /**
-   * Closes the camera driver if still in use.
-   */
-  public void closeDriver() {
-    if (camera != null) {
-      FlashlightManager.disableFlashlight();
-      camera.release();
-      camera = null;
-    }
-  }
-
-  /**
-   * Asks the camera hardware to begin drawing preview frames to the screen.
-   */
-  public void startPreview() {
-    if (camera != null && !previewing) {
-      camera.startPreview();
-      previewing = true;
-    }
-  }
-
-  /**
-   * Tells the camera to stop drawing preview frames.
-   */
-  public void stopPreview() {
-    if (camera != null && previewing) {
-      if (!useOneShotPreviewCallback) {
-        camera.setPreviewCallback(null);
-      }
-      camera.stopPreview();
-      previewHandler = null;
-      autoFocusHandler = null;
-      previewing = false;
-    }
-  }
-
-  /**
-   * A single preview frame will be returned to the handler supplied. The data will arrive as byte[]
-   * in the message.obj field, with width and height encoded as message.arg1 and message.arg2,
-   * respectively.
-   *
-   * @param handler The handler to send the message to.
-   * @param message The what field of the message to be sent.
-   */
-  public void requestPreviewFrame(Handler handler, int message) {
-    if (camera != null && previewing) {
-      previewHandler = handler;
-      previewMessage = message;
-      if (useOneShotPreviewCallback) {
-        camera.setOneShotPreviewCallback(previewCallback);
-      } else {
-        camera.setPreviewCallback(previewCallback);
-      }
-    }
-  }
-
-  /**
-   * Asks the camera hardware to perform an autofocus.
-   *
-   * @param handler The Handler to notify when the autofocus completes.
-   * @param message The message to deliver.
-   */
-  public void requestAutoFocus(Handler handler, int message) {
-    if (camera != null && previewing) {
-      autoFocusHandler = handler;
-      autoFocusMessage = message;
-      camera.autoFocus(autoFocusCallback);
-    }
-  }
-
-  /**
-   * Calculates the framing rect which the UI should draw to show the user where to place the
-   * barcode. This target helps with alignment as well as forces the user to hold the device
-   * far enough away to ensure the image will be in focus.
-   *
-   * @return The rectangle to draw on screen in window coordinates.
-   */
-  public Rect getFramingRect() {
-    if (framingRect == null) {
-      if (camera == null) {
-        return null;
-      }
-      int width = cameraResolution.x * 3 / 4;
-      if (width < MIN_FRAME_WIDTH) {
-        width = MIN_FRAME_WIDTH;
-      } else if (width > MAX_FRAME_WIDTH) {
-        width = MAX_FRAME_WIDTH;
-      }
-      int height = cameraResolution.y * 3 / 4;
-      if (height < MIN_FRAME_HEIGHT) {
-        height = MIN_FRAME_HEIGHT;
-      } else if (height > MAX_FRAME_HEIGHT) {
-        height = MAX_FRAME_HEIGHT;
-      }
-      int leftOffset = (cameraResolution.x - width) / 2;
-      int topOffset = (cameraResolution.y - height) / 2;
-      framingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
-      Log.v(TAG, "Calculated framing rect: " + framingRect);
-    }
-    return framingRect;
-  }
-
-  /**
-   * Converts the result points from still resolution coordinates to screen coordinates.
-   *
-   * @param points The points returned by the Reader subclass through Result.getResultPoints().
-   * @return An array of Points scaled to the size of the framing rect and offset appropriately
-   *         so they can be drawn in screen coordinates.
-   */
-  public Point[] convertResultPoints(ResultPoint[] points) {
-    Rect frame = getFramingRect();
-    int count = points.length;
-    Point[] output = new Point[count];
-    for (int x = 0; x < count; x++) {
-      output[x] = new Point();
-      output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
-      output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
-    }
-    return output;
-  }
-
-  /**
-   * A factory method to build the appropriate LuminanceSource object based on the format
-   * of the preview buffers, as described by Camera.Parameters.
-   *
-   * @param data A preview frame.
-   * @param width The width of the image.
-   * @param height The height of the image.
-   * @return A PlanarYUVLuminanceSource instance.
-   */
-  public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
-    Rect rect = getFramingRect();
-    switch (previewFormat) {
-      // This is the standard Android format which all devices are REQUIRED to support.
-      // In theory, it's the only one we should ever care about.
-      case PixelFormat.YCbCr_420_SP:
-      // This format has never been seen in the wild, but is compatible as we only care
-      // about the Y channel, so allow it.
-      case PixelFormat.YCbCr_422_SP:
-        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
-            rect.width(), rect.height());
-      default:
-        // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
-        // Fortunately, it too has all the Y data up front, so we can read it.
-        if ("yuv420p".equals(previewFormatString)) {
-          return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
-            rect.width(), rect.height());
-        }
-    }
-    throw new IllegalArgumentException("Unsupported picture format: " +
-        previewFormat + '/' + previewFormatString);
-  }
-
-  /**
-   * Sets the camera up to take preview images which are used for both preview and decoding.
-   * We detect the preview format here so that buildLuminanceSource() can build an appropriate
-   * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
-   * and the planar Y can be used for barcode scanning without a copy in some cases.
-   */
-  private void setCameraParameters() {
-    Camera.Parameters parameters = camera.getParameters();
-    previewFormat = parameters.getPreviewFormat();
-    previewFormatString = parameters.get("preview-format");
-    Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
-
-    cameraResolution = getCameraResolution(parameters);
-    Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
-    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
-
-    setFlash(parameters);
-    setZoom(parameters);
-    //setSharpness(parameters);
-
-    camera.setParameters(parameters);
-  }
-
-  private Point getScreenResolution() {
-    if (screenResolution == null) {
-      WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-      Display display = manager.getDefaultDisplay();
-      screenResolution = new Point(display.getWidth(), display.getHeight());
-    }
-    return screenResolution;
-  }
-
-  private Point getCameraResolution(Camera.Parameters parameters) {
-
-    String previewSizeValueString = parameters.get("preview-size-values");
-    // saw this on Xperia
-    if (previewSizeValueString == null) {
-      previewSizeValueString = parameters.get("preview-size-value");
-    }
-
-    Point cameraResolution = null;
-    
-    if (previewSizeValueString != null) {
-      Log.v(TAG, "preview-size parameter: " + previewSizeValueString);
-      cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
-    }
-
-    if (cameraResolution == null) {
-      // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
-      cameraResolution = new Point(
-          (screenResolution.x >> 3) << 3,
-          (screenResolution.y >> 3) << 3);
-    }
-
-    return cameraResolution;
-  }
-
-  private static Point findBestPreviewSizeValue(String previewSizeValueString, Point screenResolution) {
-    int bestX = 0;
-    int bestY = 0;
-    int diff = Integer.MAX_VALUE;
-    for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
-
-      previewSize = previewSize.trim();
-      int dimPosition = previewSize.indexOf('x');
-      if (dimPosition < 0) {
-        Log.w(TAG, "Bad preview-size: " + previewSize);
-        continue;
-      }
-
-      int newX;
-      int newY;
-      try {
-        newX = Integer.parseInt(previewSize.substring(0, dimPosition));
-        newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
-      } catch (NumberFormatException nfe) {
-        Log.w(TAG, "Bad preview-size: " + previewSize);
-        continue;
-      }
-
-      int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
-      if (newDiff == 0) {
-        bestX = newX;
-        bestY = newY;
-        break;
-      } else if (newDiff < diff) {
-        bestX = newX;
-        bestY = newY;
-        diff = newDiff;
-      }
-
-    }
-
-    if (bestX > 0 && bestY > 0) {
-      return new Point(bestX, bestY);
-    }
-    return null;
-  }
-
-  private static int findBestMotZoomValue(String stringValues, int tenDesiredZoom) {
-    int tenBestValue = 0;
-    for (String stringValue : COMMA_PATTERN.split(stringValues)) {
-      stringValue = stringValue.trim();
-      double value;
-      try {
-        value = Double.parseDouble(stringValue);
-      } catch (NumberFormatException nfe) {
-        return tenDesiredZoom;
-      }
-      int tenValue = (int) (10.0 * value);
-      if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
-        tenBestValue = tenValue;
-      }
-    }
-    return tenBestValue;
-  }
-
-  private void setFlash(Camera.Parameters parameters) {
-    // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
-    parameters.set("flash-value", 2);
-    // This is the standard setting to turn the flash off that all devices should honor.
-    parameters.set("flash-mode", "off");
-  }
-
-  private void setZoom(Camera.Parameters parameters) {
-
-    String zoomSupportedString = parameters.get("zoom-supported");
-    if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
-      return;
-    }
-
-    int tenDesiredZoom = TEN_DESIRED_ZOOM;
-
-    String maxZoomString = parameters.get("max-zoom");
-    if (maxZoomString != null) {
-      try {
-        int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
-        if (tenDesiredZoom > tenMaxZoom) {
-          tenDesiredZoom = tenMaxZoom;
-        }
-      } catch (NumberFormatException nfe) {
-        Log.w(TAG, "Bad max-zoom: " + maxZoomString);
-      }
-    }
-
-    String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
-    if (takingPictureZoomMaxString != null) {
-      try {
-        int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
-        if (tenDesiredZoom > tenMaxZoom) {
-          tenDesiredZoom = tenMaxZoom;
-        }
-      } catch (NumberFormatException nfe) {
-        Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
-      }
-    }
-
-    String motZoomValuesString = parameters.get("mot-zoom-values");
-    if (motZoomValuesString != null) {
-      tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
-    }
-
-    String motZoomStepString = parameters.get("mot-zoom-step");
-    if (motZoomStepString != null) {
-      try {
-        double motZoomStep = Double.parseDouble(motZoomStepString.trim());
-        int tenZoomStep = (int) (10.0 * motZoomStep);
-        if (tenZoomStep > 1) {
-          tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
-        }
-      } catch (NumberFormatException nfe) {
-        // continue
-      }
-    }
-
-    // Set zoom. This helps encourage the user to pull back.
-    // Some devices like the Behold have a zoom parameter
-    if (maxZoomString != null || motZoomValuesString != null) {
-      parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
-    }
-
-    // Most devices, like the Hero, appear to expose this zoom parameter.
-    // It takes on values like "27" which appears to mean 2.7x zoom
-    if (takingPictureZoomMaxString != null) {    
-      parameters.set("taking-picture-zoom", tenDesiredZoom);
-    }
-  }
-
-  /*
-  private void setSharpness(Camera.Parameters parameters) {
-
-    int desiredSharpness = DESIRED_SHARPNESS;
-
-    String maxSharpnessString = parameters.get("sharpness-max");
-    if (maxSharpnessString != null) {
-      try {
-        int maxSharpness = Integer.parseInt(maxSharpnessString);
-        if (desiredSharpness > maxSharpness) {
-          desiredSharpness = maxSharpness;
-        }
-      } catch (NumberFormatException nfe) {
-        Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
-      }
-    }
-
-    parameters.set("sharpness", desiredSharpness);
-  }
-   */
-}
diff --git a/android/src/com/google/zxing/client/android/CaptureActivity.java b/android/src/com/google/zxing/client/android/CaptureActivity.java
index ba5ca28..9ad0093 100755
--- a/android/src/com/google/zxing/client/android/CaptureActivity.java
+++ b/android/src/com/google/zxing/client/android/CaptureActivity.java
@@ -19,6 +19,7 @@ package com.google.zxing.client.android;
 import com.google.zxing.BarcodeFormat;
 import com.google.zxing.Result;
 import com.google.zxing.ResultPoint;
+import com.google.zxing.client.android.camera.CameraManager;
 import com.google.zxing.client.android.history.HistoryManager;
 import com.google.zxing.client.android.result.ResultButtonListener;
 import com.google.zxing.client.android.result.ResultHandler;
diff --git a/android/src/com/google/zxing/client/android/CaptureActivityHandler.java b/android/src/com/google/zxing/client/android/CaptureActivityHandler.java
index 362881c..a0d29e6 100755
--- a/android/src/com/google/zxing/client/android/CaptureActivityHandler.java
+++ b/android/src/com/google/zxing/client/android/CaptureActivityHandler.java
@@ -18,6 +18,7 @@ package com.google.zxing.client.android;
 
 import com.google.zxing.BarcodeFormat;
 import com.google.zxing.Result;
+import com.google.zxing.client.android.camera.CameraManager;
 
 import android.app.Activity;
 import android.content.Intent;
@@ -35,6 +36,7 @@ import java.util.Vector;
  * @author dswitkin at google.com (Daniel Switkin)
  */
 public final class CaptureActivityHandler extends Handler {
+
   private final CaptureActivity activity;
   private final DecodeThread decodeThread;
   private State state;
@@ -122,4 +124,5 @@ public final class CaptureActivityHandler extends Handler {
       activity.drawViewfinder();
     }
   }
+
 }
diff --git a/android/src/com/google/zxing/client/android/DecodeThread.java b/android/src/com/google/zxing/client/android/DecodeThread.java
index dce7d14..7a407b1 100755
--- a/android/src/com/google/zxing/client/android/DecodeThread.java
+++ b/android/src/com/google/zxing/client/android/DecodeThread.java
@@ -23,6 +23,7 @@ import com.google.zxing.MultiFormatReader;
 import com.google.zxing.ReaderException;
 import com.google.zxing.Result;
 import com.google.zxing.ResultPointCallback;
+import com.google.zxing.client.android.camera.CameraManager;
 import com.google.zxing.common.HybridBinarizer;
 
 import android.content.SharedPreferences;
@@ -141,4 +142,5 @@ final class DecodeThread extends Thread {
       message.sendToTarget();
     }
   }
+
 }
diff --git a/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java b/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java
index 549dfd5..31cf7d9 100644
--- a/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java
+++ b/android/src/com/google/zxing/client/android/PlanarYUVLuminanceSource.java
@@ -37,7 +37,7 @@ public final class PlanarYUVLuminanceSource extends LuminanceSource {
   private final int left;
   private final int top;
 
-  PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
+  public PlanarYUVLuminanceSource(byte[] yuvData, int dataWidth, int dataHeight, int left, int top,
       int width, int height) {
     super(width, height);
 
diff --git a/android/src/com/google/zxing/client/android/PreferencesActivity.java b/android/src/com/google/zxing/client/android/PreferencesActivity.java
index 090953a..d34e7ff 100755
--- a/android/src/com/google/zxing/client/android/PreferencesActivity.java
+++ b/android/src/com/google/zxing/client/android/PreferencesActivity.java
@@ -31,16 +31,16 @@ import android.preference.PreferenceScreen;
 public final class PreferencesActivity extends PreferenceActivity
     implements OnSharedPreferenceChangeListener {
 
-  static final String KEY_DECODE_1D = "preferences_decode_1D";
-  static final String KEY_DECODE_QR = "preferences_decode_QR";
+  public static final String KEY_DECODE_1D = "preferences_decode_1D";
+  public static final String KEY_DECODE_QR = "preferences_decode_QR";
   public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
 
-  static final String KEY_PLAY_BEEP = "preferences_play_beep";
-  static final String KEY_VIBRATE = "preferences_vibrate";
-  static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
-  static final String KEY_FRONT_LIGHT = "preferences_front_light";
+  public static final String KEY_PLAY_BEEP = "preferences_play_beep";
+  public static final String KEY_VIBRATE = "preferences_vibrate";
+  public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
+  public static final String KEY_FRONT_LIGHT = "preferences_front_light";
 
-  static final String KEY_HELP_VERSION_SHOWN = "preferences_help_version_shown";
+  public static final String KEY_HELP_VERSION_SHOWN = "preferences_help_version_shown";
   public static final String KEY_NOT_OUR_RESULTS_SHOWN = "preferences_not_out_results_shown";
 
   private CheckBoxPreference decode1D;
diff --git a/android/src/com/google/zxing/client/android/ViewfinderView.java b/android/src/com/google/zxing/client/android/ViewfinderView.java
index b972077..8696062 100755
--- a/android/src/com/google/zxing/client/android/ViewfinderView.java
+++ b/android/src/com/google/zxing/client/android/ViewfinderView.java
@@ -16,6 +16,9 @@
 
 package com.google.zxing.client.android;
 
+import com.google.zxing.ResultPoint;
+import com.google.zxing.client.android.camera.CameraManager;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
@@ -24,7 +27,6 @@ import android.graphics.Paint;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.view.View;
-import com.google.zxing.ResultPoint;
 
 import java.util.Collection;
 import java.util.HashSet;
@@ -36,6 +38,7 @@ import java.util.HashSet;
  * @author dswitkin at google.com (Daniel Switkin)
  */
 public final class ViewfinderView extends View {
+
   private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
   private static final long ANIMATION_DELAY = 100L;
   private static final int OPAQUE = 0xFF;
diff --git a/android/src/com/google/zxing/client/android/camera/AutoFocusCallback.java b/android/src/com/google/zxing/client/android/camera/AutoFocusCallback.java
new file mode 100644
index 0000000..fb3dd04
--- /dev/null
+++ b/android/src/com/google/zxing/client/android/camera/AutoFocusCallback.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 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.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+
+final class AutoFocusCallback implements Camera.AutoFocusCallback {
+
+  private static final long AUTOFOCUS_INTERVAL_MS = 1500L;
+
+  private final CameraConfigurationManager configManager;
+  private boolean reinitCamera;
+  private Handler autoFocusHandler;
+  private int autoFocusMessage;
+
+  AutoFocusCallback(CameraConfigurationManager configManager) {
+    this.configManager = configManager;
+  }
+
+  void setHandler(Handler autoFocusHandler, int autoFocusMessage) {
+    this.autoFocusHandler = autoFocusHandler;
+    this.autoFocusMessage = autoFocusMessage;
+  }
+
+  public void onAutoFocus(boolean success, Camera camera) {
+    if (autoFocusHandler != null) {
+      Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
+      // Simulate continuous autofocus by sending a focus request every
+      // AUTOFOCUS_INTERVAL_MS milliseconds.
+      autoFocusHandler.sendMessageDelayed(message, AUTOFOCUS_INTERVAL_MS);
+      autoFocusHandler = null;
+      if (!reinitCamera) {
+        reinitCamera = true;
+        configManager.setDesiredCameraParameters(camera);
+      }
+    }
+  }
+
+}
diff --git a/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
new file mode 100644
index 0000000..e70b1a3
--- /dev/null
+++ b/android/src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2010 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 java.util.regex.Pattern;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+final class CameraConfigurationManager {
+
+  private static final String TAG = CameraConfigurationManager.class.getSimpleName();
+
+  private static final int TEN_DESIRED_ZOOM = 27;
+  private static final int DESIRED_SHARPNESS = 30;
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  private final Context context;
+  private Point screenResolution;
+  private Point cameraResolution;
+  private int previewFormat;
+  private String previewFormatString;
+
+  CameraConfigurationManager(Context context) {
+    this.context = context;
+  }
+
+  /**
+   * Reads, one time, values from the camera that are needed by the app.
+   */
+  void initFromCameraParameters(Camera camera) {
+    Camera.Parameters parameters = camera.getParameters();
+    previewFormat = parameters.getPreviewFormat();
+    previewFormatString = parameters.get("preview-format");
+    Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
+    screenResolution = getScreenResolution();
+    cameraResolution = getCameraResolution(parameters);
+  }
+
+  /**
+   * Sets the camera up to take preview images which are used for both preview and decoding.
+   * We detect the preview format here so that buildLuminanceSource() can build an appropriate
+   * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
+   * and the planar Y can be used for barcode scanning without a copy in some cases.
+   */
+  void setDesiredCameraParameters(Camera camera) {
+    Camera.Parameters parameters = camera.getParameters();
+    Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
+    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+    setFlash(parameters);
+    setZoom(parameters);
+    //setSharpness(parameters);
+    camera.setParameters(parameters);
+  }
+
+  Point getCameraResolution() {
+    return cameraResolution;
+  }
+
+  int getPreviewFormat() {
+    return previewFormat;
+  }
+
+  String getPreviewFormatString() {
+    return previewFormatString;
+  }
+
+  private Point getScreenResolution() {
+    WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+    Display display = manager.getDefaultDisplay();
+    return new Point(display.getWidth(), display.getHeight());
+  }
+
+  private Point getCameraResolution(Camera.Parameters parameters) {
+
+    String previewSizeValueString = parameters.get("preview-size-values");
+    // saw this on Xperia
+    if (previewSizeValueString == null) {
+      previewSizeValueString = parameters.get("preview-size-value");
+    }
+
+    Point cameraResolution = null;
+
+    if (previewSizeValueString != null) {
+      Log.v(TAG, "preview-size parameter: " + previewSizeValueString);
+      cameraResolution = findBestPreviewSizeValue(previewSizeValueString, screenResolution);
+    }
+
+    if (cameraResolution == null) {
+      // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
+      cameraResolution = new Point(
+          (screenResolution.x >> 3) << 3,
+          (screenResolution.y >> 3) << 3);
+    }
+
+    return cameraResolution;
+  }
+
+  private static Point findBestPreviewSizeValue(String previewSizeValueString, Point screenResolution) {
+    int bestX = 0;
+    int bestY = 0;
+    int diff = Integer.MAX_VALUE;
+    for (String previewSize : COMMA_PATTERN.split(previewSizeValueString)) {
+
+      previewSize = previewSize.trim();
+      int dimPosition = previewSize.indexOf('x');
+      if (dimPosition < 0) {
+        Log.w(TAG, "Bad preview-size: " + previewSize);
+        continue;
+      }
+
+      int newX;
+      int newY;
+      try {
+        newX = Integer.parseInt(previewSize.substring(0, dimPosition));
+        newY = Integer.parseInt(previewSize.substring(dimPosition + 1));
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad preview-size: " + previewSize);
+        continue;
+      }
+
+      int newDiff = Math.abs(newX - screenResolution.x) + Math.abs(newY - screenResolution.y);
+      if (newDiff == 0) {
+        bestX = newX;
+        bestY = newY;
+        break;
+      } else if (newDiff < diff) {
+        bestX = newX;
+        bestY = newY;
+        diff = newDiff;
+      }
+
+    }
+
+    if (bestX > 0 && bestY > 0) {
+      return new Point(bestX, bestY);
+    }
+    return null;
+  }
+
+  private static int findBestMotZoomValue(String stringValues, int tenDesiredZoom) {
+    int tenBestValue = 0;
+    for (String stringValue : COMMA_PATTERN.split(stringValues)) {
+      stringValue = stringValue.trim();
+      double value;
+      try {
+        value = Double.parseDouble(stringValue);
+      } catch (NumberFormatException nfe) {
+        return tenDesiredZoom;
+      }
+      int tenValue = (int) (10.0 * value);
+      if (Math.abs(tenDesiredZoom - value) < Math.abs(tenDesiredZoom - tenBestValue)) {
+        tenBestValue = tenValue;
+      }
+    }
+    return tenBestValue;
+  }
+
+  private void setFlash(Camera.Parameters parameters) {
+    // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
+    parameters.set("flash-value", 2);
+    // This is the standard setting to turn the flash off that all devices should honor.
+    parameters.set("flash-mode", "off");
+  }
+
+  private void setZoom(Camera.Parameters parameters) {
+
+    String zoomSupportedString = parameters.get("zoom-supported");
+    if (zoomSupportedString != null && !Boolean.parseBoolean(zoomSupportedString)) {
+      return;
+    }
+
+    int tenDesiredZoom = TEN_DESIRED_ZOOM;
+
+    String maxZoomString = parameters.get("max-zoom");
+    if (maxZoomString != null) {
+      try {
+        int tenMaxZoom = (int) (10.0 * Double.parseDouble(maxZoomString));
+        if (tenDesiredZoom > tenMaxZoom) {
+          tenDesiredZoom = tenMaxZoom;
+        }
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad max-zoom: " + maxZoomString);
+      }
+    }
+
+    String takingPictureZoomMaxString = parameters.get("taking-picture-zoom-max");
+    if (takingPictureZoomMaxString != null) {
+      try {
+        int tenMaxZoom = Integer.parseInt(takingPictureZoomMaxString);
+        if (tenDesiredZoom > tenMaxZoom) {
+          tenDesiredZoom = tenMaxZoom;
+        }
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad taking-picture-zoom-max: " + takingPictureZoomMaxString);
+      }
+    }
+
+    String motZoomValuesString = parameters.get("mot-zoom-values");
+    if (motZoomValuesString != null) {
+      tenDesiredZoom = findBestMotZoomValue(motZoomValuesString, tenDesiredZoom);
+    }
+
+    String motZoomStepString = parameters.get("mot-zoom-step");
+    if (motZoomStepString != null) {
+      try {
+        double motZoomStep = Double.parseDouble(motZoomStepString.trim());
+        int tenZoomStep = (int) (10.0 * motZoomStep);
+        if (tenZoomStep > 1) {
+          tenDesiredZoom -= tenDesiredZoom % tenZoomStep;
+        }
+      } catch (NumberFormatException nfe) {
+        // continue
+      }
+    }
+
+    // Set zoom. This helps encourage the user to pull back.
+    // Some devices like the Behold have a zoom parameter
+    if (maxZoomString != null || motZoomValuesString != null) {
+      parameters.set("zoom", String.valueOf(tenDesiredZoom / 10.0));
+    }
+
+    // Most devices, like the Hero, appear to expose this zoom parameter.
+    // It takes on values like "27" which appears to mean 2.7x zoom
+    if (takingPictureZoomMaxString != null) {
+      parameters.set("taking-picture-zoom", tenDesiredZoom);
+    }
+  }
+
+  /*
+  private void setSharpness(Camera.Parameters parameters) {
+
+    int desiredSharpness = DESIRED_SHARPNESS;
+
+    String maxSharpnessString = parameters.get("sharpness-max");
+    if (maxSharpnessString != null) {
+      try {
+        int maxSharpness = Integer.parseInt(maxSharpnessString);
+        if (desiredSharpness > maxSharpness) {
+          desiredSharpness = maxSharpness;
+        }
+      } catch (NumberFormatException nfe) {
+        Log.w(TAG, "Bad sharpness-max: " + maxSharpnessString);
+      }
+    }
+
+    parameters.set("sharpness", desiredSharpness);
+  }
+   */
+}
diff --git a/androidtest/src/com/google/zxing/client/androidtest/CameraManager.java b/android/src/com/google/zxing/client/android/camera/CameraManager.java
similarity index 55%
copy from androidtest/src/com/google/zxing/client/androidtest/CameraManager.java
copy to android/src/com/google/zxing/client/android/camera/CameraManager.java
index 0df0f7a..5f67471 100755
--- a/androidtest/src/com/google/zxing/client/androidtest/CameraManager.java
+++ b/android/src/com/google/zxing/client/android/camera/CameraManager.java
@@ -14,19 +14,23 @@
  * limitations under the License.
  */
 
-package com.google.zxing.client.androidtest;
+package com.google.zxing.client.android.camera;
+
+import com.google.zxing.ResultPoint;
 
 import android.content.Context;
+import android.content.SharedPreferences;
+import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.Camera;
 import android.os.Build;
 import android.os.Handler;
-import android.os.Message;
+import android.preference.PreferenceManager;
 import android.util.Log;
-import android.view.Display;
 import android.view.SurfaceHolder;
-import android.view.WindowManager;
+import com.google.zxing.client.android.PlanarYUVLuminanceSource;
+import com.google.zxing.client.android.PreferencesActivity;
 
 import java.io.IOException;
 
@@ -37,61 +41,31 @@ import java.io.IOException;
  *
  * @author dswitkin at google.com (Daniel Switkin)
  */
-final class CameraManager {
-  private static final String TAG = "CameraManager";
+public final class CameraManager {
+
+  private static final String TAG = CameraManager.class.getSimpleName();
+
   private static final int MIN_FRAME_WIDTH = 240;
   private static final int MIN_FRAME_HEIGHT = 240;
   private static final int MAX_FRAME_WIDTH = 480;
   private static final int MAX_FRAME_HEIGHT = 360;
 
   private static CameraManager cameraManager;
-  private Camera camera;
+
   private final Context context;
-  private Point screenResolution;
-  private Point cameraResolution;
+  private final CameraConfigurationManager configManager;
+  private Camera camera;
   private Rect framingRect;
-  private Handler previewHandler;
-  private int previewMessage;
-  private Handler autoFocusHandler;
-  private int autoFocusMessage;
   private boolean initialized;
   private boolean previewing;
-  private int previewFormat;
-  private String previewFormatString;
-  private boolean useOneShotPreviewCallback;
-
+  private final boolean useOneShotPreviewCallback;
   /**
    * Preview frames are delivered here, which we pass on to the registered handler. Make sure to
    * clear the handler so it will only receive one message.
    */
-  private final Camera.PreviewCallback previewCallback = new Camera.PreviewCallback() {
-    public void onPreviewFrame(byte[] data, Camera camera) {
-      if (!useOneShotPreviewCallback) {
-        camera.setPreviewCallback(null);
-      }
-      if (previewHandler != null) {
-        Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
-            cameraResolution.y, data);
-        message.sendToTarget();
-        previewHandler = null;
-      }
-    }
-  };
-
-  /**
-   * Autofocus callbacks arrive here, and are dispatched to the Handler which requested them.
-   */
-  private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
-    public void onAutoFocus(boolean success, Camera camera) {
-      if (autoFocusHandler != null) {
-        Message message = autoFocusHandler.obtainMessage(autoFocusMessage, success);
-        // Barcode Scanner needs to insert a delay here because it does continuous focus,
-        // but this test app does not, so send the message immediately.
-        message.sendToTarget();
-        autoFocusHandler = null;
-      }
-    }
-  };
+  private final PreviewCallback previewCallback;
+  /** Autofocus callbacks arrive here, and are dispatched to the Handler which requested them. */
+  private final AutoFocusCallback autoFocusCallback;
 
   /**
    * Initializes this static object with the Context of the calling Activity.
@@ -114,20 +88,18 @@ final class CameraManager {
   }
 
   private CameraManager(Context context) {
+
     this.context = context;
-    camera = null;
-    initialized = false;
-    previewing = false;
+    this.configManager = new CameraConfigurationManager(context);
 
     // Camera.setOneShotPreviewCallback() has a race condition in Cupcake, so we use the older
     // Camera.setPreviewCallback() on 1.5 and earlier. For Donut and later, we need to use
     // the more efficient one shot callback, as the older one can swamp the system and cause it
     // to run out of memory. We can't use SDK_INT because it was introduced in the Donut SDK.
-    if (Integer.parseInt(Build.VERSION.SDK) <= Build.VERSION_CODES.CUPCAKE) {
-      useOneShotPreviewCallback = false;
-    } else {
-      useOneShotPreviewCallback = true;
-    }
+    useOneShotPreviewCallback = Integer.parseInt(Build.VERSION.SDK) > Build.VERSION_CODES.CUPCAKE;
+
+    previewCallback = new PreviewCallback(configManager, useOneShotPreviewCallback);
+    autoFocusCallback = new AutoFocusCallback(configManager);
   }
 
   /**
@@ -136,23 +108,25 @@ final class CameraManager {
    * @param holder The surface object which the camera will draw preview frames into.
    * @throws IOException Indicates the camera driver failed to open.
    */
-  public String openDriver(SurfaceHolder holder, boolean getParameters) throws IOException {
-    String result = null;
+  public void openDriver(SurfaceHolder holder) throws IOException {
     if (camera == null) {
       camera = Camera.open();
+      if (camera == null) {
+        throw new IOException();
+      }
       camera.setPreviewDisplay(holder);
 
       if (!initialized) {
         initialized = true;
-        getScreenResolution();
+        configManager.initFromCameraParameters(camera);
       }
+      configManager.setDesiredCameraParameters(camera);
 
-      if (getParameters) {
-        result = collectCameraParameters();
+      SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+      if (prefs.getBoolean(PreferencesActivity.KEY_FRONT_LIGHT, false)) {
+        FlashlightManager.enableFlashlight();
       }
-      setCameraParameters();
     }
-    return result;
   }
 
   /**
@@ -160,6 +134,7 @@ final class CameraManager {
    */
   public void closeDriver() {
     if (camera != null) {
+      FlashlightManager.disableFlashlight();
       camera.release();
       camera = null;
     }
@@ -184,8 +159,8 @@ final class CameraManager {
         camera.setPreviewCallback(null);
       }
       camera.stopPreview();
-      previewHandler = null;
-      autoFocusHandler = null;
+      previewCallback.setHandler(null, 0);
+      autoFocusCallback.setHandler(null, 0);
       previewing = false;
     }
   }
@@ -200,8 +175,7 @@ final class CameraManager {
    */
   public void requestPreviewFrame(Handler handler, int message) {
     if (camera != null && previewing) {
-      previewHandler = handler;
-      previewMessage = message;
+      previewCallback.setHandler(handler, message);
       if (useOneShotPreviewCallback) {
         camera.setOneShotPreviewCallback(previewCallback);
       } else {
@@ -218,8 +192,7 @@ final class CameraManager {
    */
   public void requestAutoFocus(Handler handler, int message) {
     if (camera != null && previewing) {
-      autoFocusHandler = handler;
-      autoFocusMessage = message;
+      autoFocusCallback.setHandler(handler, message);
       camera.autoFocus(autoFocusCallback);
     }
   }
@@ -232,6 +205,7 @@ final class CameraManager {
    * @return The rectangle to draw on screen in window coordinates.
    */
   public Rect getFramingRect() {
+    Point cameraResolution = configManager.getCameraResolution();
     if (framingRect == null) {
       if (camera == null) {
         return null;
@@ -257,56 +231,56 @@ final class CameraManager {
   }
 
   /**
-   * Sets the camera up to take preview images which are used for both preview and decoding.
-   * We detect the preview format here so that buildLuminanceSource() can build an appropriate
-   * LuminanceSource subclass. In the future we may want to force YUV420SP as it's the smallest,
-   * and the planar Y can be used for barcode scanning without a copy in some cases.
+   * Converts the result points from still resolution coordinates to screen coordinates.
+   *
+   * @param points The points returned by the Reader subclass through Result.getResultPoints().
+   * @return An array of Points scaled to the size of the framing rect and offset appropriately
+   *         so they can be drawn in screen coordinates.
    */
-  private void setCameraParameters() {
-    Camera.Parameters parameters = camera.getParameters();
-    Camera.Size size = parameters.getPreviewSize();
-    Log.v(TAG, "Default preview size: " + size.width + ", " + size.height);
-    previewFormat = parameters.getPreviewFormat();
-    previewFormatString = parameters.get("preview-format");
-    Log.v(TAG, "Default preview format: " + previewFormat + '/' + previewFormatString);
-
-    // Ensure that the camera resolution is a multiple of 8, as the screen may not be.
-    // TODO: A better solution would be to request the supported preview resolutions
-    // and pick the best match, but this parameter is not standardized in Cupcake.
-    cameraResolution = new Point();
-    cameraResolution.x = (screenResolution.x >> 3) << 3;
-    cameraResolution.y = (screenResolution.y >> 3) << 3;
-    Log.v(TAG, "Setting preview size: " + cameraResolution.x + ", " + cameraResolution.y);
-    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
-
-    // FIXME: This is a hack to turn the flash off on the Samsung Galaxy.
-    parameters.set("flash-value", 2);
-
-    // This is the standard setting to turn the flash off that all devices should honor.
-    parameters.set("flash-mode", "off");
-
-    camera.setParameters(parameters);
-  }
-
-  private String collectCameraParameters() {
-    Camera.Parameters parameters = camera.getParameters();
-    String[] params = parameters.flatten().split(";");
-    StringBuffer result = new StringBuffer();
-    result.append("Default camera parameters:");
-    for (String param : params) {
-      result.append("\n  ");
-      result.append(param);
+  public Point[] convertResultPoints(ResultPoint[] points) {
+    Rect frame = getFramingRect();
+    int count = points.length;
+    Point[] output = new Point[count];
+    for (int x = 0; x < count; x++) {
+      output[x] = new Point();
+      output[x].x = frame.left + (int) (points[x].getX() + 0.5f);
+      output[x].y = frame.top + (int) (points[x].getY() + 0.5f);
     }
-    result.append('\n');
-    return result.toString();
+    return output;
   }
 
-  private Point getScreenResolution() {
-    if (screenResolution == null) {
-      WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-      Display display = manager.getDefaultDisplay();
-      screenResolution = new Point(display.getWidth(), display.getHeight());
+  /**
+   * A factory method to build the appropriate LuminanceSource object based on the format
+   * of the preview buffers, as described by Camera.Parameters.
+   *
+   * @param data A preview frame.
+   * @param width The width of the image.
+   * @param height The height of the image.
+   * @return A PlanarYUVLuminanceSource instance.
+   */
+  public PlanarYUVLuminanceSource buildLuminanceSource(byte[] data, int width, int height) {
+    Rect rect = getFramingRect();
+    int previewFormat = configManager.getPreviewFormat();
+    String previewFormatString = configManager.getPreviewFormatString();
+    switch (previewFormat) {
+      // This is the standard Android format which all devices are REQUIRED to support.
+      // In theory, it's the only one we should ever care about.
+      case PixelFormat.YCbCr_420_SP:
+      // This format has never been seen in the wild, but is compatible as we only care
+      // about the Y channel, so allow it.
+      case PixelFormat.YCbCr_422_SP:
+        return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+            rect.width(), rect.height());
+      default:
+        // The Samsung Moment incorrectly uses this variant instead of the 'sp' version.
+        // Fortunately, it too has all the Y data up front, so we can read it.
+        if ("yuv420p".equals(previewFormatString)) {
+          return new PlanarYUVLuminanceSource(data, width, height, rect.left, rect.top,
+            rect.width(), rect.height());
+        }
     }
-    return screenResolution;
+    throw new IllegalArgumentException("Unsupported picture format: " +
+        previewFormat + '/' + previewFormatString);
   }
+
 }
diff --git a/android/src/com/google/zxing/client/android/FlashlightManager.java b/android/src/com/google/zxing/client/android/camera/FlashlightManager.java
similarity index 99%
rename from android/src/com/google/zxing/client/android/FlashlightManager.java
rename to android/src/com/google/zxing/client/android/camera/FlashlightManager.java
index 894d751..44459d1 100644
--- a/android/src/com/google/zxing/client/android/FlashlightManager.java
+++ b/android/src/com/google/zxing/client/android/camera/FlashlightManager.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.google.zxing.client.android;
+package com.google.zxing.client.android.camera;
 
 import android.os.IBinder;
 import android.util.Log;
diff --git a/android/src/com/google/zxing/client/android/camera/PreviewCallback.java b/android/src/com/google/zxing/client/android/camera/PreviewCallback.java
new file mode 100644
index 0000000..83377ba
--- /dev/null
+++ b/android/src/com/google/zxing/client/android/camera/PreviewCallback.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 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.hardware.Camera;
+import android.os.Handler;
+import android.os.Message;
+
+final class PreviewCallback implements Camera.PreviewCallback {
+
+  private final CameraConfigurationManager configManager;
+  private final boolean useOneShotPreviewCallback;
+  private Handler previewHandler;
+  private int previewMessage;
+
+  PreviewCallback(CameraConfigurationManager configManager, boolean useOneShotPreviewCallback) {
+    this.configManager = configManager;
+    this.useOneShotPreviewCallback = useOneShotPreviewCallback;
+  }
+
+  void setHandler(Handler previewHandler, int previewMessage) {
+    this.previewHandler = previewHandler;
+    this.previewMessage = previewMessage;
+  }
+
+  public void onPreviewFrame(byte[] data, Camera camera) {
+    Point cameraResolution = configManager.getCameraResolution();
+    if (!useOneShotPreviewCallback) {
+      camera.setPreviewCallback(null);
+    }
+    if (previewHandler != null) {
+      Message message = previewHandler.obtainMessage(previewMessage, cameraResolution.x,
+          cameraResolution.y, data);
+      message.sendToTarget();
+      previewHandler = null;
+    }
+  }
+
+}

-- 
Multi-format 1D/2D barcode image processing library



More information about the Pkg-google-commits mailing list