liuyuqi-dellpc 3 years ago
commit
2c149bea4b
50 changed files with 4094 additions and 0 deletions
  1. 9 0
      .classpath
  2. 4 0
      .gitignore
  3. 33 0
      .project
  4. 129 0
      AndroidManifest.xml
  5. BIN
      libs/zxing.jar
  6. 15 0
      project.properties
  7. BIN
      res/drawable-hdpi/ic_launcher.png
  8. BIN
      res/drawable-hdpi/mzw_camera_close.png
  9. BIN
      res/drawable-hdpi/mzw_camera_open.png
  10. BIN
      res/drawable-mdpi/ic_launcher.png
  11. 16 0
      res/layout/activity_main.xml
  12. 53 0
      res/layout/capture.xml
  13. BIN
      res/raw/beep.ogg
  14. 30 0
      res/values/colors.xml
  15. 7 0
      res/values/dimens.xml
  16. 25 0
      res/values/ids.xml
  17. 32 0
      res/values/strings.xml
  18. 20 0
      res/values/styles.xml
  19. 112 0
      src/com/google/zxing/client/android/camera/AutoFocusManager.java
  20. 260 0
      src/com/google/zxing/client/android/camera/CameraConfigurationManager.java
  21. 338 0
      src/com/google/zxing/client/android/camera/CameraManager.java
  22. 56 0
      src/com/google/zxing/client/android/camera/PreviewCallback.java
  23. 34 0
      src/com/google/zxing/client/android/camera/open/DefaultOpenCameraInterface.java
  24. 66 0
      src/com/google/zxing/client/android/camera/open/GingerbreadOpenCameraInterface.java
  25. 29 0
      src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java
  26. 32 0
      src/com/google/zxing/client/android/camera/open/OpenCameraManager.java
  27. 94 0
      src/com/google/zxing/client/android/common/PlatformSupportManager.java
  28. 25 0
      src/com/google/zxing/client/android/common/executor/AsyncTaskExecInterface.java
  29. 28 0
      src/com/google/zxing/client/android/common/executor/AsyncTaskExecManager.java
  30. 32 0
      src/com/google/zxing/client/android/common/executor/DefaultAsyncTaskExecInterface.java
  31. 36 0
      src/com/google/zxing/client/android/common/executor/HoneycombAsyncTaskExecInterface.java
  32. 121 0
      src/com/google/zxing/client/android/decode/BeepManager.java
  33. 155 0
      src/com/google/zxing/client/android/decode/CaptureActivityHandler.java
  34. 100 0
      src/com/google/zxing/client/android/decode/DecodeFormatManager.java
  35. 143 0
      src/com/google/zxing/client/android/decode/DecodeHandler.java
  36. 103 0
      src/com/google/zxing/client/android/decode/DecodeThread.java
  37. 121 0
      src/com/google/zxing/client/android/decode/InactivityTimer.java
  38. 26 0
      src/com/google/zxing/client/android/decode/IntentSource.java
  39. 261 0
      src/com/google/zxing/client/android/decode/Intents.java
  40. 37 0
      src/com/google/zxing/client/android/decode/ViewfinderResultPointCallback.java
  41. 163 0
      src/com/google/zxing/client/android/decode/ViewfinderView.java
  42. 114 0
      src/com/google/zxing/client/android/result/Contents.java
  43. 40 0
      src/com/google/zxing/client/android/result/DefaultResultHandler.java
  44. 170 0
      src/com/google/zxing/client/android/result/LocaleManager.java
  45. 41 0
      src/com/google/zxing/client/android/result/ResultButtonListener.java
  46. 488 0
      src/com/google/zxing/client/android/result/ResultHandler.java
  47. 55 0
      src/com/google/zxing/client/android/result/ResultHandlerFactory.java
  48. 85 0
      src/com/google/zxing/client/android/result/URIResultHandler.java
  49. 332 0
      src/com/libs/zxing/CaptureActivity.java
  50. 24 0
      src/com/libs/zxing/Config.java

+ 9 - 0
.classpath

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
+	<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
+	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="src" path="gen"/>
+	<classpathentry kind="output" path="bin/classes"/>
+</classpath>

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+/.settings
+/bin
+/gen
+.svn

+ 33 - 0
.project

@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>ZXingCodeLibs</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>com.android.ide.eclipse.adt.ApkBuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>

+ 129 - 0
AndroidManifest.xml

@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.libs.zxing"
+    android:installLocation="auto"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.FLASHLIGHT" />
+    <uses-permission android:name="android.permission.READ_CONTACTS" />
+    <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+
+    <uses-sdk
+        android:minSdkVersion="7"
+        android:targetSdkVersion="10" />
+
+    <!-- Don't require camera, as this requires a rear camera. This allows it to work on the Nexus 7 -->
+    <uses-feature
+        android:name="android.hardware.camera"
+        android:required="false" />
+    <uses-feature
+        android:name="android.hardware.camera.front"
+        android:required="false" />
+    <uses-feature
+        android:name="android.hardware.camera.autofocus"
+        android:required="false" />
+    <uses-feature
+        android:name="android.hardware.camera.flash"
+        android:required="false" />
+    <uses-feature android:name="android.hardware.screen.landscape" />
+    <uses-feature
+        android:name="android.hardware.wifi"
+        android:required="false" />
+    <uses-feature
+        android:name="android.hardware.touchscreen"
+        android:required="false" />
+
+    <!-- Donut-specific flags which allow us to run on any dpi screens. -->
+    <supports-screens
+        android:anyDensity="true"
+        android:largeScreens="true"
+        android:normalScreens="true"
+        android:smallScreens="true"
+        android:xlargeScreens="true" />
+
+    <uses-sdk
+        android:minSdkVersion="9"
+        android:targetSdkVersion="17" />
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:theme="@style/AppTheme" >
+        <activity
+            android:name=".CaptureActivity"
+            android:clearTaskOnLaunch="true"
+            android:configChanges="orientation|keyboardHidden"
+            android:screenOrientation="portrait"
+            android:stateNotNeeded="true"
+            android:theme="@android:style/Theme.NoTitleBar"
+            android:windowSoftInputMode="stateAlwaysHidden" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="com.google.zxing.client.android.SCAN" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <!-- Allow web apps to launch Barcode Scanner by linking to http://zxing.appspot.com/scan. -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data
+                    android:host="zxing.appspot.com"
+                    android:path="/scan"
+                    android:scheme="http" />
+            </intent-filter>
+            <!-- We also support a Google Product Search URL. -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data
+                    android:host="www.google.com"
+                    android:path="/m/products/scan"
+                    android:scheme="http" />
+            </intent-filter>
+            <!-- And the UK version. -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data
+                    android:host="www.google.co.uk"
+                    android:path="/m/products/scan"
+                    android:scheme="http" />
+            </intent-filter>
+            <!-- Support zxing://scan/?... like iPhone app -->
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data
+                    android:host="scan"
+                    android:path="/"
+                    android:scheme="zxing" />
+            </intent-filter>
+        </activity>
+    </application>
+
+</manifest>

BIN
libs/zxing.jar


+ 15 - 0
project.properties

@@ -0,0 +1,15 @@
+# This file is automatically generated by Android Tools.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file must be checked in Version Control Systems.
+#
+# To customize properties used by the Ant build system edit
+# "ant.properties", and override values to adapt the script to your
+# project structure.
+#
+# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
+#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
+
+# Project target.
+target=android-21
+android.library=false

BIN
res/drawable-hdpi/ic_launcher.png


BIN
res/drawable-hdpi/mzw_camera_close.png


BIN
res/drawable-hdpi/mzw_camera_open.png


BIN
res/drawable-mdpi/ic_launcher.png


+ 16 - 0
res/layout/activity_main.xml

@@ -0,0 +1,16 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingBottom="@dimen/activity_vertical_margin"
+    android:paddingLeft="@dimen/activity_horizontal_margin"
+    android:paddingRight="@dimen/activity_horizontal_margin"
+    android:paddingTop="@dimen/activity_vertical_margin"
+    tools:context=".CaptureActivity" >
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/hello_world" />
+	
+</RelativeLayout>

+ 53 - 0
res/layout/capture.xml

@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" >
+
+    <SurfaceView
+        android:id="@+id/preview_view"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent" />
+
+    <com.google.zxing.client.android.decode.ViewfinderView
+        android:id="@+id/viewfinder_view"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_centerInParent="true" />
+    <!--
+    <Button
+        android:id="@+id/button_openClick"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top|right"
+        android:text="@string/openorclose_light" />
+    -->
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentRight="true"
+        android:layout_marginRight="20dip"
+        android:layout_marginTop="20dip"
+        android:orientation="horizontal" >
+
+        <ImageView
+            android:id="@+id/button_openorcloseClick"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/mzw_camera_open" />
+
+    </LinearLayout>
+
+    <TextView
+        android:id="@+id/barcode_notice"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentBottom="true"
+        android:layout_marginBottom="80dip"
+        android:layout_marginLeft="60dip"
+        android:layout_marginRight="60dip"
+        android:text="@string/qrcode_notice"
+        android:textColor="#ffffff"
+        android:textSize="14.0sp" />
+
+</RelativeLayout>

