liuyuqi-dellpc 6 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){
+        if (cameraManager == null) { return; // not ready yet, early draw before done configuring
+        }
+        Rect frame = cameraManager.getFramingRect ();
+        if (frame == null) { return; }
+        int width = canvas.getWidth ();
+        int height = canvas.getHeight ();
+
+        // Draw the exterior (i.e. outside the framing rect) darkened
+        paint.setColor (resultBitmap != null ? resultColor : maskColor);
+        canvas.drawRect (0, 0, width, frame.top, paint);
+        canvas.drawRect (0, frame.top, frame.left, frame.bottom + 1, paint);
+        canvas.drawRect (frame.right + 1, frame.top, width, frame.bottom + 1, paint);
+        canvas.drawRect (0, frame.bottom + 1, width, height, paint);
+
+        if (resultBitmap != null)
+        {
+            // Draw the opaque result bitmap over the scanning rectangle
+            paint.setAlpha (CURRENT_POINT_OPACITY);
+            canvas.drawBitmap (resultBitmap, null, frame, paint);
+        } else
+        {
+
+            int linewidht = 10;
+            paint.setColor (frameColor);
+
+            canvas.drawRect (frame.left, frame.top,(linewidht + frame.left), (50 + frame.top), paint);
+            canvas.drawRect (frame.left, frame.top,(50 + frame.left), (linewidht + frame.top), paint);
+            canvas.drawRect (((0 - linewidht) + frame.right), frame.top, (1 + frame.right), (50 + frame.top), paint);
+            canvas.drawRect ((-50 + frame.right), frame.top, frame.right, (linewidht + frame.top), paint);
+            canvas.drawRect (frame.left, (-49 + frame.bottom), (linewidht + frame.left), (1 + frame.bottom), paint);
+            canvas.drawRect (frame.left, ((0 - linewidht) + frame.bottom), (50 + frame.left), (1 + frame.bottom), paint);
+            canvas.drawRect (((0 - linewidht) + frame.right), -1 + (-49 + frame.bottom), (1 + frame.right), (1 + frame.bottom), paint);
+            canvas.drawRect ((-50 + frame.right), ((0 - linewidht) + frame.bottom), frame.right, (linewidht - (linewidht - 1) + frame.bottom), paint);
+            
+            paint.setAntiAlias(true);
+            paint.setStrokeWidth(4);
+            canvas.drawLine (frame.left, frame.top + margin, frame.right, frame.top + margin, paint);
+            
+            margin = (margin+4) % (frame.top);
+            
+            paint.setAlpha (SCANNER_ALPHA[scannerAlpha]);
+            scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
+            int vmiddle = frame.height () / 2 + frame.top;
+            int hmiddle = frame.width () / 2 + frame.left;
+            canvas.drawRect (hmiddle - 20, vmiddle - 1, hmiddle + 20, vmiddle + 2, paint);
+            canvas.drawRect (hmiddle - 1, vmiddle - 20, hmiddle + 2, vmiddle + 20, paint);
+            postInvalidateDelayed (ANIMATION_DELAY, frame.left, frame.top, frame.right, frame.bottom);
+        }
+    }
+
+    public void drawViewfinder(){
+        Bitmap resultBitmap = this.resultBitmap;
+        this.resultBitmap = null;
+        if (resultBitmap != null)
+        {
+            resultBitmap.recycle ();
+        }
+        invalidate ();
+    }
+
+    /**
+     * Draw a bitmap with the result points highlighted instead of the live scanning display.
+     * 
+     * @param barcode
+     *            An image of the decoded barcode.
+     */
+    public void drawResultBitmap(Bitmap barcode){
+        resultBitmap = barcode;
+        invalidate ();
+    }
+
+    public void addPossibleResultPoint(ResultPoint point){
+        possibleResultPoints.add (point);
+    }
+
+}

+ 114 - 0
src/com/google/zxing/client/android/result/Contents.java

@@ -0,0 +1,114 @@
+/*
+ * 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.result;
+
+import android.provider.ContactsContract;
+
+/**
+ * The set of constants to use when sending Barcode Scanner an Intent which requests a barcode
+ * to be encoded.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class Contents {
+  private Contents() {
+  }
+
+  public static final class Type {
+    /**
+     * Plain text. Use Intent.putExtra(DATA, string). This can be used for URLs too, but string
+     * must include "http://" or "https://".
+     */
+    public static final String TEXT = "TEXT_TYPE";
+
+    /**
+     * An email type. Use Intent.putExtra(DATA, string) where string is the email address.
+     */
+    public static final String EMAIL = "EMAIL_TYPE";
+
+    /**
+     * Use Intent.putExtra(DATA, string) where string is the phone number to call.
+     */
+    public static final String PHONE = "PHONE_TYPE";
+
+    /**
+     * An SMS type. Use Intent.putExtra(DATA, string) where string is the number to SMS.
+     */
+    public static final String SMS = "SMS_TYPE";
+
+    /**
+     * A contact. Send a request to encode it as follows:
+     * <p/>
+     * import android.provider.Contacts;
+     * <p/>
+     * Intent intent = new Intent(Intents.Encode.ACTION);
+     * intent.putExtra(Intents.Encode.TYPE, CONTACT);
+     * Bundle bundle = new Bundle();
+     * bundle.putString(Contacts.Intents.Insert.NAME, "Jenny");
+     * bundle.putString(Contacts.Intents.Insert.PHONE, "8675309");
+     * bundle.putString(Contacts.Intents.Insert.EMAIL, "jenny@the80s.com");
+     * bundle.putString(Contacts.Intents.Insert.POSTAL, "123 Fake St. San Francisco, CA 94102");
+     * intent.putExtra(Intents.Encode.DATA, bundle);
+     */
+    public static final String CONTACT = "CONTACT_TYPE";
+
+    /**
+     * A geographic location. Use as follows:
+     * Bundle bundle = new Bundle();
+     * bundle.putFloat("LAT", latitude);
+     * bundle.putFloat("LONG", longitude);
+     * intent.putExtra(Intents.Encode.DATA, bundle);
+     */
+    public static final String LOCATION = "LOCATION_TYPE";
+
+    private Type() {
+    }
+  }
+
+  public static final String URL_KEY = "URL_KEY";
+
+  public static final String NOTE_KEY = "NOTE_KEY";
+
+  /**
+   * When using Type.CONTACT, these arrays provide the keys for adding or retrieving multiple
+   * phone numbers and addresses.
+   */
+  public static final String[] PHONE_KEYS = {
+      ContactsContract.Intents.Insert.PHONE,
+      ContactsContract.Intents.Insert.SECONDARY_PHONE,
+      ContactsContract.Intents.Insert.TERTIARY_PHONE
+  };
+
+  public static final String[] PHONE_TYPE_KEYS = {
+      ContactsContract.Intents.Insert.PHONE_TYPE,
+      ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE,
+      ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE
+  };
+
+  public static final String[] EMAIL_KEYS = {
+      ContactsContract.Intents.Insert.EMAIL,
+      ContactsContract.Intents.Insert.SECONDARY_EMAIL,
+      ContactsContract.Intents.Insert.TERTIARY_EMAIL
+  };
+
+  public static final String[] EMAIL_TYPE_KEYS = {
+      ContactsContract.Intents.Insert.EMAIL_TYPE,
+      ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE,
+      ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE
+  };
+
+}