BIN
res/raw/beep.ogg


+ 30 - 0
res/values/colors.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<resources>
+  <color name="contents_text">#ff000000</color>
+  <color name="encode_view">#ffffffff</color>
+  <color name="possible_result_points">#c0ffbd21</color> <!-- Android standard ICS color -->
+  <color name="result_minor_text">#ffc0c0c0</color>
+  <color name="result_points">#c099cc00</color> <!-- Android standard ICS color -->
+  <color name="result_text">#ffffffff</color>
+  <color name="result_view">#b0000000</color>
+  <color name="status_text">#ffffffff</color>
+  <color name="transparent">#00000000</color>
+  <color name="viewfinder_laser">#ffcc0000</color> <!-- Android standard ICS color -->
+  <color name="viewfinder_mask">#60000000</color>
+  <color name="viewfinder_frame">#ff88ae00</color>
+</resources>

+ 7 - 0
res/values/dimens.xml

@@ -0,0 +1,7 @@
+<resources>
+
+    <!-- Default screen margins, per the Android Design guidelines. -->
+    <dimen name="activity_horizontal_margin">16dp</dimen>
+    <dimen name="activity_vertical_margin">16dp</dimen>
+
+</resources>

+ 25 - 0
res/values/ids.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+ -->
+<resources>
+  <item type="id" name="decode"/>
+  <item type="id" name="decode_failed"/>
+  <item type="id" name="decode_succeeded"/>
+  <item type="id" name="launch_product_query"/>
+  <item type="id" name="quit"/>
+  <item type="id" name="restart_preview"/>
+  <item type="id" name="return_scan_result"/> 
+</resources>

+ 32 - 0
res/values/strings.xml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <string name="app_name">ZXingCodeLibs</string>
+    <string name="action_settings">Settings</string>
+    <string name="hello_world">Hello world!</string>
+    
+	<string name="camera_open_notice">您已经开启了灯光!</string>
+	<string name="camera_close_notice">您已经关闭了灯光!</string>
+    
+	<string name="camera_problem">抱歉,您相机出现问题。您可能需要重启设备。</string>
+	<string name="framwork_problem">二维码扫描暂时出现问题。您重启再试。</string>
+	<string name="msg_share_subject_line">这是我所扫描条码的内容</string>
+	<string name="msg_default_mms_subject">Hi</string>
+	<string name="msg_intent_failed">抱歉,无法打开所需软件。 条码内容可能无效。</string>
+	 <string name="button_ok">确定</string>
+	 <string name="result_uri">网址路径</string>
+	 
+	 <string name="button_1">浏览器打开</string>
+	 <string name="button_2">内容</string>
+	 <string name="button_3">其它标题</string>
+	 <string name="result_title_default">网址路径</string>
+	 
+	 <string name="qrcode_notice">将二维码图案放在取景框内,即可自动扫描。</string>
+	 <string name="openorclose_light">打开或关闭灯光</string>
+	 <string name="qrcode_empty"></string>
+	 
+	 <string name="qrcode_open">用浏览器打开</string>
+	 
+	 <string name="paramete_error">参数正确,请重新扫描</string>
+	 
+</resources>

+ 20 - 0
res/values/styles.xml

@@ -0,0 +1,20 @@
+<resources>
+
+    <!--
+        Base application theme, dependent on API level. This theme is replaced
+        by AppBaseTheme from res/values-vXX/styles.xml on newer devices.
+    -->
+    <style name="AppBaseTheme" parent="android:Theme.Light">
+        <!--
+            Theme customizations available in newer API levels can go in
+            res/values-vXX/styles.xml, while customizations related to
+            backward-compatibility can go here.
+        -->
+    </style>
+
+    <!-- Application theme. -->
+    <style name="AppTheme" parent="AppBaseTheme">
+        <!-- All customizations that are NOT specific to a particular API-level can go here. -->
+    </style>
+
+</resources>

+ 112 - 0
src/com/google/zxing/client/android/camera/AutoFocusManager.java

@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2012 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.content.Context;
+import android.hardware.Camera;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import com.google.zxing.client.android.common.executor.AsyncTaskExecInterface;
+import com.google.zxing.client.android.common.executor.AsyncTaskExecManager;
+import com.libs.zxing.Config;
+
+final class AutoFocusManager implements Camera.AutoFocusCallback {
+
+  private static final String TAG = AutoFocusManager.class.getSimpleName();
+
+  private static final long AUTO_FOCUS_INTERVAL_MS = 2500L;
+  private static final Collection<String> FOCUS_MODES_CALLING_AF;
+  static {
+    FOCUS_MODES_CALLING_AF = new ArrayList<String>(2);
+    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_AUTO);
+    FOCUS_MODES_CALLING_AF.add(Camera.Parameters.FOCUS_MODE_MACRO);
+  }
+
+  private boolean active;
+  private final boolean useAutoFocus;
+  private final Camera camera;
+  private AutoFocusTask outstandingTask;
+  private final AsyncTaskExecInterface taskExec;
+
+  AutoFocusManager(Context context, Camera camera) {
+    this.camera = camera;
+    taskExec = new AsyncTaskExecManager().build();
+    String currentFocusMode = camera.getParameters().getFocusMode();
+    useAutoFocus =Config.AOTO_FOCUS && FOCUS_MODES_CALLING_AF.contains(currentFocusMode);
+    Log.i(TAG, "Current focus mode '" + currentFocusMode + "'; use auto focus? " + useAutoFocus);
+    start();
+  }
+
+  @Override
+  public synchronized void onAutoFocus(boolean success, Camera theCamera) {
+    if (active) {
+      Log.i(TAG, "onAutoFocus hit");
+      outstandingTask = new AutoFocusTask();
+      taskExec.execute(outstandingTask);
+    }
+  }
+
+  synchronized void start() {
+    if (useAutoFocus) {
+      active = true;
+      try {
+        camera.autoFocus(this);
+      } catch (RuntimeException re) {
+        // Have heard RuntimeException reported in Android 4.0.x+; continue?
+        Log.w(TAG, "Unexpected exception while focusing", re);
+      }
+    }
+  }
+
+  synchronized void stop() {
+    if (useAutoFocus) {
+      try {
+        camera.cancelAutoFocus();
+      } catch (RuntimeException re) {
+        // Have heard RuntimeException reported in Android 4.0.x+; continue?
+        Log.w(TAG, "Unexpected exception while cancelling focusing", re);
+      }
+    }
+    if (outstandingTask != null) {
+      outstandingTask.cancel(true);
+      outstandingTask = null;
+    }
+    active = false;
+  }
+
+  private final class AutoFocusTask extends AsyncTask<Object,Object,Object> {
+    @Override
+    protected Object doInBackground(Object... voids) {
+      try {
+        Thread.sleep(AUTO_FOCUS_INTERVAL_MS);
+      } catch (InterruptedException e) {
+        // continue
+      }
+      synchronized (AutoFocusManager.this) {
+        if (active) {
+          start();
+        }
+      }
+      return null;
+    }
+  }
+
+}

+ 260 - 0
src/com/google/zxing/client/android/camera/CameraConfigurationManager.java

@@ -0,0 +1,260 @@
+/*
+ * 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.content.Context;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.util.Log;
+import android.view.Display;
+import android.view.WindowManager;
+
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import com.libs.zxing.Config;
+
+/**
+ * A class which deals with reading, parsing, and setting the camera parameters which are used to
+ * configure the camera hardware.
+ */
+public 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 = 470 * 320; // normal screen
+  private static final int MAX_PREVIEW_PIXELS = 1280 * 720;
+
+  private final Context context;
+  private Point screenResolution;
+  private Point cameraResolution;
+
+  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();
+      WindowManager manager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE);
+      Display display = manager.getDefaultDisplay();
+      int width = display.getWidth();
+      int height = display.getHeight();
+      if (width < height) {
+           int temp = width;
+           width = height;
+           height = temp;
+      }
+      screenResolution = new Point(height, width);
+      cameraResolution = findBestPreviewSizeValue(parameters, new Point(width, height));
+  }
+
+  void setDesiredCameraParameters(Camera camera, boolean safeMode) {
+    Camera.Parameters parameters = camera.getParameters();
+    
+    if (parameters == null) {
+      Log.w(TAG, "Device error: no camera parameters are available. Proceeding without configuration.");
+      return;
+    }
+
+    Log.i(TAG, "Initial camera parameters: " + parameters.flatten());
+
+    if (safeMode) {
+      Log.w(TAG, "In camera config safe mode -- most settings will not be honored");
+    }
+
+
+    initializeTorch(parameters, safeMode);
+
+    String focusMode = null;
+    if (Config.AOTO_FOCUS) {
+      if (safeMode ||Config.KEY_DISABLE_CONTINUOUS_FOCUS) {
+        focusMode = findSettableValue(parameters.getSupportedFocusModes(),
+                                      Camera.Parameters.FOCUS_MODE_AUTO);
+      } 
+//      else {
+//        focusMode = findSettableValue(parameters.getSupportedFocusModes(),
+//                                      "continuous-picture", // Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE in 4.0+
+//                                      "continuous-video",   // Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO in 4.0+
+//                                      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,
+                                    "edof"); // Camera.Parameters.FOCUS_MODE_EDOF in 2.2+
+      Log.i(TAG, "Initial camera parameters: " + "focus:FOCUS_MODE_MACRO");
+    }
+    if (focusMode != null) {
+      parameters.setFocusMode(focusMode);
+    }
+
+    parameters.setPreviewSize(cameraResolution.x, cameraResolution.y);
+    camera.setDisplayOrientation(90);
+    camera.setParameters(parameters);
+  }
+
+  Point getCameraResolution() {
+    return cameraResolution;
+  }
+
+  Point getScreenResolution() {
+    return screenResolution;
+  }
+
+  public void setTorch(Camera camera, boolean newSetting) {
+    Camera.Parameters parameters = camera.getParameters();
+    doSetTorch(parameters, newSetting, false);
+    camera.setParameters(parameters);
+//    boolean currentSetting = Config.KEY_FRONT_LIGHT;
+//    if (currentSetting != newSetting) {
+//      SharedPreferences.Editor editor = prefs.edit();
+//      editor.putBoolean(PreferencesActivity.KEY_FRONT_LIGHT, newSetting);
+//      editor.commit();
+//    }
+  }
+
+  public void initializeTorch(Camera.Parameters parameters, boolean safeMode) {
+    boolean currentSetting = Config.KEY_FRONT_LIGHT;
+    doSetTorch(parameters, currentSetting, safeMode);
+  }
+
+  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);
+    }
+
+    /*
+    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+    if (!prefs.getBoolean(PreferencesActivity.KEY_DISABLE_EXPOSURE, false)) {
+      if (!safeMode) {
+        ExposureInterface exposure = new ExposureManager().build();
+        exposure.setExposure(parameters, newSetting);
+      }
+    }
+     */
+  }
+
+  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<Camera.Size>(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);
+    }
+
+    Point bestSize = null;
+    float screenAspectRatio = (float) screenResolution.x / (float) screenResolution.y;
+
+    float diff = Float.POSITIVE_INFINITY;
+    for (Camera.Size supportedPreviewSize : supportedPreviewSizes) {
+      int realWidth = supportedPreviewSize.width;
+      int realHeight = supportedPreviewSize.height;
+      int pixels = realWidth * realHeight;
+      if (pixels < MIN_PREVIEW_PIXELS || pixels > MAX_PREVIEW_PIXELS) {
+        continue;
+      }
+      boolean isCandidatePortrait = realWidth < realHeight;
+      int maybeFlippedWidth = isCandidatePortrait ? realHeight : realWidth;
+      int maybeFlippedHeight = isCandidatePortrait ? realWidth : realHeight;
+      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;
+      }
+      float aspectRatio = (float) maybeFlippedWidth / (float) maybeFlippedHeight;
+      float newDiff = Math.abs(aspectRatio - screenAspectRatio);
+      if (newDiff < diff) {
+        bestSize = new Point(realWidth, realHeight);
+        diff = newDiff;
+      }
+    }
+
+    if (bestSize == null) {
+      Camera.Size defaultSize = parameters.getPreviewSize();
+      bestSize = new Point(defaultSize.width, defaultSize.height);
+      Log.i(TAG, "No suitable preview sizes, using default: " + bestSize);
+    }
+
+    Log.i(TAG, "Found best approximate preview size: " + bestSize);
+    return bestSize;
+  }
+
+  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;
+  }
+
+}

+ 338 - 0
src/com/google/zxing/client/android/camera/CameraManager.java