+ 40 - 0
src/com/google/zxing/client/android/result/DefaultResultHandler.java

@@ -0,0 +1,40 @@
+package com.google.zxing.client.android.result;
+
+import android.app.Activity;
+import android.widget.Toast;
+
+import com.google.zxing.Result;
+import com.google.zxing.client.result.ParsedResult;
+import com.libs.zxing.R;
+
+
+public class DefaultResultHandler extends ResultHandler
+{
+
+    public DefaultResultHandler(Activity activity, ParsedResult result, Result rawResult)
+    {
+        super (activity, result, rawResult);
+        // TODO Auto-generated constructor stub
+    }
+
+    @Override
+    public int getButtonCount(){
+        return 1;
+    }
+
+    @Override
+    public int getButtonText(int index){
+        return R.string.button_2;
+    }
+
+    @Override
+    public void handleButtonPress(int index){
+        Toast.makeText (getActivity(), getResult ().getDisplayResult (), Toast.LENGTH_SHORT).show ();
+    }
+
+    @Override
+    public int getDisplayTitle(){
+        return R.string.button_3;
+    }
+
+}

+ 170 - 0
src/com/google/zxing/client/android/result/LocaleManager.java

@@ -0,0 +1,170 @@
+/*
+ * 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.result;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.HashMap;
+
+/**
+ * Handles any locale-specific logic for the client.
+ *
+ * @author Sean Owen
+ */
+public final class LocaleManager {
+
+  private static final String DEFAULT_TLD = "com";
+  private static final String DEFAULT_COUNTRY = "US";
+  private static final String DEFAULT_LANGUAGE = "en";
+
+  /**
+   * Locales (well, countries) where Google web search is available.
+   * These should be kept in sync with our translations.
+   */
+  private static final Map<String,String> GOOGLE_COUNTRY_TLD;
+  static {
+    GOOGLE_COUNTRY_TLD = new HashMap<String,String>();
+    GOOGLE_COUNTRY_TLD.put("AR", "com.ar"); // ARGENTINA
+    GOOGLE_COUNTRY_TLD.put("AU", "com.au"); // AUSTRALIA
+    GOOGLE_COUNTRY_TLD.put("BR", "com.br"); // BRAZIL
+    GOOGLE_COUNTRY_TLD.put("BG", "bg"); // BULGARIA
+    GOOGLE_COUNTRY_TLD.put(Locale.CANADA.getCountry(), "ca");
+    GOOGLE_COUNTRY_TLD.put(Locale.CHINA.getCountry(), "cn");
+    GOOGLE_COUNTRY_TLD.put("CZ", "cz"); // CZECH REPUBLIC
+    GOOGLE_COUNTRY_TLD.put("DK", "dk"); // DENMARK
+    GOOGLE_COUNTRY_TLD.put("FI", "fi"); // FINLAND
+    GOOGLE_COUNTRY_TLD.put(Locale.FRANCE.getCountry(), "fr");
+    GOOGLE_COUNTRY_TLD.put(Locale.GERMANY.getCountry(), "de");
+    GOOGLE_COUNTRY_TLD.put("GR", "gr"); // GREECE
+    GOOGLE_COUNTRY_TLD.put("HU", "hu"); // HUNGARY
+    GOOGLE_COUNTRY_TLD.put("ID", "co.id"); // INDONESIA
+    GOOGLE_COUNTRY_TLD.put("IL", "co.il"); // ISRAEL
+    GOOGLE_COUNTRY_TLD.put(Locale.ITALY.getCountry(), "it");
+    GOOGLE_COUNTRY_TLD.put(Locale.JAPAN.getCountry(), "co.jp");
+    GOOGLE_COUNTRY_TLD.put(Locale.KOREA.getCountry(), "co.kr");
+    GOOGLE_COUNTRY_TLD.put("NL", "nl"); // NETHERLANDS
+    GOOGLE_COUNTRY_TLD.put("PL", "pl"); // POLAND
+    GOOGLE_COUNTRY_TLD.put("PT", "pt"); // PORTUGAL
+    GOOGLE_COUNTRY_TLD.put("RU", "ru"); // RUSSIA
+    GOOGLE_COUNTRY_TLD.put("SK", "sk"); // SLOVAK REPUBLIC
+    GOOGLE_COUNTRY_TLD.put("SI", "si"); // SLOVENIA
+    GOOGLE_COUNTRY_TLD.put("ES", "es"); // SPAIN
+    GOOGLE_COUNTRY_TLD.put("SE", "se"); // SWEDEN
+    GOOGLE_COUNTRY_TLD.put(Locale.TAIWAN.getCountry(), "tw");
+    GOOGLE_COUNTRY_TLD.put("TR", "com.tr"); // TURKEY
+    GOOGLE_COUNTRY_TLD.put(Locale.UK.getCountry(), "co.uk");
+    GOOGLE_COUNTRY_TLD.put(Locale.US.getCountry(), "com");
+  }
+
+  /**
+   * Google Product Search for mobile is available in fewer countries than web search. See here:
+   * http://www.google.com/support/merchants/bin/answer.py?answer=160619
+   */
+  private static final Map<String,String> GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD;
+  static {
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD = new HashMap<String,String>();
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("AU", "com.au"); // AUSTRALIA
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.CHINA.getCountry(), "cn");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.FRANCE.getCountry(), "fr");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.GERMANY.getCountry(), "de");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.ITALY.getCountry(), "it");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.JAPAN.getCountry(), "co.jp");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("NL", "nl"); // NETHERLANDS
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put("ES", "es"); // SPAIN
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.UK.getCountry(), "co.uk");
+    GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD.put(Locale.US.getCountry(), "com");
+  }
+
+  /**
+   * Book search is offered everywhere that web search is available.
+   */
+  private static final Map<String,String> GOOGLE_BOOK_SEARCH_COUNTRY_TLD = GOOGLE_COUNTRY_TLD;
+
+  private static final Collection<String> TRANSLATED_HELP_ASSET_LANGUAGES =
+      Arrays.asList("de", "en", "es", "fr", "it", "ja", "ko", "nl", "pt", "ru", "zh-rCN", "zh-rTW");
+
+  private LocaleManager() {}
+
+  /**
+   * @return country-specific TLD suffix appropriate for the current default locale
+   *  (e.g. "co.uk" for the United Kingdom)
+   */
+  public static String getCountryTLD(Context context) {
+    return doGetTLD(GOOGLE_COUNTRY_TLD, context);
+  }
+
+  /**
+   * The same as above, but specifically for Google Product Search.
+   * @return The top-level domain to use.
+   */
+  public static String getProductSearchCountryTLD(Context context) {
+    return doGetTLD(GOOGLE_PRODUCT_SEARCH_COUNTRY_TLD, context);
+  }
+
+  /**
+   * The same as above, but specifically for Google Book Search.
+   * @return The top-level domain to use.
+   */
+  public static String getBookSearchCountryTLD(Context context) {
+    return doGetTLD(GOOGLE_BOOK_SEARCH_COUNTRY_TLD, context);
+  }
+
+  /**
+   * Does a given URL point to Google Book Search, regardless of domain.
+   *
+   * @param url The address to check.
+   * @return True if this is a Book Search URL.
+   */
+  public static boolean isBookSearchUrl(String url) {
+    return url.startsWith("http://google.com/books") || url.startsWith("http://books.google.");
+  }
+
+  private static String getSystemCountry() {
+    Locale locale = Locale.getDefault();
+    return locale == null ? DEFAULT_COUNTRY : locale.getCountry();
+  }
+
+  private static String getSystemLanguage() {
+    Locale locale = Locale.getDefault();
+    if (locale == null) {
+      return DEFAULT_LANGUAGE;
+    }
+    String language = locale.getLanguage();
+    // Special case Chinese
+    if (Locale.SIMPLIFIED_CHINESE.getLanguage().equals(language)) {
+      return language + "-r" + getSystemCountry();
+    }
+    return language;
+  }
+
+  public static String getTranslatedAssetLanguage() {
+    String language = getSystemLanguage();
+    return TRANSLATED_HELP_ASSET_LANGUAGES.contains(language) ? language : DEFAULT_LANGUAGE;
+  }
+
+  private static String doGetTLD(Map<String,String> map, Context context) {
+    String tld = map.get(getSystemCountry());
+    return tld == null ? DEFAULT_TLD : tld;
+  }
+
+}