@@ -0,0 +1,338 @@
+/*
+ * 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.camera;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.client.android.camera.open.OpenCameraManager;
+
+import java.io.IOException;
+
+/**
+ * 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@google.com (Daniel Switkin)
+ */
+public final class CameraManager
+{
+
+    private static final String              TAG              = CameraManager.class.getSimpleName ();
+
+    private static final int                 MIN_FRAME_WIDTH  = 300;
+    private static final int                 MIN_FRAME_HEIGHT = 300;
+    private static final int                 MAX_FRAME_WIDTH  = 500;
+    private static final int                 MAX_FRAME_HEIGHT = 500;
+
+    private final Context                    context;
+    private final CameraConfigurationManager configManager;
+    private Camera                           camera;
+    private AutoFocusManager                 autoFocusManager;
+    private Rect                             framingRect;
+    private Rect                             framingRectInPreview;
+    private boolean                          initialized;
+    private boolean                          previewing;
+    private int                              requestedFramingRectWidth;
+    private int                              requestedFramingRectHeight;
+    /**
+     * 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 PreviewCallback            previewCallback;
+
+    public CameraManager(Context context)
+    {
+        this.context = context;
+        this.configManager = new CameraConfigurationManager (context);
+        previewCallback = new PreviewCallback (configManager);
+    }
+
+    public CameraConfigurationManager getConfigManager(){
+        return configManager;
+    }
+
+    
+    public Camera getCamera(){
+        return camera;
+    }
+
+    /**
+     * 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 synchronized void openDriver(SurfaceHolder holder) throws IOException{
+        Camera theCamera = camera;
+        if (theCamera == null)
+        {
+            theCamera = new OpenCameraManager ().build ().open ();
+            if (theCamera == null) { throw new IOException (); }
+            camera = theCamera;
+        }
+        theCamera.setPreviewDisplay (holder);
+
+        if (!initialized)
+        {
+            initialized = true;
+            configManager.initFromCameraParameters (theCamera);
+            if (requestedFramingRectWidth > 0 && requestedFramingRectHeight > 0)
+            {
+                setManualFramingRect (requestedFramingRectWidth, requestedFramingRectHeight);
+                requestedFramingRectWidth = 0;
+                requestedFramingRectHeight = 0;
+            }
+        }
+
+        Camera.Parameters parameters = theCamera.getParameters ();
+        String parametersFlattened = parameters == null ? null : parameters.flatten (); // Save these, temporarily
+        try
+        {
+            configManager.setDesiredCameraParameters (theCamera, false);
+        } catch (RuntimeException re)
+        {
+            // Driver failed
+            Log.w (TAG, "Camera rejected parameters. Setting only minimal safe-mode parameters");
+            Log.i (TAG, "Resetting to saved camera params: " + parametersFlattened);
+            // Reset:
+            if (parametersFlattened != null)
+            {
+                parameters = theCamera.getParameters ();
+                parameters.unflatten (parametersFlattened);
+                try
+                {
+                    theCamera.setParameters (parameters);
+                    configManager.setDesiredCameraParameters (theCamera, true);
+                } catch (RuntimeException re2)
+                {
+                    // Well, darn. Give up
+                    Log.w (TAG, "Camera rejected even safe-mode parameters! No configuration");
+                }
+            }
+        }
+
+    }
+
+    public synchronized boolean isOpen(){
+        return camera != null;
+    }
+
+    /**
+     * Closes the camera driver if still in use.
+     */
+    public synchronized void closeDriver(){
+        if (camera != null)
+        {
+            camera.release ();
+            camera = null;
+            // Make sure to clear these each time we close the camera, so that any scanning rect
+            // requested by intent is forgotten.
+            framingRect = null;
+            framingRectInPreview = null;
+        }
+    }
+
+    /**
+     * Asks the camera hardware to begin drawing preview frames to the screen.
+     */
+    public synchronized void startPreview(){
+        Camera theCamera = camera;
+        if (theCamera != null && !previewing)
+        {
+            theCamera.startPreview ();
+            previewing = true;
+            autoFocusManager = new AutoFocusManager (context,camera);
+        }
+    }
+
+    /**
+     * Tells the camera to stop drawing preview frames.
+     */
+    public synchronized void stopPreview(){
+        if (autoFocusManager != null)
+        {
+            autoFocusManager.stop ();
+            autoFocusManager = null;
+        }
+        if (camera != null && previewing)
+        {
+            camera.stopPreview ();
+            previewCallback.setHandler (null, 0);
+            previewing = false;
+        }
+    }
+
+    /**
+     * Convenience method for {@link com.google.zxing.client.android.CaptureActivity}
+     */
+    public synchronized void setTorch(boolean newSetting){
+        if (camera != null)
+        {
+            if (autoFocusManager != null)
+            {
+                autoFocusManager.stop ();
+            }
+            configManager.setTorch (camera, newSetting);
+            if (autoFocusManager != null)
+            {
+                autoFocusManager.start ();
+            }
+        }
+    }
+
+    /**
+     * 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 synchronized void requestPreviewFrame(Handler handler,int message){
+        Camera theCamera = camera;
+        if (theCamera != null && previewing)
+        {
+            previewCallback.setHandler (handler, message);
+            theCamera.setOneShotPreviewCallback (previewCallback);
+        }
+    }
+
+    /**
+     * 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 synchronized Rect getFramingRect(){
+        if (framingRect == null)
+        {
+            if (camera == null) { return null; }
+            Point screenResolution = configManager.getScreenResolution ();
+            if (screenResolution == null)
+            {
+                // Called early, before init even finished
+                return null;
+            }
+            int width = screenResolution.x * 3 / 4;
+            int height = screenResolution.y * 3 / 4;
+            if (width < MAX_FRAME_WIDTH)
+            {
+                width = MIN_FRAME_WIDTH;
+                height = MIN_FRAME_HEIGHT;
+
+            } else
+            {
+                width = MAX_FRAME_WIDTH;
+                height = MAX_FRAME_HEIGHT;
+            }
+            int leftOffset = (screenResolution.x - width) / 2;
+            int topOffset = (screenResolution.y - height) / 2;
+            framingRect = new Rect (leftOffset,topOffset,leftOffset + width,topOffset + height);
+            Log.d (TAG, "Calculated framing rect: " + framingRect);
+        }
+        return framingRect;
+    }
+
+    /**
+     * Like {@link #getFramingRect} but coordinates are in terms of the preview frame, not UI / screen.
+     */
+    public synchronized Rect getFramingRectInPreview(){
+        if (framingRectInPreview == null)
+        {
+            Rect framingRect = getFramingRect ();
+            if (framingRect == null) { return null; }
+            Rect rect = new Rect (framingRect);
+            Point cameraResolution = configManager.getCameraResolution ();
+            Point screenResolution = configManager.getScreenResolution ();
+            if (cameraResolution == null || screenResolution == null)
+            {
+                // Called early, before init even finished
+                return null;
+            }
+            // rect.left = rect.left * cameraResolution.x / screenResolution.x;
+            // rect.right = rect.right * cameraResolution.x / screenResolution.x;
+            // rect.top = rect.top * cameraResolution.y / screenResolution.y;
+            // rect.bottom = rect.bottom * cameraResolution.y / screenResolution.y;
+            rect.left = rect.left * cameraResolution.y / screenResolution.x;
+            rect.right = rect.right * cameraResolution.y / screenResolution.x;
+            rect.top = rect.top * cameraResolution.x / screenResolution.y;
+            rect.bottom = rect.bottom * cameraResolution.x / screenResolution.y;
+
+            framingRectInPreview = rect;
+        }
+        return framingRectInPreview;
+    }
+
+    /**
+     * Allows third party apps to specify the scanning rectangle dimensions, rather than determine them automatically based on screen resolution.
+     * 
+     * @param width
+     *            The width in pixels to scan.
+     * @param height
+     *            The height in pixels to scan.
+     */
+    public synchronized void setManualFramingRect(int width,int height){
+        if (initialized)
+        {
+            Point screenResolution = configManager.getScreenResolution ();
+            if (width > screenResolution.x)
+            {
+                width = screenResolution.x;
+            }
+            if (height > screenResolution.y)
+            {
+                height = screenResolution.y;
+            }
+            int leftOffset = (screenResolution.x - width) / 2;
+            int topOffset = (screenResolution.y - height) / 2;
+            framingRect = new Rect (leftOffset,topOffset,leftOffset + width,topOffset + height);
+            Log.d (TAG, "Calculated manual framing rect: " + framingRect);
+            framingRectInPreview = null;
+        } else
+        {
+            requestedFramingRectWidth = width;
+            requestedFramingRectHeight = height;
+        }
+    }
+
+    /**
+     * 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 = getFramingRectInPreview ();
+        if (rect == null) { return null; }
+        // Go ahead and assume it's YUV rather than die.
+        return new PlanarYUVLuminanceSource (data,width,height,rect.left,rect.top,rect.width (),rect.height (),false);
+    }
+
+}

+ 56 - 0
src/com/google/zxing/client/android/camera/PreviewCallback.java

@@ -0,0 +1,56 @@
+/*
+ * 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;
+import android.util.Log;
+
+final class PreviewCallback implements Camera.PreviewCallback {
+
+  private static final String TAG = PreviewCallback.class.getSimpleName();
+
+  private final CameraConfigurationManager configManager;
+  private Handler previewHandler;
+  private int previewMessage;
+
+  PreviewCallback(CameraConfigurationManager configManager) {
+    this.configManager = configManager;
+  }
+
+  void setHandler(Handler previewHandler, int previewMessage) {
+    this.previewHandler = previewHandler;
+    this.previewMessage = previewMessage;
+  }
+
+  @Override
+  public void onPreviewFrame(byte[] data, Camera camera) {
+    Point cameraResolution = configManager.getCameraResolution();
+    Handler thePreviewHandler = previewHandler;
+    if (cameraResolution != null && thePreviewHandler != null) {
+      Message message = thePreviewHandler.obtainMessage(previewMessage, cameraResolution.x,
+          cameraResolution.y, data);
+      message.sendToTarget();
+      previewHandler = null;
+    } else {
+      Log.d(TAG, "Got preview callback, but no handler or resolution available");
+    }
+  }
+
+}

+ 34 - 0
src/com/google/zxing/client/android/camera/open/DefaultOpenCameraInterface.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android.camera.open;
+
+import android.hardware.Camera;
+
+/**
+ * Default implementation for Android before API 9 / Gingerbread.
+ */
+final class DefaultOpenCameraInterface implements OpenCameraInterface {
+
+  /**
+   * Calls {@link Camera#open()}.
+   */
+  @Override
+  public Camera open() {
+    return Camera.open();
+  }
+  
+}

+ 66 - 0
src/com/google/zxing/client/android/camera/open/GingerbreadOpenCameraInterface.java

@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android.camera.open;
+
+import android.annotation.TargetApi;
+import android.hardware.Camera;
+import android.util.Log;
+
+/**
+ * Implementation for Android API 9 (Gingerbread) and later. This opens up the possibility of accessing
+ * front cameras, and rotated cameras.
+ */
+@TargetApi(9)
+public final class GingerbreadOpenCameraInterface implements OpenCameraInterface {
+
+  private static final String TAG = "GingerbreadOpenCamera";
+
+  /**
+   * Opens a rear-facing camera with {@link Camera#open(int)}, if one exists, or opens camera 0.
+   */
+  @Override
+  public Camera open() {
+    
+    int numCameras = Camera.getNumberOfCameras();
+    if (numCameras == 0) {
+      Log.w(TAG, "No cameras!");
+      return null;
+    }
+
+    int index = 0;
+    while (index < numCameras) {
+      Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+      Camera.getCameraInfo(index, cameraInfo);
+      if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+        break;
+      }
+      index++;
+    }
+    
+    Camera camera;
+    if (index < numCameras) {
+      Log.i(TAG, "Opening camera #" + index);
+      camera = Camera.open(index);
+    } else {
+      Log.i(TAG, "No camera facing back; returning camera #0");
+      camera = Camera.open(0);
+    }
+
+    return camera;
+  }
+
+}

+ 29 - 0
src/com/google/zxing/client/android/camera/open/OpenCameraInterface.java

@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android.camera.open;
+
+import android.hardware.Camera;
+
+/**
+ * Provides an abstracted means to open a {@link Camera}. The API changes over Android API versions and
+ * this allows the app to use newer API methods while retaining backwards-compatible behavior.
+ */
+public interface OpenCameraInterface {
+
+  Camera open();
+
+}

+ 32 - 0
src/com/google/zxing/client/android/camera/open/OpenCameraManager.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.zxing.client.android.camera.open;
+
+import com.google.zxing.client.android.common.PlatformSupportManager;
+
+/**
+ * Selects an appropriate implementation of {@link OpenCameraInterface} based on the device's
+ * API level.
+ */
+public final class OpenCameraManager extends PlatformSupportManager<OpenCameraInterface> {
+
+  public OpenCameraManager() {
+    super(OpenCameraInterface.class, new DefaultOpenCameraInterface());
+    addImplementationClass(9, "com.google.zxing.client.android.camera.open.GingerbreadOpenCameraInterface");
+  }
+
+}

+ 94 - 0
src/com/google/zxing/client/android/common/PlatformSupportManager.java

@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 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.common;
+
+import android.os.Build;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * <p>Sometimes the application wants to access advanced functionality exposed by Android APIs that are only available
+ * in later versions of the platform. While {@code Build.VERSION} can be used to determine the device's API level
+ * and alter behavior accordingly, and it is possible to write code that uses both old and new APIs selectively,
+ * such code would fail to load on older devices that do not have the new API methods.</p>
+ *
+ * <p>It is necessary to only load classes that use newer APIs than the device may support after the app
+ * has checked the API level. This requires reflection, loading one of several implementations based on the
+ * API level.</p>
+ *
+ * <p>This class manages that process. Subclasses of this class manage access to implementations of a given interface
+ * in an API-level-aware way. Subclasses implementation classes <em>by name</em>, and the minimum API level that
+ * the implementation is compatible with. They also provide a default implementation.</p>
+ *
+ * <p>At runtime an appropriate implementation is then chosen, instantiated and returned from {@link #build()}.</p>
+ *
+ * @param <T> the interface which managed implementations implement
+ */
+public abstract class PlatformSupportManager<T> {
+  
+  private static final String TAG = PlatformSupportManager.class.getSimpleName();
+
+  private final Class<T> managedInterface;
+  private final T defaultImplementation;
+  private final SortedMap<Integer,String> implementations;
+  
+  protected PlatformSupportManager(Class<T> managedInterface, T defaultImplementation) {
+    if (!managedInterface.isInterface()) {
+      throw new IllegalArgumentException();
+    }
+    if (!managedInterface.isInstance(defaultImplementation)) {
+      throw new IllegalArgumentException();
+    }
+    this.managedInterface = managedInterface;
+    this.defaultImplementation = defaultImplementation;
+    this.implementations = new TreeMap<Integer,String>(Collections.reverseOrder());
+  }
+  
+  protected void addImplementationClass(int minVersion, String className) {
+    implementations.put(minVersion, className);
+  }
+
+  public T build() {
+    for (Integer minVersion : implementations.keySet()) {
+      if (Build.VERSION.SDK_INT >= minVersion) {
+        String className = implementations.get(minVersion);
+        try {
+          Class<? extends T> clazz = Class.forName(className).asSubclass(managedInterface);
+          Log.i(TAG, "Using implementation " + clazz + " of " + managedInterface + " for SDK " + minVersion);
+          return clazz.getConstructor().newInstance();
+        } catch (ClassNotFoundException cnfe) {
+          Log.w(TAG, cnfe);
+        } catch (IllegalAccessException iae) {
+          Log.w(TAG, iae);
+        } catch (InstantiationException ie) {
+          Log.w(TAG, ie);
+        } catch (NoSuchMethodException nsme) {
+          Log.w(TAG, nsme);
+        } catch (InvocationTargetException ite) {
+          Log.w(TAG, ite);
+        }
+      }
+    }
+    Log.i(TAG, "Using default implementation " + defaultImplementation.getClass() + " of " + managedInterface);
+    return defaultImplementation;
+  }
+
+}

+ 25 - 0
src/com/google/zxing/client/android/common/executor/AsyncTaskExecInterface.java

@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 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.common.executor;
+
+import android.os.AsyncTask;
+
+public interface AsyncTaskExecInterface {
+
+  <T> void execute(AsyncTask<T,?,?> task, T... args);
+
+}

+ 28 - 0
src/com/google/zxing/client/android/common/executor/AsyncTaskExecManager.java

@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 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.common.executor;
+
+import com.google.zxing.client.android.common.PlatformSupportManager;
+
+public final class AsyncTaskExecManager extends PlatformSupportManager<AsyncTaskExecInterface> {
+
+  public AsyncTaskExecManager() {
+    super(AsyncTaskExecInterface.class, new DefaultAsyncTaskExecInterface());
+    addImplementationClass(11, "com.google.zxing.client.android.common.executor.HoneycombAsyncTaskExecInterface");
+  }
+
+}

+ 32 - 0
src/com/google/zxing/client/android/common/executor/DefaultAsyncTaskExecInterface.java

@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 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.common.executor;
+
+import android.os.AsyncTask;
+
+/**
+ * Before Honeycomb, {@link AsyncTask} uses parallel execution by default, which is desired. Good thing
+ * too since there is no API to request otherwise.
+ */
+public final class DefaultAsyncTaskExecInterface implements AsyncTaskExecInterface {
+
+  @Override
+  public <T> void execute(AsyncTask<T,?,?> task, T... args) {
+    task.execute(args);
+  }
+
+}

+ 36 - 0
src/com/google/zxing/client/android/common/executor/HoneycombAsyncTaskExecInterface.java

@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 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.common.executor;
+
+import android.annotation.TargetApi;
+import android.os.AsyncTask;
+
+/**
+ * On Honeycomb and later, {@link AsyncTask} returns to serial execution by default which is undesirable. This calls Honeycomb-only APIs to request parallel execution.
+ */
+@TargetApi(11)
+public final class HoneycombAsyncTaskExecInterface implements AsyncTaskExecInterface
+{
+
+
+    @Override
+    public <T> void execute(AsyncTask<T, ?, ?> task,T... args){
+//        task.executeOnExecutor (THREAD_POOL_EXECUTOR, args);
+        task.execute (args);
+    }
+
+}

+ 121 - 0
src/com/google/zxing/client/android/decode/BeepManager.java

@@ -0,0 +1,121 @@
+/*
+ * 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.decode;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Vibrator;
+import android.util.Log;
+
+import java.io.IOException;
+
+import com.libs.zxing.Config;
+import com.libs.zxing.R;
+
+/**
+ * Manages beeps and vibrations for {@link CaptureActivity}.
+ */
+public final class BeepManager
+{
+
+    private static final String TAG              = BeepManager.class.getSimpleName ();
+
+    private static final float  BEEP_VOLUME      = 0.10f;
+    private static final long   VIBRATE_DURATION = 200L;
+
+    private final Activity      activity;
+    private MediaPlayer         mediaPlayer;
+    private boolean             playBeep;
+    private boolean             vibrate;
+
+    public BeepManager(Activity activity)
+    {
+        this.activity = activity;
+        this.mediaPlayer = null;
+        updatePrefs ();
+    }
+
+    public void updatePrefs(){
+        playBeep = shouldBeep (activity);
+        vibrate =Config.KEY_VIBRATE;
+        if (playBeep && mediaPlayer == null)
+        {
+            // The volume on STREAM_SYSTEM is not adjustable, and users found it too loud,
+            // so we now play on the music stream.
+            activity.setVolumeControlStream (AudioManager.STREAM_MUSIC);
+            mediaPlayer = buildMediaPlayer (activity);
+        }
+    }
+
+    public void playBeepSoundAndVibrate(){
+        if (playBeep && mediaPlayer != null)
+        {
+            mediaPlayer.start ();
+        }
+        if (vibrate)
+        {
+            Vibrator vibrator = (Vibrator) activity.getSystemService (Context.VIBRATOR_SERVICE);
+            vibrator.vibrate (VIBRATE_DURATION);
+        }
+    }
+
+    private static boolean shouldBeep(Context activity){
+        boolean shouldPlayBeep = Config.KEY_PLAY_BEEP;
+        if (shouldPlayBeep)
+        {
+            // See if sound settings overrides this
+            AudioManager audioService = (AudioManager) activity.getSystemService (Context.AUDIO_SERVICE);
+            if (audioService.getRingerMode () != AudioManager.RINGER_MODE_NORMAL)
+            {
+                shouldPlayBeep = false;
+            }
+        }
+        return shouldPlayBeep;
+    }
+
+    private static MediaPlayer buildMediaPlayer(Context activity){
+        MediaPlayer mediaPlayer = new MediaPlayer ();
+        mediaPlayer.setAudioStreamType (AudioManager.STREAM_MUSIC);
+        // When the beep has finished playing, rewind to queue up another one.
+        mediaPlayer.setOnCompletionListener (new MediaPlayer.OnCompletionListener ()
+        {
+
+            @Override
+            public void onCompletion(MediaPlayer player){
+                player.seekTo (0);
+            }
+        });
+
+        AssetFileDescriptor file = activity.getResources ().openRawResourceFd (R.raw.beep);
+        try
+        {
+            mediaPlayer.setDataSource (file.getFileDescriptor (), file.getStartOffset (), file.getLength ());
+            file.close ();
+            mediaPlayer.setVolume (BEEP_VOLUME, BEEP_VOLUME);
+            mediaPlayer.prepare ();
+        } catch (IOException ioe)
+        {
+            Log.w (TAG, ioe);
+            mediaPlayer = null;
+        }
+        return mediaPlayer;
+    }
+
+}

+ 155 - 0
src/com/google/zxing/client/android/decode/CaptureActivityHandler.java

@@ -0,0 +1,155 @@
+/*
+ * 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.decode;
+
+import android.content.ActivityNotFoundException;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.provider.Browser;
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.google.zxing.client.android.camera.CameraManager;
+import com.libs.zxing.CaptureActivity;
+import com.libs.zxing.R;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.Collection;
+
+/**
+ * This class handles all the messaging which comprises the state machine for capture.
+ * 
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class CaptureActivityHandler extends Handler
+{
+
+    private static final String   TAG = CaptureActivityHandler.class.getSimpleName ();
+
+    private final CaptureActivity activity;
+    private final DecodeThread    decodeThread;
+    private State                 state;
+    private final CameraManager   cameraManager;
+
+    private enum State
+    {
+        PREVIEW, SUCCESS, DONE
+    }
+
+    public CaptureActivityHandler(CaptureActivity activity,ViewfinderView viewFinderView, Collection<BarcodeFormat> decodeFormats, String characterSet, CameraManager cameraManager)
+    {
+        this.activity = activity;
+        decodeThread = new DecodeThread (activity,decodeFormats,characterSet,new ViewfinderResultPointCallback (viewFinderView));
+        decodeThread.start ();
+        state = State.SUCCESS;
+
+        // Start ourselves capturing previews and decoding.
+        this.cameraManager = cameraManager;
+        cameraManager.startPreview ();
+        restartPreviewAndDecode ();
+    }
+
+    @Override
+    public void handleMessage(Message message){
+        int what = message.what;
+        if(what == R.id.restart_preview){
+            restartPreviewAndDecode ();
+        }
+        else if(what == R.id.decode_succeeded){
+            state = State.SUCCESS;
+            Bundle bundle = message.getData ();
+            Bitmap barcode = bundle == null ? null : (Bitmap) bundle.getParcelable (DecodeThread.BARCODE_BITMAP);
+            activity.handleDecode ((Result) message.obj, barcode);
+        }
+        else if(what == R.id.decode_failed){
+            state = State.PREVIEW;
+            cameraManager.requestPreviewFrame (decodeThread.getHandler (), R.id.decode);
+        }
+        else if(what == R.id.return_scan_result){
+            activity.setResult (Activity.RESULT_OK, (Intent) message.obj);
+            activity.finish ();
+        }
+        else if(what == R.id.launch_product_query){
+            Log.d (TAG, "Got product query message");
+            String url = (String) message.obj;
+
+            Intent intent = new Intent (Intent.ACTION_VIEW);
+            intent.addFlags (Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+            intent.setData (Uri.parse (url));
+
+            ResolveInfo resolveInfo = activity.getPackageManager ().resolveActivity (intent, PackageManager.MATCH_DEFAULT_ONLY);
+            String browserPackageName = null;
+            if (resolveInfo.activityInfo != null)
+            {
+                browserPackageName = resolveInfo.activityInfo.packageName;
+                Log.d (TAG, "Using browser in package " + browserPackageName);
+            }
+
+            // Needed for default Android browser only apparently
+            if ("com.android.browser".equals (browserPackageName))
+            {
+                intent.setPackage (browserPackageName);
+                intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
+                intent.putExtra (Browser.EXTRA_APPLICATION_ID, browserPackageName);
+            }
+
+            try
+            {
+                activity.startActivity (intent);
+            } catch (ActivityNotFoundException anfe)
+            {
+                Log.w (TAG, "Can't find anything to handle VIEW of URI " + url);
+            }
+        }
+    }
+
+    public void quitSynchronously(){
+        state = State.DONE;
+        cameraManager.stopPreview ();
+        Message quit = Message.obtain (decodeThread.getHandler (), R.id.quit);
+        quit.sendToTarget ();
+        try
+        {
+            // Wait at most half a second; should be enough time, and onPause() will timeout quickly
+            decodeThread.join (500L);
+        } catch (InterruptedException e)
+        {
+            // continue
+        }
+
+        // Be absolutely sure we don't send any queued up messages
+        removeMessages (R.id.decode_succeeded);
+        removeMessages (R.id.decode_failed);
+    }
+
+    private void restartPreviewAndDecode(){
+        if (state == State.SUCCESS)
+        {
+            state = State.PREVIEW;
+            cameraManager.requestPreviewFrame (decodeThread.getHandler (), R.id.decode);
+            activity.drawViewfinder ();
+        }
+    }
+
+}

+ 100 - 0
src/com/google/zxing/client/android/decode/DecodeFormatManager.java

@@ -0,0 +1,100 @@
+/*
+ * 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.decode;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import android.content.Intent;
+import android.net.Uri;
+import com.google.zxing.BarcodeFormat;
+
+final class DecodeFormatManager {
+
+  private static final Pattern COMMA_PATTERN = Pattern.compile(",");
+
+  static final Collection<BarcodeFormat> PRODUCT_FORMATS;
+  static final Collection<BarcodeFormat> ONE_D_FORMATS;
+  static final Collection<BarcodeFormat> QR_CODE_FORMATS = EnumSet.of(BarcodeFormat.QR_CODE);
+  static final Collection<BarcodeFormat> DATA_MATRIX_FORMATS = EnumSet.of(BarcodeFormat.DATA_MATRIX);
+  static {
+    PRODUCT_FORMATS = EnumSet.of(BarcodeFormat.UPC_A,
+                                 BarcodeFormat.UPC_E,
+                                 BarcodeFormat.EAN_13,
+                                 BarcodeFormat.EAN_8,
+                                 BarcodeFormat.RSS_14);
+    ONE_D_FORMATS = EnumSet.of(BarcodeFormat.CODE_39,
+                               BarcodeFormat.CODE_93,
+                               BarcodeFormat.CODE_128,
+                               BarcodeFormat.ITF,
+                               BarcodeFormat.CODABAR);
+    ONE_D_FORMATS.addAll(PRODUCT_FORMATS);
+  }
+
+  private DecodeFormatManager() {}
+
+  static Collection<BarcodeFormat> parseDecodeFormats(Intent intent) {
+    List<String> scanFormats = null;
+    String scanFormatsString = intent.getStringExtra(Intents.Scan.FORMATS);
+    if (scanFormatsString != null) {
+      scanFormats = Arrays.asList(COMMA_PATTERN.split(scanFormatsString));
+    }
+    return parseDecodeFormats(scanFormats, intent.getStringExtra(Intents.Scan.MODE));
+  }
+
+  static Collection<BarcodeFormat> parseDecodeFormats(Uri inputUri) {
+    List<String> formats = inputUri.getQueryParameters(Intents.Scan.FORMATS);
+    if (formats != null && formats.size() == 1 && formats.get(0) != null){
+      formats = Arrays.asList(COMMA_PATTERN.split(formats.get(0)));
+    }
+    return parseDecodeFormats(formats, inputUri.getQueryParameter(Intents.Scan.MODE));
+  }
+
+  private static Collection<BarcodeFormat> parseDecodeFormats(Iterable<String> scanFormats,
+                                                              String decodeMode) {
+    if (scanFormats != null) {
+      Collection<BarcodeFormat> formats = EnumSet.noneOf(BarcodeFormat.class);
+      try {
+        for (String format : scanFormats) {
+          formats.add(BarcodeFormat.valueOf(format));
+        }
+        return formats;
+      } catch (IllegalArgumentException iae) {
+        // ignore it then
+      }
+    }
+    if (decodeMode != null) {
+      if (Intents.Scan.PRODUCT_MODE.equals(decodeMode)) {
+        return PRODUCT_FORMATS;
+      }
+      if (Intents.Scan.QR_CODE_MODE.equals(decodeMode)) {
+        return QR_CODE_FORMATS;
+      }
+      if (Intents.Scan.DATA_MATRIX_MODE.equals(decodeMode)) {
+        return DATA_MATRIX_FORMATS;
+      }
+      if (Intents.Scan.ONE_D_MODE.equals(decodeMode)) {
+        return ONE_D_FORMATS;
+      }
+    }
+    return null;
+  }
+
+}

+ 143 - 0
src/com/google/zxing/client/android/decode/DecodeHandler.java

@@ -0,0 +1,143 @@
+/*
+ * 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.decode;
+
+import android.graphics.Bitmap;
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.LuminanceSource;
+import com.google.zxing.MultiFormatReader;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.client.android.camera.CameraManager;
+import com.google.zxing.common.HybridBinarizer;
+import com.libs.zxing.CaptureActivity;
+import com.libs.zxing.R;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.Map;
+
+final class DecodeHandler extends Handler
+{
+
+    private static final String     TAG     = DecodeHandler.class.getSimpleName ();
+
+    private final CaptureActivity   activity;
+    private final MultiFormatReader multiFormatReader;
+    private boolean                 running = true;
+
+    DecodeHandler(CaptureActivity activity, Map<DecodeHintType, Object> hints)
+    {
+        multiFormatReader = new MultiFormatReader ();
+        multiFormatReader.setHints (hints);
+        this.activity = activity;
+    }
+
+    @Override
+    public void handleMessage(Message message){
+        if (!running) { return; }
+        int what = message.what;
+        if (what == R.id.decode)
+        {
+            decode ((byte[]) message.obj, message.arg1, message.arg2);
+        } else if (what == R.id.quit)
+        {
+            Looper.myLooper ().quit ();
+        }
+    }
+
+    /**
+     * Decode the data within the viewfinder rectangle, and time how long it took. For efficiency, reuse the same reader objects from one decode to the next.
+     * 
+     * @param data
+     *            The YUV preview frame.
+     * @param width
+     *            The width of the preview frame.
+     * @param height
+     *            The height of the preview frame.
+     */
+    private void decode(byte[] data,int width,int height){
+        long start = System.currentTimeMillis ();
+        Result rawResult = null;
+        
+        byte[] rotatedData = new byte[data.length];
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++)
+                rotatedData[x * height + height - y - 1] = data[x + y * width];
+        }
+        int tmp = width; 
+        width = height;
+        height = tmp;
+    
+        data = rotatedData;
+        
+        PlanarYUVLuminanceSource source =activity.getCameraManager().buildLuminanceSource (data, width, height);
+        if (source != null)
+        {
+            BinaryBitmap bitmap = new BinaryBitmap (new HybridBinarizer (source));
+            try
+            {
+                rawResult = multiFormatReader.decodeWithState (bitmap);
+            } catch (ReaderException re)
+            {
+                // continue
+            } finally
+            {
+                multiFormatReader.reset ();
+            }
+        }
+
+        Handler handler = activity.getHandler ();
+        if (rawResult != null)
+        {
+            // Don't log the barcode contents for security.
+            long end = System.currentTimeMillis ();
+            Log.d (TAG, "Found barcode in " + (end - start) + " ms");
+            if (handler != null)
+            {
+                Message message = Message.obtain (handler, R.id.decode_succeeded, rawResult);
+                Bundle bundle = new Bundle ();
+                Bitmap grayscaleBitmap = toBitmap (source, source.renderCroppedGreyscaleBitmap ());
+                bundle.putParcelable (DecodeThread.BARCODE_BITMAP, grayscaleBitmap);
+                message.setData (bundle);
+                message.sendToTarget ();
+            }
+        } else
+        {
+            if (handler != null)
+            {
+                Message message = Message.obtain (handler, R.id.decode_failed);
+                message.sendToTarget ();
+            }
+        }
+    }
+
+    private static Bitmap toBitmap(LuminanceSource source,int[] pixels){
+        int width = source.getWidth ();
+        int height = source.getHeight ();
+        Bitmap bitmap = Bitmap.createBitmap (width, height, Bitmap.Config.ARGB_8888);
+        bitmap.setPixels (pixels, 0, width, 0, 0, width, height);
+        return bitmap;
+    }
+
+}