+ 41 - 0
src/com/google/zxing/client/android/result/ResultButtonListener.java

@@ -0,0 +1,41 @@
+/*
+ * 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.result;
+
+import android.view.View;
+import android.widget.Button;
+
+/**
+ * Handles the result of barcode decoding in the context of the Android platform, by dispatching the
+ * proper intents to open other activities like GMail, Maps, etc.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ResultButtonListener implements Button.OnClickListener {
+  private final ResultHandler resultHandler;
+  private final int index;
+
+  public ResultButtonListener(ResultHandler resultHandler, int index) {
+    this.resultHandler = resultHandler;
+    this.index = index;
+  }
+
+  @Override
+  public void onClick(View view) {
+    resultHandler.handleButtonPress(index);
+  }
+}

+ 488 - 0
src/com/google/zxing/client/android/result/ResultHandler.java

@@ -0,0 +1,488 @@
+/*
+ * 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.result;
+
+import com.google.zxing.Result;
+import com.google.zxing.client.result.ParsedResult;
+import com.google.zxing.client.result.ParsedResultType;
+import com.libs.zxing.R;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.SearchManager;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.Collection;
+import java.util.Locale;
+
+/**
+ * A base class for the Android-specific barcode handlers. These allow the app to polymorphically
+ * suggest the appropriate actions for each data type.
+ *
+ * This class also contains a bunch of utility methods to take common actions like opening a URL.
+ * They could easily be moved into a helper object, but it can't be static because the Activity
+ * instance is needed to launch an intent.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+public abstract class ResultHandler {
+
+  private static final String TAG = ResultHandler.class.getSimpleName();
+
+  private static final String GOOGLE_SHOPPER_PACKAGE = "com.google.android.apps.shopper";
+  private static final String GOOGLE_SHOPPER_ACTIVITY = GOOGLE_SHOPPER_PACKAGE +
+      ".results.SearchResultsActivity";
+  private static final String MARKET_URI_PREFIX = "market://details?id=";
+  private static final String MARKET_REFERRER_SUFFIX =
+      "&referrer=utm_source%3Dbarcodescanner%26utm_medium%3Dapps%26utm_campaign%3Dscan";
+
+  private static final String[] EMAIL_TYPE_STRINGS = {"home", "work", "mobile"};
+  private static final String[] PHONE_TYPE_STRINGS = {"home", "work", "mobile", "fax", "pager", "main"};
+  private static final String[] ADDRESS_TYPE_STRINGS = {"home", "work"};
+  private static final int[] EMAIL_TYPE_VALUES = {
+      ContactsContract.CommonDataKinds.Email.TYPE_HOME,
+      ContactsContract.CommonDataKinds.Email.TYPE_WORK,
+      ContactsContract.CommonDataKinds.Email.TYPE_MOBILE,
+  };
+  private static final int[] PHONE_TYPE_VALUES = {
+      ContactsContract.CommonDataKinds.Phone.TYPE_HOME,
+      ContactsContract.CommonDataKinds.Phone.TYPE_WORK,
+      ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE,
+      ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK,
+      ContactsContract.CommonDataKinds.Phone.TYPE_PAGER,
+      ContactsContract.CommonDataKinds.Phone.TYPE_MAIN,
+  };
+  private static final int[] ADDRESS_TYPE_VALUES = {
+      ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME,
+      ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK,
+  };
+  private static final int NO_TYPE = -1;
+
+  public static final int MAX_BUTTON_COUNT = 4;
+
+  private final ParsedResult result;
+  private final Activity activity;
+  private final Result rawResult;
+
+  private final DialogInterface.OnClickListener shopperMarketListener =
+      new DialogInterface.OnClickListener() {
+    @Override
+    public void onClick(DialogInterface dialogInterface, int which) {
+      launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(MARKET_URI_PREFIX +
+          GOOGLE_SHOPPER_PACKAGE + MARKET_REFERRER_SUFFIX)));
+    }
+  };
+
+  public ResultHandler(Activity activity, ParsedResult result) {
+    this(activity, result, null);
+  }
+
+  public ResultHandler(Activity activity, ParsedResult result, Result rawResult) {
+    this.result = result;
+    this.activity = activity;
+    this.rawResult = rawResult;
+  }
+
+  public ParsedResult getResult() {
+    return result;
+  }
+
+  Activity getActivity() {
+    return activity;
+  }
+
+  /**
+   * Indicates how many buttons the derived class wants shown.
+   *
+   * @return The integer button count.
+   */
+  public abstract int getButtonCount();
+
+  /**
+   * The text of the nth action button.
+   *
+   * @param index From 0 to getButtonCount() - 1
+   * @return The button text as a resource ID
+   */
+  public abstract int getButtonText(int index);
+
+
+  /**
+   * Execute the action which corresponds to the nth button.
+   *
+   * @param index The button that was clicked.
+   */
+  public abstract void handleButtonPress(int index);
+
+  /**
+   * Some barcode contents are considered secure, and should not be saved to history, copied to
+   * the clipboard, or otherwise persisted.
+   *
+   * @return If true, do not create any permanent record of these contents.
+   */
+  public boolean areContentsSecure() {
+    return false;
+  }
+
+
+  /**
+   * Create a possibly styled string for the contents of the current barcode.
+   *
+   * @return The text to be displayed.
+   */
+  public CharSequence getDisplayContents() {
+    String contents = result.getDisplayResult();
+    return contents.replace("\r", "");
+  }
+
+  /**
+   * A string describing the kind of barcode that was found, e.g. "Found contact info".
+   *
+   * @return The resource ID of the string.
+   */
+  public abstract int getDisplayTitle();
+
+  /**
+   * A convenience method to get the parsed type. Should not be overridden.
+   *
+   * @return The parsed type, e.g. URI or ISBN
+   */
+  public final ParsedResultType getType() {
+    return result.getType();
+  }
+
+  final void addPhoneOnlyContact(String[] phoneNumbers,String[] phoneTypes) {
+    addContact(null, null, phoneNumbers, phoneTypes, null, null, null, null, null, null, null, null, null, null);
+  }
+
+  final void addEmailOnlyContact(String[] emails, String[] emailTypes) {
+    addContact(null, null, null, null, emails, emailTypes, null, null, null, null, null, null, null, null);
+  }
+
+  final void addContact(String[] names,
+                        String pronunciation,
+                        String[] phoneNumbers,
+                        String[] phoneTypes,
+                        String[] emails,
+                        String[] emailTypes,
+                        String note,
+                        String instantMessenger,
+                        String address,
+                        String addressType,
+                        String org,
+                        String title,
+                        String url,
+                        String birthday) {
+
+    // Only use the first name in the array, if present.
+    Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT, ContactsContract.Contacts.CONTENT_URI);
+    intent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
+    putExtra(intent, ContactsContract.Intents.Insert.NAME, names != null ? names[0] : null);
+
+    putExtra(intent, ContactsContract.Intents.Insert.PHONETIC_NAME, pronunciation);
+
+    int phoneCount = Math.min(phoneNumbers != null ? phoneNumbers.length : 0, Contents.PHONE_KEYS.length);
+    for (int x = 0; x < phoneCount; x++) {
+      putExtra(intent, Contents.PHONE_KEYS[x], phoneNumbers[x]);
+      if (phoneTypes != null && x < phoneTypes.length) {
+        int type = toPhoneContractType(phoneTypes[x]);
+        if (type >= 0) {
+          intent.putExtra(Contents.PHONE_TYPE_KEYS[x], type);
+        }
+      }
+    }
+
+    int emailCount = Math.min(emails != null ? emails.length : 0, Contents.EMAIL_KEYS.length);
+    for (int x = 0; x < emailCount; x++) {
+      putExtra(intent, Contents.EMAIL_KEYS[x], emails[x]);
+      if (emailTypes != null && x < emailTypes.length) {
+        int type = toEmailContractType(emailTypes[x]);
+        if (type >= 0) {
+          intent.putExtra(Contents.EMAIL_TYPE_KEYS[x], type);
+        }
+      }
+    }
+
+    // No field for URL, birthday; use notes
+    StringBuilder aggregatedNotes = new StringBuilder();
+    for (String aNote : new String[] { url, birthday, note }) {
+      if (aNote != null) {
+        if (aggregatedNotes.length() > 0) {
+          aggregatedNotes.append('\n');
+        }
+        aggregatedNotes.append(aNote);
+      }
+    }
+    if (aggregatedNotes.length() > 0) {
+      putExtra(intent, ContactsContract.Intents.Insert.NOTES, aggregatedNotes.toString());
+    }
+    
+    putExtra(intent, ContactsContract.Intents.Insert.IM_HANDLE, instantMessenger);
+    putExtra(intent, ContactsContract.Intents.Insert.POSTAL, address);
+    if (addressType != null) {
+      int type = toAddressContractType(addressType);
+      if (type >= 0) {
+        intent.putExtra(ContactsContract.Intents.Insert.POSTAL_TYPE, type);
+      }
+    }
+    putExtra(intent, ContactsContract.Intents.Insert.COMPANY, org);
+    putExtra(intent, ContactsContract.Intents.Insert.JOB_TITLE, title);
+    launchIntent(intent);
+  }
+
+  private static int toEmailContractType(String typeString) {
+    return doToContractType(typeString, EMAIL_TYPE_STRINGS, EMAIL_TYPE_VALUES);
+  }
+
+  private static int toPhoneContractType(String typeString) {
+    return doToContractType(typeString, PHONE_TYPE_STRINGS, PHONE_TYPE_VALUES);
+  }
+
+  private static int toAddressContractType(String typeString) {
+    return doToContractType(typeString, ADDRESS_TYPE_STRINGS, ADDRESS_TYPE_VALUES);
+  }
+
+  private static int doToContractType(String typeString, String[] types, int[] values) {
+    if (typeString == null) {
+      return NO_TYPE;
+    }
+    for (int i = 0; i < types.length; i++) {
+      String type = types[i];
+      if (typeString.startsWith(type) || typeString.startsWith(type.toUpperCase(Locale.ENGLISH))) {
+        return values[i];
+      }
+    }
+    return NO_TYPE;
+  }
+
+  final void shareByEmail(String contents) {
+    sendEmailFromUri("mailto:", null, activity.getString(R.string.msg_share_subject_line),
+        contents);
+  }
+
+  final void sendEmail(String address, String subject, String body) {
+    sendEmailFromUri("mailto:" + address, address, subject, body);
+  }
+
+  // Use public Intent fields rather than private GMail app fields to specify subject and body.
+  final void sendEmailFromUri(String uri, String email, String subject, String body) {
+    Intent intent = new Intent(Intent.ACTION_SEND, Uri.parse(uri));
+    if (email != null) {
+      intent.putExtra(Intent.EXTRA_EMAIL, new String[] {email});
+    }
+    putExtra(intent, Intent.EXTRA_SUBJECT, subject);
+    putExtra(intent, Intent.EXTRA_TEXT, body);
+    intent.setType("text/plain");
+    launchIntent(intent);
+  }
+
+  final void shareBySMS(String contents) {
+    sendSMSFromUri("smsto:", activity.getString(R.string.msg_share_subject_line) + ":\n" +
+        contents);
+  }
+
+  final void sendSMS(String phoneNumber, String body) {
+    sendSMSFromUri("smsto:" + phoneNumber, body);
+  }
+
+  final void sendSMSFromUri(String uri, String body) {
+    Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
+    putExtra(intent, "sms_body", body);
+    // Exit the app once the SMS is sent
+    intent.putExtra("compose_mode", true);
+    launchIntent(intent);
+  }
+
+  final void sendMMS(String phoneNumber, String subject, String body) {
+    sendMMSFromUri("mmsto:" + phoneNumber, subject, body);
+  }
+
+  final void sendMMSFromUri(String uri, String subject, String body) {
+    Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(uri));
+    // The Messaging app needs to see a valid subject or else it will treat this an an SMS.
+    if (subject == null || subject.length() == 0) {
+      putExtra(intent, "subject", activity.getString(R.string.msg_default_mms_subject));
+    } else {
+      putExtra(intent, "subject", subject);
+    }
+    putExtra(intent, "sms_body", body);
+    intent.putExtra("compose_mode", true);
+    launchIntent(intent);
+  }
+
+  final void dialPhone(String phoneNumber) {
+    launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneNumber)));
+  }
+
+  final void dialPhoneFromUri(String uri) {
+    launchIntent(new Intent(Intent.ACTION_DIAL, Uri.parse(uri)));
+  }
+
+  final void openMap(String geoURI) {
+    launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse(geoURI)));
+  }
+
+  /**
+   * Do a geo search using the address as the query.
+   *
+   * @param address The address to find
+   * @param title An optional title, e.g. the name of the business at this address
+   */
+  final void searchMap(String address, CharSequence title) {
+    String query = address;
+    if (title != null && title.length() > 0) {
+      query += " (" + title + ')';
+    }
+    launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" + Uri.encode(query))));
+  }
+
+  final void getDirections(double latitude, double longitude) {
+    launchIntent(new Intent(Intent.ACTION_VIEW, Uri.parse("http://maps.google." +
+        LocaleManager.getCountryTLD(activity) + "/maps?f=d&daddr=" + latitude + ',' + longitude)));
+  }
+
+  // Uses the mobile-specific version of Product Search, which is formatted for small screens.
+  final void openProductSearch(String upc) {
+    Uri uri = Uri.parse("http://www.google." + LocaleManager.getProductSearchCountryTLD(activity) +
+        "/m/products?q=" + upc + "&source=zxing");
+    launchIntent(new Intent(Intent.ACTION_VIEW, uri));
+  }
+
+  final void openBookSearch(String isbn) {
+    Uri uri = Uri.parse("http://books.google." + LocaleManager.getBookSearchCountryTLD(activity) +
+        "/books?vid=isbn" + isbn);
+    launchIntent(new Intent(Intent.ACTION_VIEW, uri));
+  }
+
+  final void openURL(String url) {
+    // Strangely, some Android browsers don't seem to register to handle HTTP:// or HTTPS://.
+    // Lower-case these as it should always be OK to lower-case these schemes.
+    if (url.startsWith("HTTP://")) {
+      url = "http" + url.substring(4);
+    } else if (url.startsWith("HTTPS://")) {
+      url = "https" + url.substring(5);
+    }
+    Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+    try {
+      launchIntent(intent);
+    } catch (ActivityNotFoundException anfe) {
+      Log.w(TAG, "Nothing available to handle " + intent);
+    }
+  }
+
+  final void webSearch(String query) {
+    Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
+    intent.putExtra("query", query);
+    launchIntent(intent);
+  }
+
+  final void openGoogleShopper(String query) {
+
+    // Construct Intent to launch Shopper
+    Intent intent = new Intent(Intent.ACTION_SEARCH);
+    intent.setClassName(GOOGLE_SHOPPER_PACKAGE, GOOGLE_SHOPPER_ACTIVITY);
+    intent.putExtra(SearchManager.QUERY, query);
+
+    // Is it available?
+    PackageManager pm = activity.getPackageManager();
+    Collection<?> availableApps = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+
+    if (availableApps != null && !availableApps.isEmpty()) {
+      // If something can handle it, start it
+      activity.startActivity(intent);
+    } else {
+      // Otherwise offer to install it from Market.
+//      AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+//      builder.setTitle(R.string.msg_google_shopper_missing);
+//      builder.setMessage(R.string.msg_install_google_shopper);
+//      builder.setIcon(R.drawable.shopper_icon);
+//      builder.setPositiveButton(R.string.button_ok, shopperMarketListener);
+//      builder.setNegativeButton(R.string.button_cancel, null);
+//      builder.show();
+        Toast.makeText (activity, query, Toast.LENGTH_SHORT).show ();
+    }
+  }
+
+  /**
+   * Like {@link #launchIntent(Intent)} but will tell you if it is not handle-able
+   * via {@link ActivityNotFoundException}.
+   *
+   * @throws ActivityNotFoundException
+   */
+  void rawLaunchIntent(Intent intent) {
+    if (intent != null) {
+      intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
+      Log.d(TAG, "Launching intent: " + intent + " with extras: " + intent.getExtras());
+      activity.startActivity(intent);
+    }
+  }
+
+  /**
+   * Like {@link #rawLaunchIntent(Intent)} but will show a user dialog if nothing is available to handle.
+   */
+  void launchIntent(Intent intent) {
+    try {
+      rawLaunchIntent(intent);
+    } catch (ActivityNotFoundException e) {
+      AlertDialog.Builder builder = new AlertDialog.Builder(activity);
+      builder.setTitle(R.string.app_name);
+      builder.setMessage(R.string.msg_intent_failed);
+      builder.setPositiveButton(R.string.button_ok, null);
+      builder.show();
+    }
+  }
+
+  private static void putExtra(Intent intent, String key, String value) {
+    if (value != null && value.length() > 0) {
+      intent.putExtra(key, value);
+    }
+  }
+
+  private String parseCustomSearchURL() {
+//    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
+//    String customProductSearch = prefs.getString(PreferencesActivity.KEY_CUSTOM_PRODUCT_SEARCH,
+//        null);
+//    if (customProductSearch != null && customProductSearch.trim().length() == 0) {
+//      return null;
+//    }
+    return null;
+  }
+
+//  String fillInCustomSearchURL(String text) {
+//    if (customProductSearch == null) {
+//      return text; // ?
+//    }
+//    String url = customProductSearch.replace("%s", text);
+//    if (rawResult != null) {
+//      url = url.replace("%f", rawResult.getBarcodeFormat().toString());
+//      if (url.contains("%t")) {
+//        ParsedResult parsedResultAgain = ResultParser.parseResult(rawResult);
+//        url = url.replace("%t", parsedResultAgain.getType().toString());
+//      }
+//    }
+//    return url;
+//  }
+
+}