+ 103 - 0
src/com/google/zxing/client/android/decode/DecodeThread.java

@@ -0,0 +1,103 @@
+/*
+ * 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.decode;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.ResultPointCallback;
+import com.libs.zxing.CaptureActivity;
+import com.libs.zxing.Config;
+
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ * 
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+final class DecodeThread extends Thread
+{
+
+    public static final String                BARCODE_BITMAP = "barcode_bitmap";
+
+    private final CaptureActivity             activity;
+    private final Map<DecodeHintType, Object> hints;
+    private Handler                           handler;
+    private final CountDownLatch              handlerInitLatch;
+
+    DecodeThread(CaptureActivity activity, Collection<BarcodeFormat> decodeFormats, String characterSet, ResultPointCallback resultPointCallback)
+    {
+
+        this.activity = activity;
+        handlerInitLatch = new CountDownLatch (1);
+
+        hints = new EnumMap<DecodeHintType, Object> (DecodeHintType.class);
+
+        // The prefs can't change while the thread is running, so pick them up once here.
+        if (decodeFormats == null || decodeFormats.isEmpty ())
+        {
+            decodeFormats = EnumSet.noneOf (BarcodeFormat.class);
+            if (Config.KEY_DECODE_1D)
+            {
+                decodeFormats.addAll (DecodeFormatManager.ONE_D_FORMATS);
+            }
+            if (Config.KEY_DECODE_QR)
+            {
+                decodeFormats.addAll (DecodeFormatManager.QR_CODE_FORMATS);
+            }
+            if (Config.KEY_DECODE_DATA_MATRIX)
+            {
+                decodeFormats.addAll (DecodeFormatManager.DATA_MATRIX_FORMATS);
+            }
+        }
+        hints.put (DecodeHintType.POSSIBLE_FORMATS, decodeFormats);
+
+        if (characterSet != null)
+        {
+            hints.put (DecodeHintType.CHARACTER_SET, characterSet);
+        }
+        hints.put (DecodeHintType.NEED_RESULT_POINT_CALLBACK, resultPointCallback);
+    }
+
+    Handler getHandler(){
+        try
+        {
+            handlerInitLatch.await ();
+        } catch (InterruptedException ie)
+        {
+            // continue?
+        }
+        return handler;
+    }
+
+    @Override
+    public void run(){
+        Looper.prepare ();
+        handler = new DecodeHandler (activity,hints);
+        handlerInitLatch.countDown ();
+        Looper.loop ();
+    }
+
+}

+ 121 - 0
src/com/google/zxing/client/android/decode/InactivityTimer.java

@@ -0,0 +1,121 @@
+/*
+ * 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.decode;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.BatteryManager;
+import android.util.Log;
+
+import com.google.zxing.client.android.common.executor.AsyncTaskExecInterface;
+import com.google.zxing.client.android.common.executor.AsyncTaskExecManager;
+
+/**
+ * Finishes an activity after a period of inactivity if the device is on battery power.
+ */
+public final class InactivityTimer
+{
+
+    private static final String          TAG                 = InactivityTimer.class.getSimpleName ();
+
+    private static final long            INACTIVITY_DELAY_MS = 5 * 60 * 1000L;
+
+    private final Activity               activity;
+    private final AsyncTaskExecInterface taskExec;
+    private final BroadcastReceiver      powerStatusReceiver;
+    private InactivityAsyncTask          inactivityTask;
+
+    public InactivityTimer(Activity activity)
+    {
+        this.activity = activity;
+        taskExec = new AsyncTaskExecManager ().build ();
+        powerStatusReceiver = new PowerStatusReceiver ();
+        onActivity ();
+    }
+
+    public synchronized void onActivity(){
+        cancel ();
+        inactivityTask = new InactivityAsyncTask ();
+        taskExec.execute (inactivityTask);
+    }
+
+    public void onPause(){
+        cancel ();
+        activity.unregisterReceiver (powerStatusReceiver);
+    }
+
+    public void onResume(){
+        activity.registerReceiver (powerStatusReceiver, new IntentFilter (Intent.ACTION_BATTERY_CHANGED));
+        onActivity ();
+    }
+
+    private synchronized void cancel(){
+        AsyncTask<?, ?, ?> task = inactivityTask;
+        if (task != null)
+        {
+            task.cancel (true);
+            inactivityTask = null;
+        }
+    }
+
+   public void shutdown(){
+        cancel ();
+    }
+
+    private final class PowerStatusReceiver extends BroadcastReceiver
+    {
+
+        @Override
+        public void onReceive(Context context,Intent intent){
+            if (Intent.ACTION_BATTERY_CHANGED.equals (intent.getAction ()))
+            {
+                // 0 indicates that we're on battery
+                boolean onBatteryNow = intent.getIntExtra (BatteryManager.EXTRA_PLUGGED, -1) <= 0;
+                if (onBatteryNow)
+                {
+                    InactivityTimer.this.onActivity ();
+                } else
+                {
+                    InactivityTimer.this.cancel ();
+                }
+            }
+        }
+    }
+
+    private final class InactivityAsyncTask extends AsyncTask<Object, Object, Object>
+    {
+
+        @Override
+        protected Object doInBackground(Object... objects){
+            try
+            {
+                Thread.sleep (INACTIVITY_DELAY_MS);
+                Log.i (TAG, "Finishing activity due to inactivity");
+                activity.finish ();
+            } catch (InterruptedException e)
+            {
+                // continue without killing
+            }
+            return null;
+        }
+    }
+
+}

+ 26 - 0
src/com/google/zxing/client/android/decode/IntentSource.java

@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2011 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.decode;
+
+public enum IntentSource {
+
+  NATIVE_APP_INTENT,
+  PRODUCT_SEARCH_LINK,
+  ZXING_LINK,
+  NONE
+
+}

+ 261 - 0
src/com/google/zxing/client/android/decode/Intents.java

@@ -0,0 +1,261 @@
+/*
+ * 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.decode;
+
+/**
+ * This class provides the constants to use when sending an Intent to Barcode Scanner.
+ * These strings are effectively API and cannot be changed.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class Intents {
+  private Intents() {
+  }
+
+  public static final class Scan {
+    /**
+     * Send this intent to open the Barcodes app in scanning mode, find a barcode, and return
+     * the results.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SCAN";
+
+    /**
+     * By default, sending this will decode all barcodes that we understand. However it
+     * may be useful to limit scanning to certain formats. Use
+     * {@link android.content.Intent#putExtra(String, String)} with one of the values below.
+     *
+     * Setting this is effectively shorthand for setting explicit formats with {@link #FORMATS}.
+     * It is overridden by that setting.
+     */
+    public static final String MODE = "SCAN_MODE";
+
+    /**
+     * Decode only UPC and EAN barcodes. This is the right choice for shopping apps which get
+     * prices, reviews, etc. for products.
+     */
+    public static final String PRODUCT_MODE = "PRODUCT_MODE";
+
+    /**
+     * Decode only 1D barcodes.
+     */
+    public static final String ONE_D_MODE = "ONE_D_MODE";
+
+    /**
+     * Decode only QR codes.
+     */
+    public static final String QR_CODE_MODE = "QR_CODE_MODE";
+
+    /**
+     * Decode only Data Matrix codes.
+     */
+    public static final String DATA_MATRIX_MODE = "DATA_MATRIX_MODE";
+
+    /**
+     * Comma-separated list of formats to scan for. The values must match the names of
+     * {@link com.google.zxing.BarcodeFormat}s, e.g. {@link com.google.zxing.BarcodeFormat#EAN_13}.
+     * Example: "EAN_13,EAN_8,QR_CODE". This overrides {@link #MODE}.
+     */
+    public static final String FORMATS = "SCAN_FORMATS";
+
+    /**
+     * @see com.google.zxing.DecodeHintType#CHARACTER_SET
+     */
+    public static final String CHARACTER_SET = "CHARACTER_SET";
+
+    /**
+     * Optional parameters to specify the width and height of the scanning rectangle in pixels.
+     * The app will try to honor these, but will clamp them to the size of the preview frame.
+     * You should specify both or neither, and pass the size as an int.
+     */
+    public static final String WIDTH = "SCAN_WIDTH";
+    public static final String HEIGHT = "SCAN_HEIGHT";
+
+    /**
+     * Desired duration in milliseconds for which to pause after a successful scan before
+     * returning to the calling intent. Specified as a long, not an integer!
+     * For example: 1000L, not 1000.
+     */
+    public static final String RESULT_DISPLAY_DURATION_MS = "RESULT_DISPLAY_DURATION_MS";
+
+    /**
+     * Prompt to show on-screen when scanning by intent. Specified as a {@link String}.
+     */
+    public static final String PROMPT_MESSAGE = "PROMPT_MESSAGE";
+
+    /**
+     * If a barcode is found, Barcodes returns {@link android.app.Activity#RESULT_OK} to
+     * {@link android.app.Activity#onActivityResult(int, int, android.content.Intent)}
+     * of the app which requested the scan via
+     * {@link android.app.Activity#startActivityForResult(android.content.Intent, int)}
+     * The barcodes contents can be retrieved with
+     * {@link android.content.Intent#getStringExtra(String)}. 
+     * If the user presses Back, the result code will be {@link android.app.Activity#RESULT_CANCELED}.
+     */
+    public static final String RESULT = "SCAN_RESULT";
+
+    /**
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_FORMAT}
+     * to determine which barcode format was found.
+     * See {@link com.google.zxing.BarcodeFormat} for possible values.
+     */
+    public static final String RESULT_FORMAT = "SCAN_RESULT_FORMAT";
+
+    /**
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_UPC_EAN_EXTENSION}
+     * to return the content of any UPC extension barcode that was also found. Only applicable
+     * to {@link com.google.zxing.BarcodeFormat#UPC_A} and {@link com.google.zxing.BarcodeFormat#EAN_13}
+     * formats.
+     */
+    public static final String RESULT_UPC_EAN_EXTENSION = "SCAN_RESULT_UPC_EAN_EXTENSION";
+
+    /**
+     * Call {@link android.content.Intent#getByteArrayExtra(String)} with {@link #RESULT_BYTES}
+     * to get a {@code byte[]} of raw bytes in the barcode, if available.
+     */
+    public static final String RESULT_BYTES = "SCAN_RESULT_BYTES";
+
+    /**
+     * Key for the value of {@link com.google.zxing.ResultMetadataType#ORIENTATION}, if available.
+     * Call {@link android.content.Intent#getIntArrayExtra(String)} with {@link #RESULT_ORIENTATION}.
+     */
+    public static final String RESULT_ORIENTATION = "SCAN_RESULT_ORIENTATION";
+
+    /**
+     * Key for the value of {@link com.google.zxing.ResultMetadataType#ERROR_CORRECTION_LEVEL}, if available.
+     * Call {@link android.content.Intent#getStringExtra(String)} with {@link #RESULT_ERROR_CORRECTION_LEVEL}.
+     */
+    public static final String RESULT_ERROR_CORRECTION_LEVEL = "SCAN_RESULT_ERROR_CORRECTION_LEVEL";
+
+    /**
+     * Prefix for keys that map to the values of {@link com.google.zxing.ResultMetadataType#BYTE_SEGMENTS},
+     * if available. The actual values will be set under a series of keys formed by adding 0, 1, 2, ...
+     * to this prefix. So the first byte segment is under key "SCAN_RESULT_BYTE_SEGMENTS_0" for example.
+     * Call {@link android.content.Intent#getByteArrayExtra(String)} with these keys.
+     */
+    public static final String RESULT_BYTE_SEGMENTS_PREFIX = "SCAN_RESULT_BYTE_SEGMENTS_";
+
+    /**
+     * Setting this to false will not save scanned codes in the history. Specified as a {@code boolean}.
+     */
+    public static final String SAVE_HISTORY = "SAVE_HISTORY";
+
+    private Scan() {
+    }
+  }
+
+  public static final class History {
+
+    public static final String ITEM_NUMBER = "ITEM_NUMBER";
+
+    private History() {
+    }
+  }
+
+  public static final class Encode {
+    /**
+     * Send this intent to encode a piece of data as a QR code and display it full screen, so
+     * that another person can scan the barcode from your screen.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.ENCODE";
+
+    /**
+     * The data to encode. Use {@link android.content.Intent#putExtra(String, String)} or
+     * {@link android.content.Intent#putExtra(String, android.os.Bundle)}, 
+     * depending on the type and format specified. Non-QR Code formats should
+     * just use a String here. For QR Code, see Contents for details.
+     */
+    public static final String DATA = "ENCODE_DATA";
+
+    /**
+     * The type of data being supplied if the format is QR Code. Use
+     * {@link android.content.Intent#putExtra(String, String)} with one of {@link Contents.Type}.
+     */
+    public static final String TYPE = "ENCODE_TYPE";
+
+    /**
+     * The barcode format to be displayed. If this isn't specified or is blank,
+     * it defaults to QR Code. Use {@link android.content.Intent#putExtra(String, String)}, where
+     * format is one of {@link com.google.zxing.BarcodeFormat}.
+     */
+    public static final String FORMAT = "ENCODE_FORMAT";
+
+    /**
+     * Normally the contents of the barcode are displayed to the user in a TextView. Setting this
+     * boolean to false will hide that TextView, showing only the encode barcode.
+     */
+    public static final String SHOW_CONTENTS = "ENCODE_SHOW_CONTENTS";
+
+    private Encode() {
+    }
+  }
+
+  public static final class SearchBookContents {
+    /**
+     * Use Google Book Search to search the contents of the book provided.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SEARCH_BOOK_CONTENTS";
+
+    /**
+     * The book to search, identified by ISBN number.
+     */
+    public static final String ISBN = "ISBN";
+
+    /**
+     * An optional field which is the text to search for.
+     */
+    public static final String QUERY = "QUERY";
+
+    private SearchBookContents() {
+    }
+  }
+
+  public static final class WifiConnect {
+    /**
+     * Internal intent used to trigger connection to a wi-fi network.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.WIFI_CONNECT";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String SSID = "SSID";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String TYPE = "TYPE";
+
+    /**
+     * The network to connect to, all the configuration provided here.
+     */
+    public static final String PASSWORD = "PASSWORD";
+
+    private WifiConnect() {
+    }
+  }
+
+  public static final class Share {
+    /**
+     * Give the user a choice of items to encode as a barcode, then render it as a QR Code and
+     * display onscreen for a friend to scan with their phone.
+     */
+    public static final String ACTION = "com.google.zxing.client.android.SHARE";
+
+    private Share() {
+    }
+  }
+}