+ 55 - 0
src/com/google/zxing/client/android/result/ResultHandlerFactory.java

@@ -0,0 +1,55 @@
+/*
+ * 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.result;
+
+import com.google.zxing.Result;
+import com.google.zxing.client.result.ParsedResult;
+import com.google.zxing.client.result.ResultParser;
+import com.libs.zxing.CaptureActivity;
+
+/**
+ * Manufactures Android-specific handlers based on the barcode content's type.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class ResultHandlerFactory {
+  private ResultHandlerFactory() {
+  }
+
+  public static ResultHandler makeResultHandler(CaptureActivity activity, Result rawResult) {
+    ParsedResult result = parseResult(rawResult);
+    switch (result.getType()) {
+     case URI:
+        return new URIResultHandler(activity, result);
+      case ADDRESSBOOK:
+      case EMAIL_ADDRESS:
+      case PRODUCT:
+      case WIFI:
+      case GEO:
+      case TEL:
+      case SMS:
+      case CALENDAR: 
+      case ISBN:
+      default:
+        return new DefaultResultHandler(activity, result, rawResult);
+    }
+  }
+
+  private static ParsedResult parseResult(Result rawResult) {
+    return ResultParser.parseResult(rawResult);
+  }
+}

+ 85 - 0
src/com/google/zxing/client/android/result/URIResultHandler.java

@@ -0,0 +1,85 @@
+/*
+ * 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.result;
+
+import com.google.zxing.client.result.ParsedResult;
+import com.google.zxing.client.result.URIParsedResult;
+import com.libs.zxing.R;
+
+import android.app.Activity;
+import android.widget.Toast;
+
+import java.util.Locale;
+
+/**
+ * Offers appropriate actions for URLS.
+ *
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+public final class URIResultHandler extends ResultHandler {
+  // URIs beginning with entries in this array will not be saved to history or copied to the
+  // clipboard for security.
+  private static final String[] SECURE_PROTOCOLS = {
+    "otpauth:"
+  };
+
+  public URIResultHandler(Activity activity, ParsedResult result) {
+    super(activity, result);
+  }
+
+  @Override
+  public int getButtonCount() {
+    return 1;
+  }
+
+  @Override
+  public int getButtonText(int index) {
+    return R.string.button_1;
+  }
+
+  @Override
+  public void handleButtonPress(int index) {
+    URIParsedResult uriResult = (URIParsedResult) getResult();
+    String uri = uriResult.getURI();
+    
+    //TODO 
+    if(uri.contains ("http://m.muzhiwan.com")){
+        Toast.makeText (getActivity (), uri, Toast.LENGTH_SHORT).show ();
+        return;
+    }
+    Toast.makeText (getActivity (), uri, Toast.LENGTH_SHORT).show ();
+    
+    openURL(uri);
+  }
+
+  @Override
+  public int getDisplayTitle() {
+    return R.string.result_uri;
+  }
+
+  @Override
+  public boolean areContentsSecure() {
+    URIParsedResult uriResult = (URIParsedResult) getResult();
+    String uri = uriResult.getURI().toLowerCase(Locale.ENGLISH);
+    for (String secure : SECURE_PROTOCOLS) {
+      if (uri.startsWith(secure)) {
+        return true;
+      }
+    }
+    return false;
+  }
+}

+ 332 - 0
src/com/libs/zxing/CaptureActivity.java

@@ -0,0 +1,332 @@
+package com.libs.zxing;
+
+import java.io.IOException;
+import java.util.Collection;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.Toast;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.Result;
+import com.google.zxing.ResultPoint;
+import com.google.zxing.client.android.camera.CameraManager;
+import com.google.zxing.client.android.decode.BeepManager;
+import com.google.zxing.client.android.decode.CaptureActivityHandler;
+import com.google.zxing.client.android.decode.InactivityTimer;
+import com.google.zxing.client.android.decode.ViewfinderView;
+import com.google.zxing.client.android.result.ResultHandler;
+import com.google.zxing.client.android.result.ResultHandlerFactory;
+
+public final class CaptureActivity extends Activity implements
+		SurfaceHolder.Callback {
+
+	private static final String TAG = CaptureActivity.class.getSimpleName();
+
+	private CameraManager cameraManager;
+	private CaptureActivityHandler handler;
+	private Result savedResultToShow;
+	private ViewfinderView viewfinderView;
+	private boolean hasSurface;
+	private Collection<BarcodeFormat> decodeFormats;
+	private InactivityTimer inactivityTimer;
+	private String characterSet;
+	private BeepManager beepManager;
+
+	final static String profix1 = "?appid=";
+	final static String profix2 = "-title=";
+	final static String action = "muzhiwan.action.detail";
+	final static String bundle_key = "detail";
+
+	ImageView opreateView;
+
+	ViewfinderView getViewfinderView() {
+		return viewfinderView;
+	}
+
+	public Handler getHandler() {
+		return handler;
+	}
+
+	public CameraManager getCameraManager() {
+		return cameraManager;
+	}
+
+	@Override
+	public void onCreate(Bundle icicle) {
+		super.onCreate(icicle);
+
+		Window window = getWindow();
+		window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+		setContentView(R.layout.capture);
+
+		hasSurface = false;
+		inactivityTimer = new InactivityTimer(this);
+		beepManager = new BeepManager(this);
+		opreateView = (ImageView) findViewById(R.id.button_openorcloseClick);
+
+		opreateView.setOnClickListener(new OnClickListener() {
+
+			@Override
+			public void onClick(View v) {
+				if (cameraManager != null) {
+					Config.KEY_FRONT_LIGHT = !Config.KEY_FRONT_LIGHT;
+					if (Config.KEY_FRONT_LIGHT == true) {
+						opreateView
+								.setImageResource(R.drawable.mzw_camera_close);
+					} else {
+						opreateView
+								.setImageResource(R.drawable.mzw_camera_open);
+					}
+					cameraManager.getConfigManager().initializeTorch(
+							cameraManager.getCamera().getParameters(), false);
+					onPause();
+					onResume();
+				}
+			}
+		});
+	}
+
+	@Override
+	protected void onResume() {
+		super.onResume();
+
+		// CameraManager must be initialized here, not in onCreate(). This is
+		// necessary because we don't
+		// want to open the camera driver and measure the screen size if we're
+		// going to show the help on
+		// first launch. That led to bugs where the scanning rectangle was the
+		// wrong size and partially
+		// off screen.
+		cameraManager = new CameraManager(getApplication());
+
+		viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
+		viewfinderView.setCameraManager(cameraManager);
+
+		handler = null;
+		SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
+		SurfaceHolder surfaceHolder = surfaceView.getHolder();
+		if (hasSurface) {
+			// The activity was paused but not stopped, so the surface still
+			// exists. Therefore
+			// surfaceCreated() won't be called, so init the camera here.
+			initCamera(surfaceHolder);
+		} else {
+			// Install the callback and wait for surfaceCreated() to init the
+			// camera.
+			surfaceHolder.addCallback(this);
+			surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+		}
+
+		beepManager.updatePrefs();
+
+		inactivityTimer.onResume();
+	}
+
+	@Override
+	protected void onPause() {
+		if (handler != null) {
+			handler.quitSynchronously();
+			handler = null;
+		}
+		inactivityTimer.onPause();
+		cameraManager.closeDriver();
+		if (!hasSurface) {
+			SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
+			SurfaceHolder surfaceHolder = surfaceView.getHolder();
+			surfaceHolder.removeCallback(this);
+		}
+		super.onPause();
+	}
+
+	@Override
+	protected void onDestroy() {
+		inactivityTimer.shutdown();
+		super.onDestroy();
+	}
+
+	@Override
+	public boolean onKeyDown(int keyCode, KeyEvent event) {
+		switch (keyCode) {
+		case KeyEvent.KEYCODE_BACK:
+			// restartPreviewAfterDelay(0L);
+			return super.onKeyDown(keyCode, event);
+		case KeyEvent.KEYCODE_FOCUS:
+		case KeyEvent.KEYCODE_CAMERA:
+			// Handle these events so they don't launch the Camera app
+			return true;
+			// Use volume up/down to turn on light
+		case KeyEvent.KEYCODE_VOLUME_DOWN:
+			cameraManager.setTorch(false);
+			return true;
+		case KeyEvent.KEYCODE_VOLUME_UP:
+			cameraManager.setTorch(true);
+			return true;
+		}
+		return super.onKeyDown(keyCode, event);
+	}
+
+	private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
+		// Bitmap isn't used yet -- will be used soon
+		if (handler == null) {
+			savedResultToShow = result;
+		} else {
+			if (result != null) {
+				savedResultToShow = result;
+			}
+			if (savedResultToShow != null) {
+				Message message = Message.obtain(handler,
+						R.id.decode_succeeded, savedResultToShow);
+				handler.sendMessage(message);
+			}
+			savedResultToShow = null;
+		}
+	}
+
+	@Override
+	public void surfaceCreated(SurfaceHolder holder) {
+		if (holder == null) {
+			Log.e(TAG,
+					"*** WARNING *** surfaceCreated() gave us a null surface!");
+		}
+		if (!hasSurface) {
+			hasSurface = true;
+			initCamera(holder);
+		}
+	}
+
+	@Override
+	public void surfaceDestroyed(SurfaceHolder holder) {
+		hasSurface = false;
+	}
+
+	@Override
+	public void surfaceChanged(SurfaceHolder holder, int format, int width,
+			int height) {
+
+	}
+
+	/**
+	 * A valid barcode has been found, so give an indication of success and show
+	 * the results.
+	 * 
+	 * @param rawResult
+	 *            The contents of the barcode.
+	 * @param barcode
+	 *            A greyscale bitmap of the camera data which was decoded.
+	 */
+	public void handleDecode(Result rawResult, Bitmap barcode) {
+		inactivityTimer.onActivity();
+		ResultHandler resultHandler = ResultHandlerFactory.makeResultHandler(
+				this, rawResult);
+
+		boolean fromLiveScan = barcode != null;
+		if (fromLiveScan) {
+			// Then not from history, so beep/vibrate and we have an image to
+			// draw on
+			beepManager.playBeepSoundAndVibrate();
+			// drawResultPoints(barcode, rawResult);
+			viewfinderView.drawResultBitmap(barcode);
+		}
+
+		String text = rawResult.getText();
+
+		Toast.makeText(this, "扫描结果:" + text, Toast.LENGTH_LONG).show();
+		Log.d(TAG, "result-->" + text);
+	}
+
+	/**
+	 * Superimpose a line for 1D or dots for 2D to highlight the key features of
+	 * the barcode.
+	 * 
+	 * @param barcode
+	 *            A bitmap of the captured image.
+	 * @param rawResult
+	 *            The decoded results which contains the points to draw.
+	 */
+	private void drawResultPoints(Bitmap barcode, Result rawResult) {
+		ResultPoint[] points = rawResult.getResultPoints();
+		if (points != null && points.length > 0) {
+			Canvas canvas = new Canvas(barcode);
+			Paint paint = new Paint();
+			paint.setColor(getResources().getColor(R.color.result_points));
+			if (points.length == 2) {
+				paint.setStrokeWidth(4.0f);
+				drawLine(canvas, paint, points[0], points[1]);
+			} else if (points.length == 4
+					&& (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A || rawResult
+							.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
+				// Hacky special case -- draw two lines, for the barcode and
+				// metadata
+				drawLine(canvas, paint, points[0], points[1]);
+				drawLine(canvas, paint, points[2], points[3]);
+			} else {
+				paint.setStrokeWidth(10.0f);
+				for (ResultPoint point : points) {
+					canvas.drawPoint(point.getX(), point.getY(), paint);
+				}
+			}
+		}
+	}
+
+	private static void drawLine(Canvas canvas, Paint paint, ResultPoint a,
+			ResultPoint b) {
+		canvas.drawLine(a.getX(), a.getY(), b.getX(), b.getY(), paint);
+	}
+
+	private void initCamera(SurfaceHolder surfaceHolder) {
+		if (surfaceHolder == null) {
+			throw new IllegalStateException("No SurfaceHolder provided");
+		}
+		if (cameraManager.isOpen()) {
+			Log.w(TAG,
+					"initCamera() while already open -- late SurfaceView callback?");
+			return;
+		}
+		try {
+			cameraManager.openDriver(surfaceHolder);
+			// Creating the handler starts the preview, which can also throw a
+			// RuntimeException.
+			if (handler == null) {
+				handler = new CaptureActivityHandler(this, viewfinderView,
+						decodeFormats, characterSet, cameraManager);
+			}
+			decodeOrStoreSavedBitmap(null, null);
+		} catch (IOException ioe) {
+			Log.w(TAG, ioe);
+			Toast.makeText(this, R.string.camera_problem, Toast.LENGTH_SHORT)
+					.show();
+			finish();
+		} catch (RuntimeException e) {
+			// Barcode Scanner has seen crashes in the wild of this variety:
+			// java.?lang.?RuntimeException: Fail to connect to camera service
+			Log.w(TAG, "Unexpected error initializing camera", e);
+			Toast.makeText(this, R.string.framwork_problem, Toast.LENGTH_SHORT)
+					.show();
+		}
+	}
+
+	public void restartPreviewAfterDelay(long delayMS) {
+		if (handler != null) {
+			handler.sendEmptyMessageDelayed(R.id.restart_preview, delayMS);
+		}
+	}
+
+	public void drawViewfinder() {
+		viewfinderView.drawViewfinder();
+	}
+}

+ 24 - 0
src/com/libs/zxing/Config.java

@@ -0,0 +1,24 @@
+package com.libs.zxing;
+
+
+public class Config
+{
+    /** 自动对焦 */
+    public static final boolean AOTO_FOCUS = true;
+    
+    /** 前置灯光 */
+    public static boolean KEY_FRONT_LIGHT = false;
+    
+    /** 摄像头配置 Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE in 4.0+ */
+    public static final boolean KEY_DISABLE_CONTINUOUS_FOCUS = true;
+    
+    /** 解码器 */
+    public static final boolean KEY_DECODE_1D = false;
+    public static final boolean KEY_DECODE_QR = false;
+    public static final boolean KEY_DECODE_DATA_MATRIX = false;
+    
+    /** 扫描到结果后是否震动,默认为false,声音默认为true */
+    public static final boolean KEY_PLAY_BEEP = false;
+    public static final boolean KEY_VIBRATE = true;
+    
+}