+ 37 - 0
src/com/google/zxing/client/android/decode/ViewfinderResultPointCallback.java

@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 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.decode;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.ResultPointCallback;
+
+final class ViewfinderResultPointCallback implements ResultPointCallback
+{
+
+    private final ViewfinderView viewfinderView;
+
+    ViewfinderResultPointCallback(ViewfinderView viewfinderView)
+    {
+        this.viewfinderView = viewfinderView;
+    }
+
+    @Override
+    public void foundPossibleResultPoint(ResultPoint point){
+        viewfinderView.addPossibleResultPoint (point);
+    }
+
+}

+ 163 - 0
src/com/google/zxing/client/android/decode/ViewfinderView.java

@@ -0,0 +1,163 @@
+/*
+ * 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.decode;
+
+import com.google.zxing.ResultPoint;
+import com.google.zxing.client.android.camera.CameraManager;
+import com.libs.zxing.R;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.Collection;
+/**
+ * This view is overlaid on top of the camera preview. It adds the viewfinder rectangle and partial transparency outside it, as well as the laser scanner animation and result points.
+ * 
+ * @author dswitkin@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 int        OPAQUE                = 0xFF;
+    private static final long       ANIMATION_DELAY       = 50L;
+    private static final int        CURRENT_POINT_OPACITY = 0xFF;
+    private static final int        MAX_RESULT_POINTS     = 20;
+    private static final int        POINT_SIZE            = 6;
+
+    private CameraManager           cameraManager;
+    private final Paint             paint;
+    private Bitmap                  resultBitmap;
+    private final int               maskColor;
+    private final int               resultColor;
+    private final int               laserColor;
+    private final int               resultPointColor;
+    private final int               frameColor;
+    private int                     scannerAlpha;
+    private Collection<ResultPoint> possibleResultPoints;
+    private Collection<ResultPoint> lastPossibleResultPoints;
+    
+    private int margin = 0;
+
+    // This constructor is used when the class is built from an XML resource.
+    public ViewfinderView(Context context, AttributeSet attrs)
+    {
+        super (context, attrs);
+
+        // Initialize these once for performance rather than calling them every time in onDraw().
+        paint = new Paint (Paint.ANTI_ALIAS_FLAG);
+        Resources resources = getResources ();
+        maskColor = resources.getColor (R.color.viewfinder_mask);
+        resultColor = resources.getColor (R.color.result_view);
+        laserColor = resources.getColor (R.color.viewfinder_laser);
+        resultPointColor = resources.getColor (R.color.possible_result_points);
+        frameColor = resources.getColor (R.color.viewfinder_frame);
+        scannerAlpha = 0;
+        possibleResultPoints = new ArrayList<ResultPoint> (5);
+        lastPossibleResultPoints = null;
+    }
+
+    public void setCameraManager(CameraManager cameraManager){
+        this.cameraManager = cameraManager;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas){