詹子聪 5 yıl önce
işleme
75a805f757
100 değiştirilmiş dosya ile 4747 ekleme ve 0 silme
  1. 37 0
      .gitignore
  2. 21 0
      CHANGELOG.md
  3. 13 0
      LICENSE
  4. 398 0
      README.md
  5. 118 0
      build.gradle
  6. 13 0
      core/build.gradle
  7. 5 0
      core/src/main/AndroidManifest.xml
  8. 205 0
      core/src/main/java/me/dm7/barcodescanner/core/BarcodeScannerView.java
  9. 37 0
      core/src/main/java/me/dm7/barcodescanner/core/CameraHandlerThread.java
  10. 308 0
      core/src/main/java/me/dm7/barcodescanner/core/CameraPreview.java
  11. 63 0
      core/src/main/java/me/dm7/barcodescanner/core/CameraUtils.java
  12. 25 0
      core/src/main/java/me/dm7/barcodescanner/core/CameraWrapper.java
  13. 41 0
      core/src/main/java/me/dm7/barcodescanner/core/DisplayUtils.java
  14. 40 0
      core/src/main/java/me/dm7/barcodescanner/core/IViewFinder.java
  15. 197 0
      core/src/main/java/me/dm7/barcodescanner/core/ViewFinderView.java
  16. 5 0
      core/src/main/res/values-hdpi/strings.xml
  17. 5 0
      core/src/main/res/values-xhdpi/strings.xml
  18. 5 0
      core/src/main/res/values-xxhdpi/strings.xml
  19. 6 0
      core/src/main/res/values/attrs.xml
  20. 6 0
      core/src/main/res/values/colors.xml
  21. 4 0
      core/src/main/res/values/strings.xml
  22. 19 0
      dependencies.gradle
  23. BIN
      gradle/wrapper/gradle-wrapper.jar
  24. 6 0
      gradle/wrapper/gradle-wrapper.properties
  25. 172 0
      gradlew
  26. 84 0
      gradlew.bat
  27. 2 0
      settings.gradle
  28. 115 0
      vision-sample/README.md
  29. 16 0
      vision-sample/build.gradle
  30. 50 0
      vision-sample/src/main/AndroidManifest.xml
  31. 28 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/BaseScannerActivity.java
  32. 87 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/CameraSelectorDialogFragment.java
  33. 94 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FormatSelectorDialogFragment.java
  34. 217 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerActivity.java
  35. 220 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerFragment.java
  36. 12 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerFragmentActivity.java
  37. 71 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/MainActivity.java
  38. 47 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/MessageDialogFragment.java
  39. 48 0
      vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/SimpleScannerActivity.java
  40. BIN
      vision-sample/src/main/res/drawable-hdpi/ic_launcher.png
  41. BIN
      vision-sample/src/main/res/drawable-mdpi/ic_launcher.png
  42. BIN
      vision-sample/src/main/res/drawable-xhdpi/ic_launcher.png
  43. BIN
      vision-sample/src/main/res/drawable-xxhdpi/ic_launcher.png
  44. 21 0
      vision-sample/src/main/res/layout/activity_full_scanner.xml
  45. 29 0
      vision-sample/src/main/res/layout/activity_full_scanner_fragment.xml
  46. 51 0
      vision-sample/src/main/res/layout/activity_main.xml
  47. 12 0
      vision-sample/src/main/res/values/colors.xml
  48. 7 0
      vision-sample/src/main/res/values/ids.xml
  49. 19 0
      vision-sample/src/main/res/values/strings.xml
  50. 36 0
      vision-sample/src/main/res/values/styles.xml
  51. 1 0
      vision/.gitignore
  52. 14 0
      vision/build.gradle
  53. 8 0
      vision/src/main/AndroidManifest.xml
  54. 65 0
      vision/src/main/java/me/dm7/barcodescanner/vision/BarcodeFormat.java
  55. 35 0
      vision/src/main/java/me/dm7/barcodescanner/vision/Result.java
  56. 175 0
      vision/src/main/java/me/dm7/barcodescanner/vision/VisionScannerView.java
  57. 16 0
      zbar-sample/build.gradle
  58. 44 0
      zbar-sample/src/main/AndroidManifest.xml
  59. 28 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/BaseScannerActivity.java
  60. 87 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/CameraSelectorDialogFragment.java
  61. 94 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FormatSelectorDialogFragment.java
  62. 216 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerActivity.java
  63. 218 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerFragment.java
  64. 12 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerFragmentActivity.java
  65. 73 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/MainActivity.java
  66. 47 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/MessageDialogFragment.java
  67. 53 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerActivity.java
  68. 52 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerFragment.java
  69. 12 0
      zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerFragmentActivity.java
  70. BIN
      zbar-sample/src/main/res/drawable-hdpi/ic_launcher.png
  71. BIN
      zbar-sample/src/main/res/drawable-mdpi/ic_launcher.png
  72. BIN
      zbar-sample/src/main/res/drawable-xhdpi/ic_launcher.png
  73. BIN
      zbar-sample/src/main/res/drawable-xxhdpi/ic_launcher.png
  74. 22 0
      zbar-sample/src/main/res/layout/activity_full_scanner.xml
  75. 23 0
      zbar-sample/src/main/res/layout/activity_full_scanner_fragment.xml
  76. 42 0
      zbar-sample/src/main/res/layout/activity_main.xml
  77. 22 0
      zbar-sample/src/main/res/layout/activity_simple_scanner.xml
  78. 23 0
      zbar-sample/src/main/res/layout/activity_simple_scanner_fragment.xml
  79. 12 0
      zbar-sample/src/main/res/values/colors.xml
  80. 7 0
      zbar-sample/src/main/res/values/ids.xml
  81. 21 0
      zbar-sample/src/main/res/values/strings.xml
  82. 36 0
      zbar-sample/src/main/res/values/styles.xml
  83. 19 0
      zbar/build.gradle
  84. BIN
      zbar/libs/zbar.jar
  85. 5 0
      zbar/src/main/AndroidManifest.xml
  86. 72 0
      zbar/src/main/java/me/dm7/barcodescanner/zbar/BarcodeFormat.java
  87. 22 0
      zbar/src/main/java/me/dm7/barcodescanner/zbar/Result.java
  88. 156 0
      zbar/src/main/java/me/dm7/barcodescanner/zbar/ZBarScannerView.java
  89. BIN
      zbar/src/main/jniLibs/arm64-v8a/libiconv.so
  90. BIN
      zbar/src/main/jniLibs/arm64-v8a/libzbarjni.so
  91. BIN
      zbar/src/main/jniLibs/armeabi-v7a/libiconv.so
  92. BIN
      zbar/src/main/jniLibs/armeabi-v7a/libzbarjni.so
  93. BIN
      zbar/src/main/jniLibs/armeabi/libiconv.so
  94. BIN
      zbar/src/main/jniLibs/armeabi/libzbarjni.so
  95. BIN
      zbar/src/main/jniLibs/mips/libiconv.so
  96. BIN
      zbar/src/main/jniLibs/mips/libzbarjni.so
  97. BIN
      zbar/src/main/jniLibs/mips64/libiconv.so
  98. BIN
      zbar/src/main/jniLibs/mips64/libzbarjni.so
  99. BIN
      zbar/src/main/jniLibs/x86/libiconv.so
  100. 0 0
      zbar/src/main/jniLibs/x86/libzbarjni.so

+ 37 - 0
.gitignore

@@ -0,0 +1,37 @@
+# built application files
+*.apk
+*.ap_
+
+# files for the dex VM
+*.dex
+
+# Java class files
+*.class
+
+# generated files
+bin/
+gen/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+gradle.properties
+
+# Eclipse project files
+.classpath
+.project
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Intellij project files
+*.iml
+*.ipr
+*.iws
+.idea/
+
+#Gradle folders
+.gradle
+
+# Stupid MAC OSX files
+.DS_Store

+ 21 - 0
CHANGELOG.md

@@ -0,0 +1,21 @@
+# Change Log
+
+## [1.9.2] - May 13, 2017
+* Add support for AZTEC codes in ZXing: #299, #288.
+
+## [1.9.1] - April 8, 2017
+* Add ability to scan inverted/negative codes with ZXing. Thanks to @manijshrestha for this pull request #265
+
+## [1.9] - July 25, 2016
+* Scale camera preview when the view size isn't full screen. Thanks to @xolan for this pull request PR #219
+* Fix inverted camera in devices with differently oriented back and forward facing cameras. Thanks to @thadcodes for PR #191
+* Add ability switch view finder view to square. Thanks to @squeeish for PR #163
+
+## [1.8.4] - Dec 30, 2015
+* Improve performance by opening camera and handling preview frames in a separate HandlerThread (#1, #99)
+* Do not automatically stopCamera after a result is found #115
+* Update samples to use Material Theme and make sure all samples use the FullScreen theme
+* Update gradle wrapper to v2.10, gradle plugin to v1.5.0, buildToolsVersion to v23.0.2 and targetSdkVersion 23
+
+## [1.8.3] - October 3, 2015
+* Rebuild ZBar libraries with position independent code (#123,#119,#94).

+ 13 - 0
LICENSE

@@ -0,0 +1,13 @@
+Copyright (c) 2014 Dushyanth Maguluru
+
+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.

+ 398 - 0
README.md

@@ -0,0 +1,398 @@
+原仓库:
+https://github.com/dm77/barcodescanner/tree/TonyTangAndroid-master
+
+Introduction
+============
+
+Android library projects that provides easy to use and extensible Barcode Scanner views based on ZXing and ZBar.
+
+Screenshots
+===========
+<img src="https://raw.github.com/dm77/barcodescanner/master/screenshots/main_activity.png" width="266">
+<img src="https://raw.github.com/dm77/barcodescanner/master/screenshots/scanner.png" width="266">
+<img src="https://raw.github.com/dm77/barcodescanner/master/screenshots/scan_results.png" width="266">
+
+
+Minor BREAKING CHANGE in 1.8.4
+==============================
+Version 1.8.4 introduces a couple of new changes:
+
+* Open Camera and handle preview frames in a separate HandlerThread (#1, #99): Though this has worked fine in my testing on 3 devices, I would advise you to test on your own devices before blindly releasing apps with this version. If you run into any issues please file a bug report.
+* Do not automatically stopCamera after a result is found #115: This means that upon a successful scan only the cameraPreview is stopped but the camera is not released. So previously if your code was calling mScannerView.startCamera() in the handleResult() method, please replace that with a call to mScannerView.resumeCameraPreview(this);
+
+
+Google Mobile Vision
+=====
+
+Installation
+------------
+
+Add the following dependency to your build.gradle file.
+
+`compile 'me.dm7.barcodescanner:vision:1.9'`
+
+Simple Usage
+------------
+
+1.) Add camera permission to your AndroidManifest.xml file:
+
+```xml
+<uses-permission android:name="android.permission.CAMERA" />
+```
+
+2.) A very basic activity would look like this:
+
+```java
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+import me.dm7.barcodescanner.vision.Result;
+import me.dm7.barcodescanner.vision.VisionScannerView;
+
+public class SimpleScannerActivity extends Activity implements VisionScannerView.ResultHandler {
+    private static final String TAG = "SimpleScannerActivity";
+
+    private VisionScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        mScannerView = new VisionScannerView(this);   // Programmatically initialize the scanner view
+        setContentView(mScannerView);                // Set the scanner view as the content view
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
+        mScannerView.startCamera();          // Start camera on resume
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();           // Stop camera on pause
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        // Do something with the result here
+        Log.v(TAG, rawResult.getBarcode().displayValue); // Prints scan results
+        Log.v(TAG, BarcodeFormat.getFormatById(rawResult.getBarcode().format).toString()); // Prints the scan format (qrcode, pdf417 etc.)
+
+        // If you would like to resume scanning, call this method below:
+        mScannerView.resumeCameraPreview(this);
+
+        Toast.makeText(SimpleScannerActivity.this, rawResult.getBarcode().displayValue, Toast.LENGTH_SHORT).show();
+    }
+}
+
+```
+
+Please take a look at the [vision-sample] (https://github.com/dm77/barcodescanner/tree/master/vision-sample) project for a full working example.
+
+Advanced Usage
+--------------
+
+Take a look at the [FullScannerActivity.java] (https://github.com/dm77/barcodescanner/blob/master/vision/sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerActivity.java) classes to get an idea on advanced usage.
+
+Interesting methods on the VisionScannerView include:
+
+```java
+// Toggle flash:
+void setFlash(boolean);
+
+// Toogle autofocus:
+void setAutoFocus(boolean);
+
+// Specify interested barcode formats:
+void setFormats(List<BarcodeFormat> formats);
+
+// Specify the cameraId to start with:
+void startCamera(int cameraId);
+```
+
+Specify front-facing or rear-facing cameras by using the `void startCamera(int cameraId);` method.
+
+Supported Formats:
+
+```java
+     BarcodeFormat.CODE_128
+     BarcodeFormat.CODE_39
+     BarcodeFormat.CODE_93
+     BarcodeFormat.CODABAR
+     BarcodeFormat.DATA_MATRIX
+     BarcodeFormat.EAN_13
+     BarcodeFormat.EAN_8
+     BarcodeFormat.ITF
+     BarcodeFormat.QR_CODE
+     BarcodeFormat.UPC_A
+     BarcodeFormat.UPC_E
+     BarcodeFormat.PDF417
+     BarcodeFormat.AZTEC
+```
+
+
+ZXing
+=====
+
+Installation
+------------
+
+Add the following dependency to your build.gradle file.
+
+`compile 'me.dm7.barcodescanner:zxing:1.9.2'`
+
+Simple Usage
+------------
+
+1.) Add camera permission to your AndroidManifest.xml file:
+
+```xml
+<uses-permission android:name="android.permission.CAMERA" />
+```
+
+2.) A very basic activity would look like this:
+
+```java
+public class SimpleScannerActivity extends Activity implements ZXingScannerView.ResultHandler {
+    private ZXingScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        mScannerView = new ZXingScannerView(this);   // Programmatically initialize the scanner view
+        setContentView(mScannerView);                // Set the scanner view as the content view
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
+        mScannerView.startCamera();          // Start camera on resume
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();           // Stop camera on pause
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        // Do something with the result here
+        Log.v(TAG, rawResult.getText()); // Prints scan results
+        Log.v(TAG, rawResult.getBarcodeFormat().toString()); // Prints the scan format (qrcode, pdf417 etc.)
+
+        // If you would like to resume scanning, call this method below:
+        mScannerView.resumeCameraPreview(this);
+    }
+}
+
+```
+
+Please take a look at the [zxing-sample] (https://github.com/dm77/barcodescanner/tree/master/zxing-sample) project for a full working example.
+
+Advanced Usage
+--------------
+
+Take a look at the [ScannerActivity.java] (https://github.com/dm77/barcodescanner/blob/master/zxing/sample/src/main/java/me/dm7/barcodescanner/zxing/sample/ScannerActivity.java) or [ScannerFragment.java] (https://github.com/dm77/barcodescanner/blob/master/zxing/sample/src/main/java/me/dm7/barcodescanner/zxing/sample/ScannerFragment.java) classes to get an idea on advanced usage.
+
+Interesting methods on the ZXingScannerView include:
+
+```java
+// Toggle flash:
+void setFlash(boolean);
+
+// Toogle autofocus:
+void setAutoFocus(boolean);
+
+// Specify interested barcode formats:
+void setFormats(List<BarcodeFormat> formats);
+
+// Specify the cameraId to start with:
+void startCamera(int cameraId);
+```
+
+Specify front-facing or rear-facing cameras by using the `void startCamera(int cameraId);` method.
+
+Supported Formats:
+
+```java
+BarcodeFormat.UPC_A
+BarcodeFormat.UPC_E
+BarcodeFormat.EAN_13
+BarcodeFormat.EAN_8
+BarcodeFormat.RSS_14
+BarcodeFormat.CODE_39
+BarcodeFormat.CODE_93
+BarcodeFormat.CODE_128
+BarcodeFormat.ITF
+BarcodeFormat.CODABAR
+BarcodeFormat.QR_CODE
+BarcodeFormat.DATA_MATRIX
+BarcodeFormat.PDF_417
+```
+
+ZBar
+====
+
+Installation
+------------
+
+Add the following dependency to your build.gradle file.
+
+`compile 'me.dm7.barcodescanner:zbar:1.9.2'`
+
+Simple Usage
+------------
+
+1.) Add camera permission to your AndroidManifest.xml file:
+
+```xml
+<uses-permission android:name="android.permission.CAMERA" />
+```
+
+2.) A very basic activity would look like this:
+
+```java
+public class SimpleScannerActivity extends Activity implements ZBarScannerView.ResultHandler {
+    private ZBarScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        mScannerView = new ZBarScannerView(this);    // Programmatically initialize the scanner view
+        setContentView(mScannerView);                // Set the scanner view as the content view
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
+        mScannerView.startCamera();          // Start camera on resume
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();           // Stop camera on pause
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        // Do something with the result here
+        Log.v(TAG, rawResult.getContents()); // Prints scan results
+        Log.v(TAG, rawResult.getBarcodeFormat().getName()); // Prints the scan format (qrcode, pdf417 etc.)
+
+        // If you would like to resume scanning, call this method below:
+        mScannerView.resumeCameraPreview(this);
+    }
+}
+
+```
+
+Please take a look at the [zbar/sample] (https://github.com/dm77/barcodescanner/tree/master/zbar/sample)  project for a full working example.
+
+Advanced Usage
+--------------
+
+
+Take a look at the [ScannerActivity.java] (https://github.com/dm77/barcodescanner/blob/master/zbar/sample/src/main/java/me/dm7/barcodescanner/zbar/sample/ScannerActivity.java) or [ScannerFragment.java] (https://github.com/dm77/barcodescanner/blob/master/zbar/sample/src/main/java/me/dm7/barcodescanner/zbar/sample/ScannerFragment.java) classes to get an idea on advanced usage.
+
+Interesting methods on the ZBarScannerView include:
+
+```java
+// Toggle flash:
+void setFlash(boolean);
+
+// Toogle autofocus:
+void setAutoFocus(boolean);
+
+// Specify interested barcode formats:
+void setFormats(List<BarcodeFormat> formats);
+```
+
+Specify front-facing or rear-facing cameras by using the `void startCamera(int cameraId);` method.
+
+Supported Formats:
+
+```
+BarcodeFormat.PARTIAL
+BarcodeFormat.EAN8
+BarcodeFormat.UPCE
+BarcodeFormat.ISBN10
+BarcodeFormat.UPCA
+BarcodeFormat.EAN13
+BarcodeFormat.ISBN13
+BarcodeFormat.I25
+BarcodeFormat.DATABAR
+BarcodeFormat.DATABAR_EXP
+BarcodeFormat.CODABAR
+BarcodeFormat.CODE39
+BarcodeFormat.PDF417
+BarcodeFormat.QR_CODE
+BarcodeFormat.CODE93
+BarcodeFormat.CODE128
+```
+
+Rebuilding ZBar Libraries
+=========================
+
+```
+mkdir some_work_dir
+cd work_dir
+wget http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.14.tar.gz
+tar zxvf libiconv-1.14.tar.gz
+```
+
+Patch the localcharset.c file:
+vim libiconv-1.14/libcharset/lib/localcharset.c
+
+On line 48, add the following line of code:
+
+```
+#undef HAVE_LANGINFO_CODESET
+```
+
+Save the file and continue with steps below:
+```
+cd libiconv-1.14
+./configure
+cd ..
+hg clone http://hg.code.sf.net/p/zbar/code zbar-code
+cd zbar-code/android
+android update project -p . -t 'android-19'
+```
+
+Open jni/Android.mk file and add fPIC flag to LOCAL_C_FLAGS.
+Open jni/Application.mk file and specify APP_ABI targets as needed.
+
+```
+ant -Dndk.dir=$NDK_HOME  -Diconv.src=some_work_dir/libiconv-1.14 zbar-clean zbar-all
+```
+
+Upon completion you can grab the .so and .jar files from the libs folder.
+
+Credits
+=======
+
+Almost all of the code for these library projects is based on:
+
+1. CameraPreview app from Android SDK APIDemos
+2. The ZXing project: https://github.com/zxing/zxing
+3. The ZBar Android SDK: http://sourceforge.net/projects/zbar/files/AndroidSDK/
+
+Contributors
+============
+
+https://github.com/dm77/barcodescanner/graphs/contributors
+
+License
+=======
+Apache License, Version 2.0

+ 118 - 0
build.gradle

@@ -0,0 +1,118 @@
+apply from: 'dependencies.gradle'
+
+buildscript {
+    repositories {
+        mavenCentral()
+        jcenter()
+        google()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.5.2'
+    }
+}
+
+allprojects {
+    group = 'me.dm7.barcodescanner'
+    version = '1.9.2'
+
+    repositories {
+        mavenCentral()
+        maven {
+            url 'https://oss.sonatype.org/content/groups/public'
+        }
+        google()
+    }
+
+    ext {
+        isLibrary = false
+    }
+}
+
+subprojects {
+    afterEvaluate { Project project ->
+        ext.pluginContainer = project.getPlugins()
+        if (ext.pluginContainer.hasPlugin("com.android.application") || ext.pluginContainer.hasPlugin("com.android.library")) {
+            android {
+                compileSdkVersion versions.compile_sdk
+                buildToolsVersion versions.build_tools
+
+                android {
+                    lintOptions {
+                        abortOnError false
+                    }
+                }
+
+                defaultConfig {
+                    minSdkVersion versions.min_sdk
+                    targetSdkVersion versions.target_sdk
+                    versionCode 192
+                    versionName "1.9.2"
+                }
+            }
+        }
+        if (project.isLibrary && rootProject.hasProperty('sonatypeUsername') && rootProject.hasProperty('sonatypePassword')) {
+            configure(project) {
+                apply plugin: 'maven'
+                apply plugin: 'signing'
+
+                signing {
+                    required { gradle.taskGraph.hasTask("uploadArchives") }
+                    sign configurations.archives
+                }
+
+                uploadArchives {
+                    repositories.mavenDeployer {
+                        beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
+
+                        repository(url: sonatypeRepo) {
+                            authentication(userName: sonatypeUsername, password: sonatypePassword)
+                            releases(updatePolicy: 'always')
+                        }
+
+                        snapshotRepository(url: sonatypeSnapshotRepo) {
+                            authentication(userName: sonatypeUsername, password: sonatypePassword)
+                            snapshots(updatePolicy: 'always')
+                        }
+
+                        pom.project {
+                            name project.pomName
+                            version project.version
+                            groupId project.group
+                            packaging project.pomPackaging
+                            artifactId project.pomArtifactId
+                            description project.pomDescription
+                            url 'https://github.com/dm77/barcodescanner'
+
+                            scm {
+                                url 'scm:[email protected]:dm77/barcodescanner.git'
+                                connection 'scm:[email protected]:dm77/barcodescanner.git'
+                                developerConnection 'scm:[email protected]:dm77/barcodescanner.git'
+                            }
+
+                            licenses {
+                                license {
+                                    name 'The Apache Software License, Version 2.0'
+                                    url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                                    distribution 'repo'
+                                }
+                            }
+
+                            developers {
+                                developer {
+                                    id 'dm77'
+                                    name 'Dushyanth Maguluru'
+                                    email '[email protected]'
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+/*task wrapper(type: Wrapper) {
+    gradleVersion = '3.4.1'
+}*/

+ 13 - 0
core/build.gradle

@@ -0,0 +1,13 @@
+apply plugin: 'com.android.library'
+
+ext {
+    isLibrary = true
+    pomPackaging = "aar"
+    pomArtifactId = "core"
+    pomName = "Barcode Scanner View"
+    pomDescription = 'An android library project which contains the core barcode scanner view'
+}
+
+dependencies {
+    compile libraries.support_v4
+}

+ 5 - 0
core/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="me.dm7.barcodescanner.core">
+    <application></application>
+</manifest>

+ 205 - 0
core/src/main/java/me/dm7/barcodescanner/core/BarcodeScannerView.java

@@ -0,0 +1,205 @@
+package me.dm7.barcodescanner.core;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.hardware.Camera;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
+
+public abstract class BarcodeScannerView extends FrameLayout implements Camera.PreviewCallback  {
+    private CameraWrapper mCameraWrapper;
+    private CameraPreview mPreview;
+    private IViewFinder mViewFinderView;
+    private Rect mFramingRectInPreview;
+    private CameraHandlerThread mCameraHandlerThread;
+    private Boolean mFlashState;
+    private boolean mAutofocusState = true;
+    private boolean mShouldScaleToFill = true;
+
+    public BarcodeScannerView(Context context) {
+        super(context);
+    }
+
+    public BarcodeScannerView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+        TypedArray a = context.getTheme().obtainStyledAttributes(
+                attributeSet,
+                R.styleable.BarcodeScannerView,
+                0, 0);
+
+        try {
+            setShouldScaleToFill(a.getBoolean(R.styleable.BarcodeScannerView_shouldScaleToFill, true));
+        } finally {
+            a.recycle();
+        }
+    }
+
+    public final void setupLayout(CameraWrapper cameraWrapper) {
+        removeAllViews();
+
+        mPreview = new CameraPreview(getContext(), cameraWrapper, this);
+        mPreview.setShouldScaleToFill(mShouldScaleToFill);
+        if (!mShouldScaleToFill) {
+            RelativeLayout relativeLayout = new RelativeLayout(getContext());
+            relativeLayout.setGravity(Gravity.CENTER);
+            relativeLayout.setBackgroundColor(Color.BLACK);
+            relativeLayout.addView(mPreview);
+            addView(relativeLayout);
+        } else {
+            addView(mPreview);
+        }
+
+        mViewFinderView = createViewFinderView(getContext());
+        if (mViewFinderView instanceof View) {
+            addView((View) mViewFinderView);
+        } else {
+            throw new IllegalArgumentException("IViewFinder object returned by " +
+                    "'createViewFinderView()' should be instance of android.view.View");
+        }
+    }
+
+    /**
+     * <p>Method that creates view that represents visual appearance of a barcode scanner</p>
+     * <p>Override it to provide your own view for visual appearance of a barcode scanner</p>
+     *
+     * @param context {@link Context}
+     * @return {@link android.view.View} that implements {@link ViewFinderView}
+     */
+    protected IViewFinder createViewFinderView(Context context) {
+        return new ViewFinderView(context);
+    }
+
+    public void startCamera(int cameraId) {
+        if(mCameraHandlerThread == null) {
+            mCameraHandlerThread = new CameraHandlerThread(this);
+        }
+        mCameraHandlerThread.startCamera(cameraId);
+    }
+
+    public void setupCameraPreview(CameraWrapper cameraWrapper) {
+        mCameraWrapper = cameraWrapper;
+        if(mCameraWrapper != null) {
+            setupLayout(mCameraWrapper);
+            mViewFinderView.setupViewFinder();
+            if(mFlashState != null) {
+                setFlash(mFlashState);
+            }
+            setAutoFocus(mAutofocusState);
+        }
+    }
+
+    public void startCamera() {
+        startCamera(CameraUtils.getDefaultCameraId());
+    }
+
+    public void stopCamera() {
+        if(mCameraWrapper != null) {
+            mPreview.stopCameraPreview();
+            mPreview.setCamera(null, null);
+            mCameraWrapper.mCamera.release();
+            mCameraWrapper = null;
+        }
+        if(mCameraHandlerThread != null) {
+            mCameraHandlerThread.quit();
+            mCameraHandlerThread = null;
+        }
+    }
+
+    public void stopCameraPreview() {
+        if(mPreview != null) {
+            mPreview.stopCameraPreview();
+        }
+    }
+
+    protected void resumeCameraPreview() {
+        if(mPreview != null) {
+            mPreview.showCameraPreview();
+        }
+    }
+
+    public synchronized Rect getFramingRectInPreview(int previewWidth, int previewHeight) {
+        if (mFramingRectInPreview == null) {
+            Rect framingRect = mViewFinderView.getFramingRect();
+            int viewFinderViewWidth = mViewFinderView.getWidth();
+            int viewFinderViewHeight = mViewFinderView.getHeight();
+            if (framingRect == null || viewFinderViewWidth == 0 || viewFinderViewHeight == 0) {
+                return null;
+            }
+
+            Rect rect = new Rect(framingRect);
+
+            if(previewWidth < viewFinderViewWidth) {
+                rect.left = rect.left * previewWidth / viewFinderViewWidth;
+                rect.right = rect.right * previewWidth / viewFinderViewWidth;
+            }
+
+            if(previewHeight < viewFinderViewHeight) {
+                rect.top = rect.top * previewHeight / viewFinderViewHeight;
+                rect.bottom = rect.bottom * previewHeight / viewFinderViewHeight;
+            }
+
+            mFramingRectInPreview = rect;
+        }
+        return mFramingRectInPreview;
+    }
+
+    public void setFlash(boolean flag) {
+        mFlashState = flag;
+        if(mCameraWrapper != null && CameraUtils.isFlashSupported(mCameraWrapper.mCamera)) {
+
+            Camera.Parameters parameters = mCameraWrapper.mCamera.getParameters();
+            if(flag) {
+                if(parameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_TORCH)) {
+                    return;
+                }
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+            } else {
+                if(parameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_OFF)) {
+                    return;
+                }
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+            }
+            mCameraWrapper.mCamera.setParameters(parameters);
+        }
+    }
+
+    public boolean getFlash() {
+        if(mCameraWrapper != null && CameraUtils.isFlashSupported(mCameraWrapper.mCamera)) {
+            Camera.Parameters parameters = mCameraWrapper.mCamera.getParameters();
+            if(parameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_TORCH)) {
+                return true;
+            } else {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    public void toggleFlash() {
+        if(mCameraWrapper != null && CameraUtils.isFlashSupported(mCameraWrapper.mCamera)) {
+            Camera.Parameters parameters = mCameraWrapper.mCamera.getParameters();
+            if(parameters.getFlashMode().equals(Camera.Parameters.FLASH_MODE_TORCH)) {
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
+            } else {
+                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
+            }
+            mCameraWrapper.mCamera.setParameters(parameters);
+        }
+    }
+
+    public void setAutoFocus(boolean state) {
+        mAutofocusState = state;
+        if(mPreview != null) {
+            mPreview.setAutoFocus(state);
+        }
+    }
+
+    public void setShouldScaleToFill(boolean shouldScaleToFill) {
+        mShouldScaleToFill = shouldScaleToFill;
+    }
+}

+ 37 - 0
core/src/main/java/me/dm7/barcodescanner/core/CameraHandlerThread.java

@@ -0,0 +1,37 @@
+package me.dm7.barcodescanner.core;
+
+
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+// This code is mostly based on the top answer here: http://stackoverflow.com/questions/18149964/best-use-of-handlerthread-over-other-similar-classes
+public class CameraHandlerThread extends HandlerThread {
+    private static final String LOG_TAG = "CameraHandlerThread";
+
+    private BarcodeScannerView mScannerView;
+
+    public CameraHandlerThread(BarcodeScannerView scannerView) {
+        super("CameraHandlerThread");
+        mScannerView = scannerView;
+        start();
+    }
+
+    public void startCamera(final int cameraId) {
+        Handler localHandler = new Handler(getLooper());
+        localHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                final Camera camera = CameraUtils.getCameraInstance(cameraId);
+                Handler mainHandler = new Handler(Looper.getMainLooper());
+                mainHandler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        mScannerView.setupCameraPreview(CameraWrapper.getWrapper(camera, cameraId));
+                    }
+                });
+            }
+        });
+    }
+}

+ 308 - 0
core/src/main/java/me/dm7/barcodescanner/core/CameraPreview.java

@@ -0,0 +1,308 @@
+package me.dm7.barcodescanner.core;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Display;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import java.util.List;
+
+public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
+    private static final String TAG = "CameraPreview";
+
+    private CameraWrapper mCameraWrapper;
+    private Handler mAutoFocusHandler;
+    private boolean mPreviewing = true;
+    private boolean mAutoFocus = true;
+    private boolean mSurfaceCreated = false;
+    private boolean mShouldScaleToFill = true;
+    private Camera.PreviewCallback mPreviewCallback;
+
+    public CameraPreview(Context context, CameraWrapper cameraWrapper, Camera.PreviewCallback previewCallback) {
+        super(context);
+        init(cameraWrapper, previewCallback);
+    }
+
+    public CameraPreview(Context context, AttributeSet attrs, CameraWrapper cameraWrapper, Camera.PreviewCallback previewCallback) {
+        super(context, attrs);
+        init(cameraWrapper, previewCallback);
+    }
+
+    public void init(CameraWrapper cameraWrapper, Camera.PreviewCallback previewCallback) {
+        setCamera(cameraWrapper, previewCallback);
+        mAutoFocusHandler = new Handler();
+        getHolder().addCallback(this);
+        getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+    }
+
+    public void setCamera(CameraWrapper cameraWrapper, Camera.PreviewCallback previewCallback) {
+        mCameraWrapper = cameraWrapper;
+        mPreviewCallback = previewCallback;
+    }
+
+    public void setShouldScaleToFill(boolean scaleToFill) {
+        mShouldScaleToFill = scaleToFill;
+    }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder surfaceHolder) {
+        mSurfaceCreated = true;
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
+        if(surfaceHolder.getSurface() == null) {
+            return;
+        }
+        stopCameraPreview();
+        showCameraPreview();
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
+        mSurfaceCreated = false;
+        stopCameraPreview();
+    }
+
+    public void showCameraPreview() {
+        if(mCameraWrapper != null) {
+            try {
+                getHolder().addCallback(this);
+                mPreviewing = true;
+                setupCameraParameters();
+                mCameraWrapper.mCamera.setPreviewDisplay(getHolder());
+                mCameraWrapper.mCamera.setDisplayOrientation(getDisplayOrientation());
+                mCameraWrapper.mCamera.setOneShotPreviewCallback(mPreviewCallback);
+                mCameraWrapper.mCamera.startPreview();
+                if(mAutoFocus) {
+                    if (mSurfaceCreated) { // check if surface created before using autofocus
+                        safeAutoFocus();
+                    } else {
+                        scheduleAutoFocus(); // wait 1 sec and then do check again
+                    }
+                }
+            } catch (Exception e) {
+                Log.e(TAG, e.toString(), e);
+            }
+        }
+    }
+
+    public void safeAutoFocus() {
+        try {
+            mCameraWrapper.mCamera.autoFocus(autoFocusCB);
+        } catch (RuntimeException re) {
+            // Horrible hack to deal with autofocus errors on Sony devices
+            // See https://github.com/dm77/barcodescanner/issues/7 for example
+            scheduleAutoFocus(); // wait 1 sec and then do check again
+        }
+    }
+
+    public void stopCameraPreview() {
+        if(mCameraWrapper != null) {
+            try {
+                mPreviewing = false;
+                getHolder().removeCallback(this);
+                mCameraWrapper.mCamera.cancelAutoFocus();
+                mCameraWrapper.mCamera.setOneShotPreviewCallback(null);
+                mCameraWrapper.mCamera.stopPreview();
+            } catch(Exception e) {
+                Log.e(TAG, e.toString(), e);
+            }
+        }
+    }
+
+    public void setupCameraParameters() {
+        Camera.Size optimalSize = getOptimalPreviewSize();
+        Camera.Parameters parameters = mCameraWrapper.mCamera.getParameters();
+        parameters.setPreviewSize(optimalSize.width, optimalSize.height);
+        mCameraWrapper.mCamera.setParameters(parameters);
+        adjustViewSize(optimalSize);
+    }
+
+    private void adjustViewSize(Camera.Size cameraSize) {
+        Point previewSize = convertSizeToLandscapeOrientation(new Point(getWidth(), getHeight()));
+        float cameraRatio = ((float) cameraSize.width) / cameraSize.height;
+        float screenRatio = ((float) previewSize.x) / previewSize.y;
+
+        if (screenRatio > cameraRatio) {
+            setViewSize((int) (previewSize.y * cameraRatio), previewSize.y);
+        } else {
+            setViewSize(previewSize.x, (int) (previewSize.x / cameraRatio));
+        }
+    }
+
+    @SuppressWarnings("SuspiciousNameCombination")
+    private Point convertSizeToLandscapeOrientation(Point size) {
+        if (getDisplayOrientation() % 180 == 0) {
+            return size;
+        } else {
+            return new Point(size.y, size.x);
+        }
+    }
+
+    @SuppressWarnings("SuspiciousNameCombination")
+    private void setViewSize(int width, int height) {
+        ViewGroup.LayoutParams layoutParams = getLayoutParams();
+        int tmpWidth;
+        int tmpHeight;
+        if (getDisplayOrientation() % 180 == 0) {
+            tmpWidth = width;
+            tmpHeight = height;
+        } else {
+            tmpWidth = height;
+            tmpHeight = width;
+        }
+
+        if (mShouldScaleToFill) {
+            int parentWidth = ((View) getParent()).getWidth();
+            int parentHeight = ((View) getParent()).getHeight();
+            float ratioWidth = (float) parentWidth / (float) tmpWidth;
+            float ratioHeight = (float) parentHeight / (float) tmpHeight;
+
+            float compensation;
+
+            if (ratioWidth > ratioHeight) {
+                compensation = ratioWidth;
+            } else {
+                compensation = ratioHeight;
+            }
+
+            tmpWidth = Math.round(tmpWidth * compensation);
+            tmpHeight = Math.round(tmpHeight * compensation);
+        }
+
+        layoutParams.width = tmpWidth;
+        layoutParams.height = tmpHeight;
+        setLayoutParams(layoutParams);
+    }
+
+    public int getDisplayOrientation() {
+        if (mCameraWrapper == null) {
+            //If we don't have a camera set there is no orientation so return dummy value
+            return 0;
+        }
+
+        Camera.CameraInfo info = new Camera.CameraInfo();
+        if(mCameraWrapper.mCameraId == -1) {
+            Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info);
+        } else {
+            Camera.getCameraInfo(mCameraWrapper.mCameraId, info);
+        }
+
+        WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+
+        int rotation = display.getRotation();
+        int degrees = 0;
+        switch (rotation) {
+            case Surface.ROTATION_0: degrees = 0; break;
+            case Surface.ROTATION_90: degrees = 90; break;
+            case Surface.ROTATION_180: degrees = 180; break;
+            case Surface.ROTATION_270: degrees = 270; break;
+        }
+
+        int result;
+        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+            result = (info.orientation + degrees) % 360;
+            result = (360 - result) % 360;  // compensate the mirror
+        } else {  // back-facing
+            result = (info.orientation - degrees + 360) % 360;
+        }
+        return result;
+    }
+
+    private Camera.Size getOptimalPreviewSize() {
+        if(mCameraWrapper == null) {
+            return null;
+        }
+
+        List<Camera.Size> sizes = mCameraWrapper.mCamera.getParameters().getSupportedPreviewSizes();
+        int w = getWidth();
+        int h = getHeight();
+        if (DisplayUtils.getScreenOrientation(getContext()) == Configuration.ORIENTATION_PORTRAIT) {
+            int portraitWidth = h;
+            h = w;
+            w = portraitWidth;
+        }
+
+        final double ASPECT_TOLERANCE = 0.1;
+        double targetRatio = (double) w / h;
+        if (sizes == null) return null;
+
+        Camera.Size optimalSize = null;
+        double minDiff = Double.MAX_VALUE;
+
+        int targetHeight = h;
+
+        // Try to find an size match aspect ratio and size
+        for (Camera.Size size : sizes) {
+            double ratio = (double) size.width / size.height;
+            if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
+            if (Math.abs(size.height - targetHeight) < minDiff) {
+                optimalSize = size;
+                minDiff = Math.abs(size.height - targetHeight);
+            }
+        }
+
+        // Cannot find the one match the aspect ratio, ignore the requirement
+        if (optimalSize == null) {
+            minDiff = Double.MAX_VALUE;
+            for (Camera.Size size : sizes) {
+                if (Math.abs(size.height - targetHeight) < minDiff) {
+                    optimalSize = size;
+                    minDiff = Math.abs(size.height - targetHeight);
+                }
+            }
+        }
+        return optimalSize;
+    }
+
+    public void setAutoFocus(boolean state) {
+        if(mCameraWrapper != null && mPreviewing) {
+            if(state == mAutoFocus) {
+                return;
+            }
+            mAutoFocus = state;
+            if(mAutoFocus) {
+                if (mSurfaceCreated) { // check if surface created before using autofocus
+                    Log.v(TAG, "Starting autofocus");
+                    safeAutoFocus();
+                } else {
+                    scheduleAutoFocus(); // wait 1 sec and then do check again
+                }
+            } else {
+                Log.v(TAG, "Cancelling autofocus");
+                mCameraWrapper.mCamera.cancelAutoFocus();
+            }
+        }
+    }
+
+    private Runnable doAutoFocus = new Runnable() {
+        public void run() {
+            if(mCameraWrapper != null && mPreviewing && mAutoFocus && mSurfaceCreated) {
+                safeAutoFocus();
+            }
+        }
+    };
+
+    // Mimic continuous auto-focusing
+    Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
+        public void onAutoFocus(boolean success, Camera camera) {
+            scheduleAutoFocus();
+        }
+    };
+
+    private void scheduleAutoFocus() {
+        mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
+    }
+}

+ 63 - 0
core/src/main/java/me/dm7/barcodescanner/core/CameraUtils.java

@@ -0,0 +1,63 @@
+package me.dm7.barcodescanner.core;
+
+import android.hardware.Camera;
+
+import java.util.List;
+
+public class CameraUtils {
+    /** A safe way to get an instance of the Camera object. */
+    public static Camera getCameraInstance() {
+        return getCameraInstance(getDefaultCameraId());
+    }
+
+    /** Favor back-facing camera by default. If none exists, fallback to whatever camera is available **/
+    public static int getDefaultCameraId() {
+        int numberOfCameras = Camera.getNumberOfCameras();
+        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+        int defaultCameraId = -1;
+        for (int i = 0; i < numberOfCameras; i++) {
+            defaultCameraId = i;
+            Camera.getCameraInfo(i, cameraInfo);
+            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+                return i;
+            }
+        }
+        return defaultCameraId;
+    }
+
+    /** A safe way to get an instance of the Camera object. */
+    public static Camera getCameraInstance(int cameraId) {
+        Camera c = null;
+        try {
+            if(cameraId == -1) {
+                c = Camera.open(); // attempt to get a Camera instance
+            } else {
+                c = Camera.open(cameraId); // attempt to get a Camera instance
+            }
+        }
+        catch (Exception e) {
+            // Camera is not available (in use or does not exist)
+        }
+        return c; // returns null if camera is unavailable
+    }
+
+    public static boolean isFlashSupported(Camera camera) {
+        /* Credits: Top answer at http://stackoverflow.com/a/19599365/868173 */
+        if (camera != null) {
+            Camera.Parameters parameters = camera.getParameters();
+
+            if (parameters.getFlashMode() == null) {
+                return false;
+            }
+
+            List<String> supportedFlashModes = parameters.getSupportedFlashModes();
+            if (supportedFlashModes == null || supportedFlashModes.isEmpty() || supportedFlashModes.size() == 1 && supportedFlashModes.get(0).equals(Camera.Parameters.FLASH_MODE_OFF)) {
+                return false;
+            }
+        } else {
+            return false;
+        }
+
+        return true;
+    }
+}

+ 25 - 0
core/src/main/java/me/dm7/barcodescanner/core/CameraWrapper.java

@@ -0,0 +1,25 @@
+package me.dm7.barcodescanner.core;
+
+import android.hardware.Camera;
+import android.support.annotation.NonNull;
+
+public class CameraWrapper {
+    public final Camera mCamera;
+    public final int mCameraId;
+
+    private CameraWrapper(@NonNull Camera camera, int cameraId) {
+        if (camera == null) {
+            throw new NullPointerException("Camera cannot be null");
+        }
+        this.mCamera = camera;
+        this.mCameraId = cameraId;
+    }
+
+    public static CameraWrapper getWrapper(Camera camera, int cameraId) {
+        if (camera == null) {
+            return null;
+        } else {
+            return new CameraWrapper(camera, cameraId);
+        }
+    }
+}

+ 41 - 0
core/src/main/java/me/dm7/barcodescanner/core/DisplayUtils.java

@@ -0,0 +1,41 @@
+package me.dm7.barcodescanner.core;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Point;
+import android.view.Display;
+import android.view.WindowManager;
+
+public class DisplayUtils {
+    public static Point getScreenResolution(Context context) {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+        Point screenResolution = new Point();
+        if (android.os.Build.VERSION.SDK_INT >= 13) {
+            display.getSize(screenResolution);
+        } else {
+            screenResolution.set(display.getWidth(), display.getHeight());
+        }
+
+        return screenResolution;
+    }
+
+    public static int getScreenOrientation(Context context)
+    {
+        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+        Display display = wm.getDefaultDisplay();
+
+        int orientation = Configuration.ORIENTATION_UNDEFINED;
+        if(display.getWidth()==display.getHeight()){
+            orientation = Configuration.ORIENTATION_SQUARE;
+        } else{
+            if(display.getWidth() < display.getHeight()){
+                orientation = Configuration.ORIENTATION_PORTRAIT;
+            }else {
+                orientation = Configuration.ORIENTATION_LANDSCAPE;
+            }
+        }
+        return orientation;
+    }
+
+}

+ 40 - 0
core/src/main/java/me/dm7/barcodescanner/core/IViewFinder.java

@@ -0,0 +1,40 @@
+package me.dm7.barcodescanner.core;
+
+import android.graphics.Rect;
+
+public interface IViewFinder {
+    /**
+     * Method that executes when Camera preview is starting.
+     * It is recommended to update framing rect here and invalidate view after that. <br/>
+     * For example see: {@link ViewFinderView#setupViewFinder()}
+     */
+    void setupViewFinder();
+
+    /**
+     * Provides {@link Rect} that identifies area where barcode scanner can detect visual codes
+     * <p>Note: This rect is a area representation in absolute pixel values. <br/>
+     * For example: <br/>
+     * If View's size is 1024x800 so framing rect might be 500x400</p>
+     *
+     * @return {@link Rect} that identifies barcode scanner area
+     */
+    Rect getFramingRect();
+
+    /**
+     * Width of a {@link android.view.View} that implements this interface
+     * <p>Note: this is already implemented in {@link android.view.View},
+     * so you don't need to override method and provide your implementation</p>
+     *
+     * @return width of a view
+     */
+    int getWidth();
+
+    /**
+     * Height of a {@link android.view.View} that implements this interface
+     * <p>Note: this is already implemented in {@link android.view.View},
+     * so you don't need to override method and provide your implementation</p>
+     *
+     * @return height of a view
+     */
+    int getHeight();
+}

+ 197 - 0
core/src/main/java/me/dm7/barcodescanner/core/ViewFinderView.java

@@ -0,0 +1,197 @@
+package me.dm7.barcodescanner.core;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class ViewFinderView extends View implements IViewFinder {
+    private static final String TAG = "ViewFinderView";
+
+    private Rect mFramingRect;
+
+    private static final float PORTRAIT_WIDTH_RATIO = 6f/8;
+    private static final float PORTRAIT_WIDTH_HEIGHT_RATIO = 0.75f;
+
+    private static final float LANDSCAPE_HEIGHT_RATIO = 5f/8;
+    private static final float LANDSCAPE_WIDTH_HEIGHT_RATIO = 1.4f;
+    private static final int MIN_DIMENSION_DIFF = 50;
+
+    private static final float SQUARE_DIMENSION_RATIO = 5f/8;
+
+    private static final int[] SCANNER_ALPHA = {0, 64, 128, 192, 255, 192, 128, 64};
+    private int scannerAlpha;
+    private static final int POINT_SIZE = 10;
+    private static final long ANIMATION_DELAY = 80l;
+
+    private final int mDefaultLaserColor = getResources().getColor(R.color.viewfinder_laser);
+    private final int mDefaultMaskColor = getResources().getColor(R.color.viewfinder_mask);
+    private final int mDefaultBorderColor = getResources().getColor(R.color.viewfinder_border);
+    private final int mDefaultBorderStrokeWidth = getResources().getInteger(R.integer.viewfinder_border_width);
+    private final int mDefaultBorderLineLength = getResources().getInteger(R.integer.viewfinder_border_length);
+
+    protected Paint mLaserPaint;
+    protected Paint mFinderMaskPaint;
+    protected Paint mBorderPaint;
+    protected int mBorderLineLength;
+    protected boolean mSquareViewFinder;
+
+    public ViewFinderView(Context context) {
+        super(context);
+        init();
+    }
+
+    public ViewFinderView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        init();
+    }
+
+    private void init() {
+        //set up laser paint
+        mLaserPaint = new Paint();
+        mLaserPaint.setColor(mDefaultLaserColor);
+        mLaserPaint.setStyle(Paint.Style.FILL);
+
+        //finder mask paint
+        mFinderMaskPaint = new Paint();
+        mFinderMaskPaint.setColor(mDefaultMaskColor);
+
+        //border paint
+        mBorderPaint = new Paint();
+        mBorderPaint.setColor(mDefaultBorderColor);
+        mBorderPaint.setStyle(Paint.Style.STROKE);
+        mBorderPaint.setStrokeWidth(mDefaultBorderStrokeWidth);
+
+        mBorderLineLength = mDefaultBorderLineLength;
+    }
+
+    public void setLaserColor(int laserColor) {
+        mLaserPaint.setColor(laserColor);
+    }
+    public void setMaskColor(int maskColor) {
+        mFinderMaskPaint.setColor(maskColor);
+    }
+    public void setBorderColor(int borderColor) {
+        mBorderPaint.setColor(borderColor);
+    }
+    public void setBorderStrokeWidth(int borderStrokeWidth) {
+        mBorderPaint.setStrokeWidth(borderStrokeWidth);
+    }
+    public void setBorderLineLength(int borderLineLength) {
+        mBorderLineLength = borderLineLength;
+    }
+
+    // TODO: Need a better way to configure this. Revisit when working on 2.0
+    public void setSquareViewFinder(boolean set) {
+        mSquareViewFinder = set;
+    }
+
+    public void setupViewFinder() {
+        updateFramingRect();
+        invalidate();
+    }
+
+    public Rect getFramingRect() {
+        return mFramingRect;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        if(getFramingRect() == null) {
+            return;
+        }
+
+        drawViewFinderMask(canvas);
+        drawViewFinderBorder(canvas);
+        drawLaser(canvas);
+    }
+
+    public void drawViewFinderMask(Canvas canvas) {
+        int width = canvas.getWidth();
+        int height = canvas.getHeight();
+        Rect framingRect = getFramingRect();
+        
+        canvas.drawRect(0, 0, width, framingRect.top, mFinderMaskPaint);
+        canvas.drawRect(0, framingRect.top, framingRect.left, framingRect.bottom + 1, mFinderMaskPaint);
+        canvas.drawRect(framingRect.right + 1, framingRect.top, width, framingRect.bottom + 1, mFinderMaskPaint);
+        canvas.drawRect(0, framingRect.bottom + 1, width, height, mFinderMaskPaint);
+    }
+
+    public void drawViewFinderBorder(Canvas canvas) {
+        Rect framingRect = getFramingRect();
+        
+        canvas.drawLine(framingRect.left - 1, framingRect.top - 1, framingRect.left - 1, framingRect.top - 1 + mBorderLineLength, mBorderPaint);
+        canvas.drawLine(framingRect.left - 1, framingRect.top - 1, framingRect.left - 1 + mBorderLineLength, framingRect.top - 1, mBorderPaint);
+
+        canvas.drawLine(framingRect.left - 1, framingRect.bottom + 1, framingRect.left - 1, framingRect.bottom + 1 - mBorderLineLength, mBorderPaint);
+        canvas.drawLine(framingRect.left - 1, framingRect.bottom + 1, framingRect.left - 1 + mBorderLineLength, framingRect.bottom + 1, mBorderPaint);
+
+        canvas.drawLine(framingRect.right + 1, framingRect.top - 1, framingRect.right + 1, framingRect.top - 1 + mBorderLineLength, mBorderPaint);
+        canvas.drawLine(framingRect.right + 1, framingRect.top - 1, framingRect.right + 1 - mBorderLineLength, framingRect.top - 1, mBorderPaint);
+
+        canvas.drawLine(framingRect.right + 1, framingRect.bottom + 1, framingRect.right + 1, framingRect.bottom + 1 - mBorderLineLength, mBorderPaint);
+        canvas.drawLine(framingRect.right + 1, framingRect.bottom + 1, framingRect.right + 1 - mBorderLineLength, framingRect.bottom + 1, mBorderPaint);
+    }
+
+    public void drawLaser(Canvas canvas) {
+        Rect framingRect = getFramingRect();
+        
+        // Draw a red "laser scanner" line through the middle to show decoding is active
+        mLaserPaint.setAlpha(SCANNER_ALPHA[scannerAlpha]);
+        scannerAlpha = (scannerAlpha + 1) % SCANNER_ALPHA.length;
+        int middle = framingRect.height() / 2 + framingRect.top;
+        canvas.drawRect(framingRect.left + 2, middle - 1, framingRect.right - 1, middle + 2, mLaserPaint);
+
+        postInvalidateDelayed(ANIMATION_DELAY,
+                framingRect.left - POINT_SIZE,
+                framingRect.top - POINT_SIZE,
+                framingRect.right + POINT_SIZE,
+                framingRect.bottom + POINT_SIZE);
+    }
+
+    @Override
+    protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
+        updateFramingRect();
+    }
+
+    public synchronized void updateFramingRect() {
+        Point viewResolution = new Point(getWidth(), getHeight());
+        int width;
+        int height;
+        int orientation = DisplayUtils.getScreenOrientation(getContext());
+
+        if(mSquareViewFinder) {
+            if(orientation != Configuration.ORIENTATION_PORTRAIT) {
+                height = (int) (getHeight() * SQUARE_DIMENSION_RATIO);
+                width = height;
+            } else {
+                width = (int) (getWidth() * SQUARE_DIMENSION_RATIO);
+                height = width;
+            }
+        } else {
+            if(orientation != Configuration.ORIENTATION_PORTRAIT) {
+                height = (int) (getHeight() * LANDSCAPE_HEIGHT_RATIO);
+                width = (int) (LANDSCAPE_WIDTH_HEIGHT_RATIO * height);
+            } else {
+                width = (int) (getWidth() * PORTRAIT_WIDTH_RATIO);
+                height = (int) (PORTRAIT_WIDTH_HEIGHT_RATIO * width);
+            }
+        }
+
+        if(width > getWidth()) {
+            width = getWidth() - MIN_DIMENSION_DIFF;
+        }
+
+        if(height > getHeight()) {
+            height = getHeight() - MIN_DIMENSION_DIFF;
+        }
+
+        int leftOffset = (viewResolution.x - width) / 2;
+        int topOffset = (viewResolution.y - height) / 2;
+        mFramingRect = new Rect(leftOffset, topOffset, leftOffset + width, topOffset + height);
+    }
+}

+ 5 - 0
core/src/main/res/values-hdpi/strings.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="viewfinder_border_width">4</integer>
+    <integer name="viewfinder_border_length">60</integer>
+</resources>

+ 5 - 0
core/src/main/res/values-xhdpi/strings.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="viewfinder_border_width">5</integer>
+    <integer name="viewfinder_border_length">80</integer>
+</resources>

+ 5 - 0
core/src/main/res/values-xxhdpi/strings.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="viewfinder_border_width">6</integer>
+    <integer name="viewfinder_border_length">100</integer>
+</resources>

+ 6 - 0
core/src/main/res/values/attrs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <declare-styleable name="BarcodeScannerView">
+        <attr name="shouldScaleToFill" format="boolean" />
+    </declare-styleable>
+</resources>

+ 6 - 0
core/src/main/res/values/colors.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="viewfinder_mask">#60000000</color>
+    <color name="viewfinder_laser">#ffcc0000</color>
+    <color name="viewfinder_border">#ffafed44</color>
+</resources>

+ 4 - 0
core/src/main/res/values/strings.xml

@@ -0,0 +1,4 @@
+<resources>    
+    <integer name="viewfinder_border_width">4</integer>
+    <integer name="viewfinder_border_length">60</integer>
+</resources>

+ 19 - 0
dependencies.gradle

@@ -0,0 +1,19 @@
+ext.versions = [
+        compile_sdk   : 25,
+        min_sdk       : 14,
+        target_sdk    : 25,
+        build_tools   : "25.0.2",
+        support_lib   : "25.3.1",
+        zxing         : "3.3.0",
+        barcodescanner: "1.9.2"
+]
+
+ext.libraries = [
+        support_v4          : "com.android.support:support-v4:$versions.support_lib",
+        appcompat_v7        : "com.android.support:appcompat-v7:$versions.support_lib",
+        design_support      : "com.android.support:design:$versions.support_lib",
+        zxing_core          : "com.google.zxing:core:$versions.zxing",
+        barcodescanner_core : "me.dm7.barcodescanner:core:$versions.barcodescanner",
+        barcodescanner_zbar : "me.dm7.barcodescanner:zbar:$versions.barcodescanner",
+        barcodescanner_zxing: "me.dm7.barcodescanner:zxing:$versions.barcodescanner"
+]

BIN
gradle/wrapper/gradle-wrapper.jar


+ 6 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Sun Jan 31 14:26:47 CST 2021
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip

+ 172 - 0
gradlew

@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save ( ) {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"

+ 84 - 0
gradlew.bat

@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 2 - 0
settings.gradle

@@ -0,0 +1,2 @@
+include ':vision', "vision-sample"
+include "core", "zxing", "zxing-sample", "zbar", "zbar-sample"

+ 115 - 0
vision-sample/README.md

@@ -0,0 +1,115 @@
+Google Mobile Vision
+=====
+
+Installation
+------------
+
+Add the following dependency to your build.gradle file.
+
+`compile 'me.dm7.barcodescanner:vision:1.9'`
+
+Simple Usage
+------------
+
+1.) Add camera permission to your AndroidManifest.xml file:
+
+```xml
+<uses-permission android:name="android.permission.CAMERA" />
+```
+
+2.) A very basic activity would look like this:
+
+```java
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+import me.dm7.barcodescanner.vision.Result;
+import me.dm7.barcodescanner.vision.VisionScannerView;
+
+public class SimpleScannerActivity extends Activity implements VisionScannerView.ResultHandler {
+    private static final String TAG = "SimpleScannerActivity";
+
+    private VisionScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        mScannerView = new VisionScannerView(this);   // Programmatically initialize the scanner view
+        setContentView(mScannerView);                // Set the scanner view as the content view
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
+        mScannerView.startCamera();          // Start camera on resume
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();           // Stop camera on pause
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        // Do something with the result here
+        Log.v(TAG, rawResult.getBarcode().displayValue); // Prints scan results
+        Log.v(TAG, BarcodeFormat.getFormatById(rawResult.getBarcode().format).toString()); // Prints the scan format (qrcode, pdf417 etc.)
+
+        // If you would like to resume scanning, call this method below:
+        mScannerView.resumeCameraPreview(this);
+
+        Toast.makeText(SimpleScannerActivity.this, rawResult.getBarcode().displayValue, Toast.LENGTH_SHORT).show();
+    }
+}
+
+```
+
+Please take a look at the [vision-sample] (https://github.com/dm77/barcodescanner/tree/master/vision-sample) project for a full working example.
+
+Advanced Usage
+--------------
+
+Take a look at the [FullScannerActivity.java] (https://github.com/dm77/barcodescanner/blob/master/vision/sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerActivity.java) classes to get an idea on advanced usage.
+
+Interesting methods on the VisionScannerView include:
+
+```java
+// Toggle flash:
+void setFlash(boolean);
+
+// Toogle autofocus:
+void setAutoFocus(boolean);
+
+// Specify interested barcode formats:
+void setFormats(List<BarcodeFormat> formats);
+
+// Specify the cameraId to start with:
+void startCamera(int cameraId);
+```
+
+Specify front-facing or rear-facing cameras by using the `void startCamera(int cameraId);` method.
+
+Supported Formats:
+
+```java
+     BarcodeFormat.CODE_128
+     BarcodeFormat.CODE_39
+     BarcodeFormat.CODE_93
+     BarcodeFormat.CODABAR
+     BarcodeFormat.DATA_MATRIX
+     BarcodeFormat.EAN_13
+     BarcodeFormat.EAN_8
+     BarcodeFormat.ITF
+     BarcodeFormat.QR_CODE
+     BarcodeFormat.UPC_A
+     BarcodeFormat.UPC_E
+     BarcodeFormat.PDF417
+     BarcodeFormat.AZTEC
+```

+ 16 - 0
vision-sample/build.gradle

@@ -0,0 +1,16 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+    compile project(":vision")
+    //compile 'me.dm7.barcodescanner:vision:1.9'
+    //compile supportLibraryDependency
+    compile 'com.android.support:appcompat-v7:25.3.1'
+    compile 'com.android.support:design:25.3.1'
+}
+
+android {
+    defaultConfig {
+        applicationId "${project.group}.vision.sample"
+    }
+    buildToolsVersion '25.0.2'
+}

+ 50 - 0
vision-sample/src/main/AndroidManifest.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="me.dm7.barcodescanner.vision.sample">
+
+    <uses-permission android:name="android.permission.CAMERA"/>
+
+    <application
+        android:allowBackup="true"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name">
+        <activity
+            android:name=".MainActivity"
+            android:label="@string/app_name"
+            android:theme="@style/AppTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name=".FullScannerActivity"
+            android:label="@string/full_activity_sample"
+            android:theme="@style/AppOverlayTheme"
+            android:uiOptions="splitActionBarWhenNarrow">
+            <meta-data
+                android:name="android.support.UI_OPTIONS"
+                android:value="splitActionBarWhenNarrow"/>
+        </activity>
+
+
+        <activity
+            android:name=".SimpleScannerActivity"
+            android:label="@string/simple_scanner_activity"
+            android:theme="@style/AppOverlayTheme">
+        </activity>
+
+        <activity
+            android:name=".FullScannerFragmentActivity"
+            android:label="@string/full_fragment_sample"
+            android:theme="@style/AppOverlayTheme"
+            android:uiOptions="splitActionBarWhenNarrow">
+            <meta-data
+                android:name="android.support.UI_OPTIONS"
+                android:value="splitActionBarWhenNarrow"/>
+        </activity>
+
+    </application>
+</manifest>

+ 28 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/BaseScannerActivity.java

@@ -0,0 +1,28 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+public class BaseScannerActivity extends AppCompatActivity {
+    public void setupToolbar() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+        final ActionBar ab = getSupportActionBar();
+        if(ab != null) {
+            ab.setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            // Respond to the action bar's Up/Home button
+            case android.R.id.home:
+                finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 87 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/CameraSelectorDialogFragment.java

@@ -0,0 +1,87 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class CameraSelectorDialogFragment extends DialogFragment {
+    public interface CameraSelectorDialogListener {
+        public void onCameraSelected(int cameraId);
+    }
+
+    private int mCameraId;
+    private CameraSelectorDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static CameraSelectorDialogFragment newInstance(CameraSelectorDialogListener listener, int cameraId) {
+        CameraSelectorDialogFragment fragment = new CameraSelectorDialogFragment();
+        fragment.mCameraId = cameraId;
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        if(mListener == null) {
+            dismiss();
+            return null;
+        }
+
+        int numberOfCameras = Camera.getNumberOfCameras();
+        String[] cameraNames = new String[numberOfCameras];
+        int checkedIndex = 0;
+
+        for (int i = 0; i < numberOfCameras; i++) {
+            Camera.CameraInfo info = new Camera.CameraInfo();
+            Camera.getCameraInfo(i, info);
+            if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+                cameraNames[i] = "Front Facing";
+            } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+                cameraNames[i] = "Rear Facing";
+            } else {
+                cameraNames[i] = "Camera ID: " + i;
+            }
+            if(i == mCameraId) {
+                checkedIndex = i;
+            }
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        // Set the dialog title
+        builder.setTitle(R.string.select_camera)
+                // Specify the list array, the items to be selected by default (null for none),
+                // and the listener through which to receive callbacks when items are selected
+                .setSingleChoiceItems(cameraNames, checkedIndex,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mCameraId = which;
+                            }
+                        })
+                        // Set the action buttons
+                .setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        // User clicked OK, so save the mSelectedIndices results somewhere
+                        // or return them to the component that opened the dialog
+                        if (mListener != null) {
+                            mListener.onCameraSelected(mCameraId);
+                        }
+                    }
+                })
+                .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                    }
+                });
+
+        return builder.create();
+    }
+}

+ 94 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FormatSelectorDialogFragment.java

@@ -0,0 +1,94 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+import java.util.ArrayList;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+
+
+public class FormatSelectorDialogFragment extends DialogFragment {
+    public interface FormatSelectorDialogListener {
+        public void onFormatsSaved(ArrayList<Integer> selectedIndices);
+    }
+
+    private ArrayList<Integer> mSelectedIndices;
+    private FormatSelectorDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static FormatSelectorDialogFragment newInstance(FormatSelectorDialogListener listener, ArrayList<Integer> selectedIndices) {
+        FormatSelectorDialogFragment fragment = new FormatSelectorDialogFragment();
+        if(selectedIndices == null) {
+            selectedIndices = new ArrayList<Integer>();
+        }
+        fragment.mSelectedIndices = new ArrayList<Integer>(selectedIndices);
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        if(mSelectedIndices == null || mListener == null) {
+            dismiss();
+            return null;
+        }
+
+        String[] formats = new String[BarcodeFormat.ALL_FORMATS.size()];
+        boolean[] checkedIndices = new boolean[BarcodeFormat.ALL_FORMATS.size()];
+        int i = 0;
+        for(BarcodeFormat format : BarcodeFormat.ALL_FORMATS) {
+            formats[i] = format.getName();
+            if(mSelectedIndices.contains(i)) {
+                checkedIndices[i] = true;
+            } else {
+                checkedIndices[i] = false;
+            }
+            i++;
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        // Set the dialog title
+        builder.setTitle(R.string.choose_formats)
+                // Specify the list array, the items to be selected by default (null for none),
+                // and the listener through which to receive callbacks when items are selected
+                .setMultiChoiceItems(formats, checkedIndices,
+                        new DialogInterface.OnMultiChoiceClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                                if (isChecked) {
+                                    // If the user checked the item, add it to the selected items
+                                    mSelectedIndices.add(which);
+                                } else if (mSelectedIndices.contains(which)) {
+                                    // Else, if the item is already in the array, remove it
+                                    mSelectedIndices.remove(mSelectedIndices.indexOf(which));
+                                }
+                            }
+                        })
+                        // Set the action buttons
+                .setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        // User clicked OK, so save the mSelectedIndices results somewhere
+                        // or return them to the component that opened the dialog
+                        if (mListener != null) {
+                            mListener.onFormatsSaved(mSelectedIndices);
+                        }
+                    }
+                })
+                .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                    }
+                });
+
+        return builder.create();
+    }
+}

+ 217 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerActivity.java

@@ -0,0 +1,217 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.view.MenuItemCompat;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+import me.dm7.barcodescanner.vision.Result;
+import me.dm7.barcodescanner.vision.VisionScannerView;
+
+
+public class FullScannerActivity extends BaseScannerActivity implements MessageDialogFragment.MessageDialogListener,
+        VisionScannerView.ResultHandler, FormatSelectorDialogFragment.FormatSelectorDialogListener,
+        CameraSelectorDialogFragment.CameraSelectorDialogListener {
+    private static final String FLASH_STATE = "FLASH_STATE";
+    private static final String AUTO_FOCUS_STATE = "AUTO_FOCUS_STATE";
+    private static final String SELECTED_FORMATS = "SELECTED_FORMATS";
+    private static final String CAMERA_ID = "CAMERA_ID";
+    private VisionScannerView mScannerView;
+    private boolean mFlash;
+    private boolean mAutoFocus;
+    private ArrayList<Integer> mSelectedIndices;
+    private int mCameraId = -1;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        if (state != null) {
+            mFlash = state.getBoolean(FLASH_STATE, false);
+            mAutoFocus = state.getBoolean(AUTO_FOCUS_STATE, true);
+            mSelectedIndices = state.getIntegerArrayList(SELECTED_FORMATS);
+            mCameraId = state.getInt(CAMERA_ID, -1);
+        } else {
+            mFlash = false;
+            mAutoFocus = true;
+            mSelectedIndices = null;
+            mCameraId = -1;
+        }
+
+        setContentView(R.layout.activity_full_scanner);
+        setupToolbar();
+        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);
+        mScannerView = new VisionScannerView(this);
+        setupFormats();
+        contentFrame.addView(mScannerView);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(FLASH_STATE, mFlash);
+        outState.putBoolean(AUTO_FOCUS_STATE, mAutoFocus);
+        outState.putIntegerArrayList(SELECTED_FORMATS, mSelectedIndices);
+        outState.putInt(CAMERA_ID, mCameraId);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuItem menuItem;
+
+        if (mFlash) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+
+        if (mAutoFocus) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_formats, 0, R.string.formats);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_camera_selector, 0, R.string.select_camera);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle presses on the action bar items
+        switch (item.getItemId()) {
+            case R.id.menu_flash:
+                mFlash = !mFlash;
+                if (mFlash) {
+                    item.setTitle(R.string.flash_on);
+                } else {
+                    item.setTitle(R.string.flash_off);
+                }
+                mScannerView.setFlash(mFlash);
+                return true;
+            case R.id.menu_auto_focus:
+                mAutoFocus = !mAutoFocus;
+                if (mAutoFocus) {
+                    item.setTitle(R.string.auto_focus_on);
+                } else {
+                    item.setTitle(R.string.auto_focus_off);
+                }
+                mScannerView.setAutoFocus(mAutoFocus);
+                return true;
+            case R.id.menu_formats:
+                DialogFragment fragment = FormatSelectorDialogFragment.newInstance(this, mSelectedIndices);
+                fragment.show(getSupportFragmentManager(), "format_selector");
+                return true;
+            case R.id.menu_camera_selector:
+                mScannerView.stopCamera();
+                DialogFragment cFragment = CameraSelectorDialogFragment.newInstance(this, mCameraId);
+                cFragment.show(getSupportFragmentManager(), "camera_selector");
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        try {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+            Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+            r.play();
+        } catch (Exception e) {
+        }
+        showMessageDialog("Contents = " + rawResult.getBarcode().rawValue + ", Format = " + BarcodeFormat.getFormatById(rawResult.getBarcode().format).getName());
+    }
+
+    public void showMessageDialog(String message) {
+        DialogFragment fragment = MessageDialogFragment.newInstance("Scan Results", message, this);
+        fragment.show(getSupportFragmentManager(), "scan_results");
+    }
+
+    public void closeMessageDialog() {
+        closeDialog("scan_results");
+    }
+
+    public void closeFormatsDialog() {
+        closeDialog("format_selector");
+    }
+
+    public void closeDialog(String dialogName) {
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        DialogFragment fragment = (DialogFragment) fragmentManager.findFragmentByTag(dialogName);
+        if (fragment != null) {
+            fragment.dismiss();
+        }
+    }
+
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // Resume the camera
+        mScannerView.resumeCameraPreview(this);
+    }
+
+    @Override
+    public void onFormatsSaved(ArrayList<Integer> selectedIndices) {
+        mSelectedIndices = selectedIndices;
+        setupFormats();
+    }
+
+    @Override
+    public void onCameraSelected(int cameraId) {
+        mCameraId = cameraId;
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+
+    public void setupFormats() {
+        List<BarcodeFormat> formats = new ArrayList<BarcodeFormat>();
+        if (mSelectedIndices == null || mSelectedIndices.isEmpty()) {
+            mSelectedIndices = new ArrayList<Integer>();
+            for (int i = 0; i < BarcodeFormat.ALL_FORMATS.size(); i++) {
+                mSelectedIndices.add(i);
+            }
+        }
+
+        for (int index : mSelectedIndices) {
+            formats.add(BarcodeFormat.ALL_FORMATS.get(index));
+        }
+        if (mScannerView != null) {
+            mScannerView.setFormats(formats);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+        closeMessageDialog();
+        closeFormatsDialog();
+    }
+}

+ 220 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerFragment.java

@@ -0,0 +1,220 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.view.MenuItemCompat;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+import me.dm7.barcodescanner.vision.Result;
+import me.dm7.barcodescanner.vision.VisionScannerView;
+
+public class FullScannerFragment extends Fragment implements MessageDialogFragment.MessageDialogListener,
+        VisionScannerView.ResultHandler, FormatSelectorDialogFragment.FormatSelectorDialogListener,
+        CameraSelectorDialogFragment.CameraSelectorDialogListener {
+    private static final String FLASH_STATE = "FLASH_STATE";
+    private static final String AUTO_FOCUS_STATE = "AUTO_FOCUS_STATE";
+    private static final String SELECTED_FORMATS = "SELECTED_FORMATS";
+    private static final String CAMERA_ID = "CAMERA_ID";
+    private VisionScannerView mScannerView;
+    private boolean mFlash;
+    private boolean mAutoFocus;
+    private ArrayList<Integer> mSelectedIndices;
+    private int mCameraId = -1;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+        mScannerView = new VisionScannerView(getActivity());
+        if (state != null) {
+            mFlash = state.getBoolean(FLASH_STATE, false);
+            mAutoFocus = state.getBoolean(AUTO_FOCUS_STATE, true);
+            mSelectedIndices = state.getIntegerArrayList(SELECTED_FORMATS);
+            mCameraId = state.getInt(CAMERA_ID, -1);
+        } else {
+            mFlash = false;
+            mAutoFocus = true;
+            mSelectedIndices = null;
+            mCameraId = -1;
+        }
+        setupFormats();
+        return mScannerView;
+    }
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setHasOptionsMenu(true);
+    }
+
+    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        MenuItem menuItem;
+
+        if (mFlash) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+
+        if (mAutoFocus) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_formats, 0, R.string.formats);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_camera_selector, 0, R.string.select_camera);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle presses on the action bar items
+        switch (item.getItemId()) {
+            case R.id.menu_flash:
+                mFlash = !mFlash;
+                if (mFlash) {
+                    item.setTitle(R.string.flash_on);
+                } else {
+                    item.setTitle(R.string.flash_off);
+                }
+                mScannerView.setFlash(mFlash);
+                return true;
+            case R.id.menu_auto_focus:
+                mAutoFocus = !mAutoFocus;
+                if (mAutoFocus) {
+                    item.setTitle(R.string.auto_focus_on);
+                } else {
+                    item.setTitle(R.string.auto_focus_off);
+                }
+                mScannerView.setAutoFocus(mAutoFocus);
+                return true;
+            case R.id.menu_formats:
+                DialogFragment fragment = FormatSelectorDialogFragment.newInstance(this, mSelectedIndices);
+                fragment.show(getActivity().getSupportFragmentManager(), "format_selector");
+                return true;
+            case R.id.menu_camera_selector:
+                mScannerView.stopCamera();
+                DialogFragment cFragment = CameraSelectorDialogFragment.newInstance(this, mCameraId);
+                cFragment.show(getActivity().getSupportFragmentManager(), "camera_selector");
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(FLASH_STATE, mFlash);
+        outState.putBoolean(AUTO_FOCUS_STATE, mAutoFocus);
+        outState.putIntegerArrayList(SELECTED_FORMATS, mSelectedIndices);
+        outState.putInt(CAMERA_ID, mCameraId);
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        try {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+            Ringtone r = RingtoneManager.getRingtone(getActivity().getApplicationContext(), notification);
+            r.play();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        showMessageDialog("Contents = " + rawResult.getBarcode().rawValue + ", Format = " + BarcodeFormat.getFormatById(rawResult.getBarcode().format).getName());
+    }
+
+    public void showMessageDialog(String message) {
+        DialogFragment fragment = MessageDialogFragment.newInstance("Scan Results", message, this);
+        fragment.show(getActivity().getSupportFragmentManager(), "scan_results");
+    }
+
+    public void closeMessageDialog() {
+        closeDialog("scan_results");
+    }
+
+    public void closeFormatsDialog() {
+        closeDialog("format_selector");
+    }
+
+    public void closeDialog(String dialogName) {
+        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
+        DialogFragment fragment = (DialogFragment) fragmentManager.findFragmentByTag(dialogName);
+        if (fragment != null) {
+            fragment.dismiss();
+        }
+    }
+
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // Resume the camera
+        mScannerView.resumeCameraPreview(this);
+    }
+
+    @Override
+    public void onFormatsSaved(ArrayList<Integer> selectedIndices) {
+        mSelectedIndices = selectedIndices;
+        setupFormats();
+    }
+
+    @Override
+    public void onCameraSelected(int cameraId) {
+        mCameraId = cameraId;
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    public void setupFormats() {
+        List<BarcodeFormat> formats = new ArrayList<>();
+        if (mSelectedIndices == null || mSelectedIndices.isEmpty()) {
+            mSelectedIndices = new ArrayList<>();
+            for (int i = 0; i < BarcodeFormat.ALL_FORMATS.size(); i++) {
+                mSelectedIndices.add(i);
+            }
+        }
+
+        for (int index : mSelectedIndices) {
+            formats.add(BarcodeFormat.ALL_FORMATS.get(index));
+        }
+        if (mScannerView != null) {
+            mScannerView.setFormats(formats);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+        closeMessageDialog();
+        closeFormatsDialog();
+    }
+}

+ 12 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/FullScannerFragmentActivity.java

@@ -0,0 +1,12 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.os.Bundle;
+
+public class FullScannerFragmentActivity extends BaseScannerActivity {
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_full_scanner_fragment);
+        setupToolbar();
+    }
+}

+ 71 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/MainActivity.java

@@ -0,0 +1,71 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.widget.Toast;
+
+public class MainActivity extends AppCompatActivity {
+    private static final int ZXING_CAMERA_PERMISSION = 1;
+    private Class<?> mClass;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_main);
+        setupToolbar();
+    }
+
+    public void setupToolbar() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+    }
+
+    public void launchSimpleActivity(View v) {
+        launchActivity(SimpleScannerActivity.class);
+    }
+
+
+    public void launchFullActivity(View v) {
+        launchActivity(FullScannerActivity.class);
+    }
+
+
+    public void launchActivity(Class<?> clss) {
+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            mClass = clss;
+            ActivityCompat.requestPermissions(this,
+                    new String[]{Manifest.permission.CAMERA}, ZXING_CAMERA_PERMISSION);
+        } else {
+            Intent intent = new Intent(this, clss);
+            startActivity(intent);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
+        switch (requestCode) {
+            case ZXING_CAMERA_PERMISSION:
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    if (mClass != null) {
+                        Intent intent = new Intent(this, mClass);
+                        startActivity(intent);
+                    }
+                } else {
+                    Toast.makeText(this, "Please grant camera permission to use the QR Scanner", Toast.LENGTH_SHORT).show();
+                }
+        }
+    }
+
+    public void launchFullFragmentActivity(View view) {
+        launchActivity(FullScannerFragmentActivity.class);
+    }
+}

+ 47 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/MessageDialogFragment.java

@@ -0,0 +1,47 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class MessageDialogFragment extends DialogFragment {
+    public interface MessageDialogListener {
+        public void onDialogPositiveClick(DialogFragment dialog);
+    }
+
+    private String mTitle;
+    private String mMessage;
+    private MessageDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static MessageDialogFragment newInstance(String title, String message, MessageDialogListener listener) {
+        MessageDialogFragment fragment = new MessageDialogFragment();
+        fragment.mTitle = title;
+        fragment.mMessage = message;
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(mMessage)
+                .setTitle(mTitle);
+
+        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                if(mListener != null) {
+                    mListener.onDialogPositiveClick(MessageDialogFragment.this);
+                }
+            }
+        });
+
+        return builder.create();
+    }
+}

+ 48 - 0
vision-sample/src/main/java/me/dm7/barcodescanner/vision/sample/SimpleScannerActivity.java

@@ -0,0 +1,48 @@
+package me.dm7.barcodescanner.vision.sample;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import me.dm7.barcodescanner.vision.BarcodeFormat;
+import me.dm7.barcodescanner.vision.Result;
+import me.dm7.barcodescanner.vision.VisionScannerView;
+
+public class SimpleScannerActivity extends Activity implements VisionScannerView.ResultHandler {
+    private static final String TAG = "SimpleScannerActivity";
+
+    private VisionScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        mScannerView = new VisionScannerView(this);   // Programmatically initialize the scanner view
+        setContentView(mScannerView);                // Set the scanner view as the content view
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this); // Register ourselves as a handler for scan results.
+        mScannerView.startCamera();          // Start camera on resume
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();           // Stop camera on pause
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        // Do something with the result here
+        Log.v(TAG, rawResult.getBarcode().displayValue); // Prints scan results
+        Log.v(TAG, BarcodeFormat.getFormatById(rawResult.getBarcode().format).toString()); // Prints the scan format (qrcode, pdf417 etc.)
+
+        // If you would like to resume scanning, call this method below:
+        mScannerView.resumeCameraPreview(this);
+
+        Toast.makeText(SimpleScannerActivity.this, rawResult.getBarcode().displayValue, Toast.LENGTH_SHORT).show();
+    }
+}

BIN
vision-sample/src/main/res/drawable-hdpi/ic_launcher.png


BIN
vision-sample/src/main/res/drawable-mdpi/ic_launcher.png


BIN
vision-sample/src/main/res/drawable-xhdpi/ic_launcher.png


BIN
vision-sample/src/main/res/drawable-xxhdpi/ic_launcher.png


+ 21 - 0
vision-sample/src/main/res/layout/activity_full_scanner.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:background="@color/actionbar_opacity"
+        android:minHeight="?attr/actionBarSize"
+        app:theme="@style/TransparentToolbar"/>
+</FrameLayout>

+ 29 - 0
vision-sample/src/main/res/layout/activity_full_scanner_fragment.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout 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:orientation="vertical">
+
+    <fragment
+        android:id="@+id/scanner_fragment"
+        android:name="me.dm7.barcodescanner.vision.sample.FullScannerFragment"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <ImageView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="top"
+        android:src="@drawable/ic_launcher"
+        tools:ignore="ContentDescription"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center|bottom"
+        android:text="Place a barcode in the viewfinder rectangle to scan it."
+        android:textColor="#ffffff"
+        tools:ignore="HardcodedText"/>
+</FrameLayout>

+ 51 - 0
vision-sample/src/main/res/layout/activity_main.xml

@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical">
+
+    <android.support.design.widget.AppBarLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </android.support.design.widget.AppBarLayout>
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:orientation="vertical">
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:onClick="launchSimpleActivity"
+                android:text="@string/simple_activity_sample"/>
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:onClick="launchFullActivity"
+                android:text="@string/full_activity_sample"/>
+
+            <Button
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:onClick="launchFullFragmentActivity"
+                android:text="@string/full_fragment_sample"/>
+
+        </LinearLayout>
+    </ScrollView>
+</LinearLayout>

+ 12 - 0
vision-sample/src/main/res/values/colors.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="actionbar_opacity">#33000000</color>
+    <color name="primary">#3F51B5</color>
+    <color name="primary_dark">#303F9F</color>
+    <color name="primary_light">#C5CAE9</color>
+    <color name="accent">#E040FB</color>
+    <color name="primary_text">#212121</color>
+    <color name="secondary_text">#727272</color>
+    <color name="icons">#FFFFFF</color>
+    <color name="divider">#B6B6B6</color>
+</resources>

+ 7 - 0
vision-sample/src/main/res/values/ids.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="id" name="menu_flash" />
+    <item type="id" name="menu_auto_focus" />
+    <item type="id" name="menu_formats" />
+    <item type="id" name="menu_camera_selector" />
+</resources>

+ 19 - 0
vision-sample/src/main/res/values/strings.xml

@@ -0,0 +1,19 @@
+<resources>
+    <string name="app_name">Google Mobile Vision Scanner Sample</string>
+    <string name="scanner_activity">Google Mobile Vision Scanner - Full Activity</string>
+    <string name="simple_scanner_activity">Google Mobile Vision Scanner - Simple Activity</string>
+    <string name="simple_activity_sample">Simple Activity Sample</string>
+    <string name="full_activity_sample">Full Activity Sample</string>
+    <string name="full_fragment_sample">Full Fragment Sample</string>
+
+    <string name="toggle_flash">Flash</string>
+    <string name="flash_on">Flash [ON]</string>
+    <string name="flash_off">Flash [OFF]</string>
+    <string name="auto_focus_on">Auto Focus [ON]</string>
+    <string name="auto_focus_off">Auto Focus [OFF]</string>
+    <string name="formats">Formats</string>
+    <string name="choose_formats">Choose Formats</string>
+    <string name="select_camera">Select a Camera</string>
+    <string name="ok_button">OK</string>
+    <string name="cancel_button">Cancel</string>
+</resources>

+ 36 - 0
vision-sample/src/main/res/values/styles.xml

@@ -0,0 +1,36 @@
+<resources>
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="colorPrimary">@color/primary</item>
+        <!-- darker variant for the status bar and contextual app bars -->
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <!--   theme UI controls like checkboxes and text fields e.g. FloatActionButton -->
+        <item name="colorAccent">@color/accent</item>
+        <!-- Title Text Color -->
+        <item name="android:textColorPrimary">@color/primary_text</item>
+        <!-- color of the menu overflow icon (three vertical dots) -->
+        <item name="android:textColorSecondary">@color/secondary_text</item>
+    </style>
+
+    <style name="AppOverlayTheme" parent="@style/Theme.AppCompat.Light">
+        <item name="windowNoTitle">true</item>
+        <item name="windowActionBar">false</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+
+        <item name="colorPrimary">@color/primary</item>
+        <!-- darker variant for the status bar and contextual app bars -->
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <!--   theme UI controls like checkboxes and text fields e.g. FloatActionButton -->
+        <item name="colorAccent">@color/accent</item>
+        <!-- Title Text Color -->
+        <item name="android:textColorPrimary">@color/primary_text</item>
+        <!-- color of the menu overflow icon (three vertical dots) -->
+        <item name="android:textColorSecondary">@color/secondary_text</item>
+    </style>
+
+    <style name="TransparentToolbar" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+        <item name="android:windowActionBarOverlay">true</item>
+        <!-- Support Library compability -->
+        <item name="windowActionBarOverlay">true</item>
+    </style>
+</resources>

+ 1 - 0
vision/.gitignore

@@ -0,0 +1 @@
+/build

+ 14 - 0
vision/build.gradle

@@ -0,0 +1,14 @@
+apply plugin: 'com.android.library'
+
+ext {
+    isLibrary = true
+    pomPackaging = "aar"
+    pomArtifactId = "vision"
+    pomName = "Google Mobile Vision Scanner View"
+    pomDescription = 'An android library project which contains the Google mobile vision barcode scanner view'
+}
+
+dependencies {
+    compile project(":core")
+    compile 'com.google.android.gms:play-services-vision:10.2.1'
+}

+ 8 - 0
vision/src/main/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="me.dm7.barcodescanner.vision">
+
+    <uses-permission android:name="android.permission.CAMERA"/>
+    <application/>
+</manifest>

+ 65 - 0
vision/src/main/java/me/dm7/barcodescanner/vision/BarcodeFormat.java

@@ -0,0 +1,65 @@
+package me.dm7.barcodescanner.vision;
+
+
+import com.google.android.gms.vision.barcode.Barcode;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BarcodeFormat {
+    public static final BarcodeFormat CODE_128 = new BarcodeFormat(Barcode.CODE_128, "CODE_128");
+    public static final BarcodeFormat CODE_39 = new BarcodeFormat(Barcode.CODE_39, "CODE_39");
+    public static final BarcodeFormat CODE_93 = new BarcodeFormat(Barcode.CODE_93, "CODE_93");
+    public static final BarcodeFormat CODABAR = new BarcodeFormat(Barcode.CODABAR, "CODABAR");
+    public static final BarcodeFormat DATA_MATRIX = new BarcodeFormat(Barcode.DATA_MATRIX, "DATA_MATRIX");
+    public static final BarcodeFormat EAN_13 = new BarcodeFormat(Barcode.EAN_13, "EAN_13");
+    public static final BarcodeFormat EAN_8 = new BarcodeFormat(Barcode.EAN_8, "EAN_8");
+    public static final BarcodeFormat ITF = new BarcodeFormat(Barcode.ITF, "ITF");
+    public static final BarcodeFormat QR_CODE = new BarcodeFormat(Barcode.QR_CODE, "QR_CODE");
+    public static final BarcodeFormat UPC_A = new BarcodeFormat(Barcode.UPC_A, "UPC_A");
+    public static final BarcodeFormat UPC_E = new BarcodeFormat(Barcode.UPC_E, "UPC_E");
+    public static final BarcodeFormat PDF417 = new BarcodeFormat(Barcode.PDF417, "PDF417");
+    public static final BarcodeFormat AZTEC = new BarcodeFormat(Barcode.AZTEC, "AZTEC");
+    public static final List<BarcodeFormat> ALL_FORMATS = new ArrayList<BarcodeFormat>();
+
+    static {
+        ALL_FORMATS.add(BarcodeFormat.CODE_128);
+        ALL_FORMATS.add(BarcodeFormat.CODE_39);
+        ALL_FORMATS.add(BarcodeFormat.CODE_93);
+        ALL_FORMATS.add(BarcodeFormat.CODABAR);
+        ALL_FORMATS.add(BarcodeFormat.DATA_MATRIX);
+        ALL_FORMATS.add(BarcodeFormat.EAN_13);
+        ALL_FORMATS.add(BarcodeFormat.EAN_8);
+        ALL_FORMATS.add(BarcodeFormat.ITF);
+        ALL_FORMATS.add(BarcodeFormat.QR_CODE);
+        ALL_FORMATS.add(BarcodeFormat.UPC_A);
+        ALL_FORMATS.add(BarcodeFormat.UPC_E);
+        ALL_FORMATS.add(BarcodeFormat.PDF417);
+        ALL_FORMATS.add(BarcodeFormat.AZTEC);
+    }
+
+    private int mId;
+    private String mName;
+
+    public BarcodeFormat(int id, String name) {
+        mId = id;
+        mName = name;
+    }
+
+    public static BarcodeFormat getFormatById(int id) {
+        for (BarcodeFormat format : ALL_FORMATS) {
+            if (format.getId() == id) {
+                return format;
+            }
+        }
+        throw new IllegalArgumentException("Invalid Barcode Format id : " + id);
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public String getName() {
+        return mName;
+    }
+}

+ 35 - 0
vision/src/main/java/me/dm7/barcodescanner/vision/Result.java

@@ -0,0 +1,35 @@
+package me.dm7.barcodescanner.vision;
+
+import com.google.android.gms.vision.barcode.Barcode;
+
+public class Result {
+    private Barcode barcode;
+
+    private Result(Builder builder) {
+        this.barcode = builder.barcode;
+    }
+
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    public Barcode getBarcode() {
+        return barcode;
+    }
+
+    public static final class Builder {
+        private Barcode barcode;
+
+        private Builder() {
+        }
+
+        public Builder withBarcode(Barcode val) {
+            barcode = val;
+            return this;
+        }
+
+        public Result build() {
+            return new Result(this);
+        }
+    }
+}

+ 175 - 0
vision/src/main/java/me/dm7/barcodescanner/vision/VisionScannerView.java

@@ -0,0 +1,175 @@
+package me.dm7.barcodescanner.vision;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.google.android.gms.vision.Detector;
+import com.google.android.gms.vision.Frame;
+import com.google.android.gms.vision.barcode.Barcode;
+import com.google.android.gms.vision.barcode.BarcodeDetector;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+import me.dm7.barcodescanner.core.BarcodeScannerView;
+import me.dm7.barcodescanner.core.DisplayUtils;
+
+public class VisionScannerView extends BarcodeScannerView {
+    private static final String TAG = "VisionScannerView";
+
+    private List<BarcodeFormat> mFormats;
+    private ResultHandler mResultHandler;
+    private BarcodeDetector barcodeDetector;
+
+    public VisionScannerView(Context context) {
+        super(context);
+        setupScanner();
+    }
+
+    public VisionScannerView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+        setupScanner();
+    }
+
+    public void setResultHandler(ResultHandler resultHandler) {
+        mResultHandler = resultHandler;
+    }
+
+    public List<BarcodeFormat> getFormats() {
+        if (mFormats == null) {
+            return BarcodeFormat.ALL_FORMATS;
+        }
+        return mFormats;
+    }
+
+    public void setFormats(List<BarcodeFormat> formats) {
+        mFormats = formats;
+        setupScanner();
+    }
+
+    public void setupScanner() {
+
+
+        barcodeDetector = new BarcodeDetector.Builder(getContext())
+                .setBarcodeFormats(getSupportedFormatBitmapMask()).build();
+        barcodeDetector.setProcessor(new Detector.Processor<Barcode>() {
+            @Override
+            public void release() {
+                // Handled via public method
+            }
+
+            @Override
+            public void receiveDetections(Detector.Detections<Barcode> detections) {
+                final SparseArray<Barcode> barcodes = detections.getDetectedItems();
+                if (barcodes.size() != 0) {
+                    // TODO: 3/22/17
+//                    onItemDetected(barcodes.valueAt(0));
+                }
+            }
+        });
+
+
+    }
+
+    @Override
+    public void onPreviewFrame(byte[] data, Camera camera) {
+        if (mResultHandler == null) {
+            return;
+        }
+
+        try {
+            Camera.Parameters parameters = camera.getParameters();
+            Camera.Size size = parameters.getPreviewSize();
+            int width = size.width;
+            int height = size.height;
+
+            if (DisplayUtils.getScreenOrientation(getContext()) == Configuration.ORIENTATION_PORTRAIT) {
+                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;
+            }
+
+            SparseArray<Barcode> barcodeSparseArray = barcodeDetector.detect(constructFrame(width, height, data));
+
+            if (barcodeSparseArray.size() != 0) {
+                final Result rawResult = Result.newBuilder().withBarcode(barcodeSparseArray.valueAt(0)).build();
+                Handler handler = new Handler(Looper.getMainLooper());
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Stopping the preview can take a little long.
+                        // So we want to set result handler to null to discard subsequent calls to
+                        // onPreviewFrame.
+                        ResultHandler tmpResultHandler = mResultHandler;
+                        mResultHandler = null;
+
+                        stopCameraPreview();
+                        if (tmpResultHandler != null) {
+                            tmpResultHandler.handleResult(rawResult);
+                        }
+                    }
+                });
+            } else {
+                camera.setOneShotPreviewCallback(this);
+            }
+        } catch (RuntimeException e) {
+            // TODO: Terrible hack. It is possible that this method is invoked after camera is released.
+            Log.e(TAG, e.toString(), e);
+        }
+    }
+
+    private Frame constructFrame(int width, int height, byte[] data) {
+
+
+        ByteBuffer pendingFrameData = ByteBuffer.wrap(data);
+
+        return new Frame.Builder()
+                .setImageData(pendingFrameData, width,
+                        height, ImageFormat.NV21)
+                .build();
+
+    }
+
+    public void resumeCameraPreview(ResultHandler resultHandler) {
+        mResultHandler = resultHandler;
+        super.resumeCameraPreview();
+    }
+
+    private int getSupportedFormatBitmapMask() {
+        final List<BarcodeFormat> formats = getFormats();
+
+
+        if (formats == null || formats.size() == 0) {
+            return Barcode.ALL_FORMATS;
+        } else if (formats.size() == 1) {
+            return formats.get(0).getId();
+        } else {
+            int bitmapMask = formats.get(0).getId();
+
+            for (int i = 1; i < formats.size(); i++) {
+                bitmapMask |= formats.get(i).getId();
+            }
+
+            return bitmapMask;
+        }
+
+
+    }
+
+    public interface ResultHandler {
+        void handleResult(Result rawResult);
+    }
+}

+ 16 - 0
zbar-sample/build.gradle

@@ -0,0 +1,16 @@
+apply plugin: 'com.android.application'
+
+dependencies {
+    //compile project(":zbar")
+    compile libraries.barcodescanner_zbar
+    compile libraries.support_v4
+    compile libraries.appcompat_v7
+    compile libraries.design_support
+}
+
+android {
+    defaultConfig {
+        applicationId "${project.group}.zbar.sample"
+    }
+    buildToolsVersion '25.0.2'
+}

+ 44 - 0
zbar-sample/src/main/AndroidManifest.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="me.dm7.barcodescanner.zbar.sample">
+  <uses-permission android:name="android.permission.CAMERA" />
+
+  <application android:allowBackup="true"
+        android:label="@string/app_name"
+        android:icon="@drawable/ic_launcher">
+        <activity android:name=".MainActivity"
+                  android:theme="@style/AppTheme"
+                  android:label="@string/app_name">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".FullScannerActivity"
+                  android:theme="@style/AppOverlayTheme"
+                  android:uiOptions="splitActionBarWhenNarrow"
+                  android:label="@string/scanner_activity">
+            <meta-data android:name="android.support.UI_OPTIONS"
+                       android:value="splitActionBarWhenNarrow" />
+        </activity>
+
+        <activity android:name=".FullScannerFragmentActivity"
+                  android:theme="@style/AppOverlayTheme"
+                  android:uiOptions="splitActionBarWhenNarrow"
+                  android:label="@string/scanner_fragment_activity">
+            <meta-data android:name="android.support.UI_OPTIONS"
+                       android:value="splitActionBarWhenNarrow" />
+        </activity>
+
+        <activity android:name=".SimpleScannerActivity"
+                android:theme="@style/AppOverlayTheme"
+                android:label="@string/simple_scanner_activity">
+        </activity>
+
+        <activity android:name=".SimpleScannerFragmentActivity"
+                android:theme="@style/AppOverlayTheme"
+                android:label="@string/simple_scanner_fragment_activity">
+        </activity>
+    </application>
+</manifest>

+ 28 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/BaseScannerActivity.java

@@ -0,0 +1,28 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+
+public class BaseScannerActivity extends AppCompatActivity {
+    public void setupToolbar() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+        final ActionBar ab = getSupportActionBar();
+        if(ab != null) {
+            ab.setDisplayHomeAsUpEnabled(true);
+        }
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+            // Respond to the action bar's Up/Home button
+            case android.R.id.home:
+                finish();
+                return true;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}

+ 87 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/CameraSelectorDialogFragment.java

@@ -0,0 +1,87 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.hardware.Camera;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class CameraSelectorDialogFragment extends DialogFragment {
+    public interface CameraSelectorDialogListener {
+        public void onCameraSelected(int cameraId);
+    }
+
+    private int mCameraId;
+    private CameraSelectorDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static CameraSelectorDialogFragment newInstance(CameraSelectorDialogListener listener, int cameraId) {
+        CameraSelectorDialogFragment fragment = new CameraSelectorDialogFragment();
+        fragment.mCameraId = cameraId;
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        if(mListener == null) {
+            dismiss();
+            return null;
+        }
+
+        int numberOfCameras = Camera.getNumberOfCameras();
+        String[] cameraNames = new String[numberOfCameras];
+        int checkedIndex = 0;
+
+        for (int i = 0; i < numberOfCameras; i++) {
+            Camera.CameraInfo info = new Camera.CameraInfo();
+            Camera.getCameraInfo(i, info);
+            if(info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+                cameraNames[i] = "Front Facing";
+            } else if(info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
+                cameraNames[i] = "Rear Facing";
+            } else {
+                cameraNames[i] = "Camera ID: " + i;
+            }
+            if(i == mCameraId) {
+                checkedIndex = i;
+            }
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        // Set the dialog title
+        builder.setTitle(R.string.select_camera)
+                // Specify the list array, the items to be selected by default (null for none),
+                // and the listener through which to receive callbacks when items are selected
+                .setSingleChoiceItems(cameraNames, checkedIndex,
+                        new DialogInterface.OnClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which) {
+                                mCameraId = which;
+                            }
+                        })
+                        // Set the action buttons
+                .setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        // User clicked OK, so save the mSelectedIndices results somewhere
+                        // or return them to the component that opened the dialog
+                        if (mListener != null) {
+                            mListener.onCameraSelected(mCameraId);
+                        }
+                    }
+                })
+                .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                    }
+                });
+
+        return builder.create();
+    }
+}

+ 94 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FormatSelectorDialogFragment.java

@@ -0,0 +1,94 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+import java.util.ArrayList;
+
+import me.dm7.barcodescanner.zbar.BarcodeFormat;
+import me.dm7.barcodescanner.zbar.ZBarScannerView;
+
+public class FormatSelectorDialogFragment extends DialogFragment {
+    public interface FormatSelectorDialogListener {
+        public void onFormatsSaved(ArrayList<Integer> selectedIndices);
+    }
+
+    private ArrayList<Integer> mSelectedIndices;
+    private FormatSelectorDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static FormatSelectorDialogFragment newInstance(FormatSelectorDialogListener listener, ArrayList<Integer> selectedIndices) {
+        FormatSelectorDialogFragment fragment = new FormatSelectorDialogFragment();
+        if(selectedIndices == null) {
+            selectedIndices = new ArrayList<Integer>();
+        }
+        fragment.mSelectedIndices = new ArrayList<Integer>(selectedIndices);
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        if(mSelectedIndices == null || mListener == null) {
+            dismiss();
+            return null;
+        }
+
+        String[] formats = new String[BarcodeFormat.ALL_FORMATS.size()];
+        boolean[] checkedIndices = new boolean[BarcodeFormat.ALL_FORMATS.size()];
+        int i = 0;
+        for(BarcodeFormat format : BarcodeFormat.ALL_FORMATS) {
+            formats[i] = format.getName();
+            if(mSelectedIndices.contains(i)) {
+                checkedIndices[i] = true;
+            } else {
+                checkedIndices[i] = false;
+            }
+            i++;
+        }
+
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        // Set the dialog title
+        builder.setTitle(R.string.choose_formats)
+                // Specify the list array, the items to be selected by default (null for none),
+                // and the listener through which to receive callbacks when items are selected
+                .setMultiChoiceItems(formats, checkedIndices,
+                        new DialogInterface.OnMultiChoiceClickListener() {
+                            @Override
+                            public void onClick(DialogInterface dialog, int which, boolean isChecked) {
+                                if (isChecked) {
+                                    // If the user checked the item, add it to the selected items
+                                    mSelectedIndices.add(which);
+                                } else if (mSelectedIndices.contains(which)) {
+                                    // Else, if the item is already in the array, remove it
+                                    mSelectedIndices.remove(mSelectedIndices.indexOf(which));
+                                }
+                            }
+                        })
+                        // Set the action buttons
+                .setPositiveButton(R.string.ok_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                        // User clicked OK, so save the mSelectedIndices results somewhere
+                        // or return them to the component that opened the dialog
+                        if (mListener != null) {
+                            mListener.onFormatsSaved(mSelectedIndices);
+                        }
+                    }
+                })
+                .setNegativeButton(R.string.cancel_button, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int id) {
+                    }
+                });
+
+        return builder.create();
+    }
+}

+ 216 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerActivity.java

@@ -0,0 +1,216 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.view.MenuItemCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.dm7.barcodescanner.zbar.BarcodeFormat;
+import me.dm7.barcodescanner.zbar.Result;
+import me.dm7.barcodescanner.zbar.ZBarScannerView;
+
+public class FullScannerActivity extends BaseScannerActivity implements MessageDialogFragment.MessageDialogListener,
+        ZBarScannerView.ResultHandler, FormatSelectorDialogFragment.FormatSelectorDialogListener,
+        CameraSelectorDialogFragment.CameraSelectorDialogListener {
+    private static final String FLASH_STATE = "FLASH_STATE";
+    private static final String AUTO_FOCUS_STATE = "AUTO_FOCUS_STATE";
+    private static final String SELECTED_FORMATS = "SELECTED_FORMATS";
+    private static final String CAMERA_ID = "CAMERA_ID";
+    private ZBarScannerView mScannerView;
+    private boolean mFlash;
+    private boolean mAutoFocus;
+    private ArrayList<Integer> mSelectedIndices;
+    private int mCameraId = -1;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        if(state != null) {
+            mFlash = state.getBoolean(FLASH_STATE, false);
+            mAutoFocus = state.getBoolean(AUTO_FOCUS_STATE, true);
+            mSelectedIndices = state.getIntegerArrayList(SELECTED_FORMATS);
+            mCameraId = state.getInt(CAMERA_ID, -1);
+        } else {
+            mFlash = false;
+            mAutoFocus = true;
+            mSelectedIndices = null;
+            mCameraId = -1;
+        }
+
+        setContentView(R.layout.activity_full_scanner);
+        setupToolbar();
+        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);
+        mScannerView = new ZBarScannerView(this);
+        setupFormats();
+        contentFrame.addView(mScannerView);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(FLASH_STATE, mFlash);
+        outState.putBoolean(AUTO_FOCUS_STATE, mAutoFocus);
+        outState.putIntegerArrayList(SELECTED_FORMATS, mSelectedIndices);
+        outState.putInt(CAMERA_ID, mCameraId);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuItem menuItem;
+
+        if(mFlash) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+
+        if(mAutoFocus) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_formats, 0, R.string.formats);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_camera_selector, 0, R.string.select_camera);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        return super.onCreateOptionsMenu(menu);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle presses on the action bar items
+        switch (item.getItemId()) {
+            case R.id.menu_flash:
+                mFlash = !mFlash;
+                if(mFlash) {
+                    item.setTitle(R.string.flash_on);
+                } else {
+                    item.setTitle(R.string.flash_off);
+                }
+                mScannerView.setFlash(mFlash);
+                return true;
+            case R.id.menu_auto_focus:
+                mAutoFocus = !mAutoFocus;
+                if(mAutoFocus) {
+                    item.setTitle(R.string.auto_focus_on);
+                } else {
+                    item.setTitle(R.string.auto_focus_off);
+                }
+                mScannerView.setAutoFocus(mAutoFocus);
+                return true;
+            case R.id.menu_formats:
+                DialogFragment fragment = FormatSelectorDialogFragment.newInstance(this, mSelectedIndices);
+                fragment.show(getSupportFragmentManager(), "format_selector");
+                return true;
+            case R.id.menu_camera_selector:
+                mScannerView.stopCamera();
+                DialogFragment cFragment = CameraSelectorDialogFragment.newInstance(this, mCameraId);
+                cFragment.show(getSupportFragmentManager(), "camera_selector");
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        try {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+            Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+            r.play();
+        } catch (Exception e) {}
+        showMessageDialog("Contents = " + rawResult.getContents() + ", Format = " + rawResult.getBarcodeFormat().getName());
+    }
+
+    public void showMessageDialog(String message) {
+        DialogFragment fragment = MessageDialogFragment.newInstance("Scan Results", message, this);
+        fragment.show(getSupportFragmentManager(), "scan_results");
+    }
+
+    public void closeMessageDialog() {
+        closeDialog("scan_results");
+    }
+
+    public void closeFormatsDialog() {
+        closeDialog("format_selector");
+    }
+
+    public void closeDialog(String dialogName) {
+        FragmentManager fragmentManager = getSupportFragmentManager();
+        DialogFragment fragment = (DialogFragment) fragmentManager.findFragmentByTag(dialogName);
+        if(fragment != null) {
+            fragment.dismiss();
+        }
+    }
+
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // Resume the camera
+        mScannerView.resumeCameraPreview(this);
+    }
+
+    @Override
+    public void onFormatsSaved(ArrayList<Integer> selectedIndices) {
+        mSelectedIndices = selectedIndices;
+        setupFormats();
+    }
+
+    @Override
+    public void onCameraSelected(int cameraId) {
+        mCameraId = cameraId;
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+
+    public void setupFormats() {
+        List<BarcodeFormat> formats = new ArrayList<BarcodeFormat>();
+        if(mSelectedIndices == null || mSelectedIndices.isEmpty()) {
+            mSelectedIndices = new ArrayList<Integer>();
+            for(int i = 0; i < BarcodeFormat.ALL_FORMATS.size(); i++) {
+                mSelectedIndices.add(i);
+            }
+        }
+
+        for(int index : mSelectedIndices) {
+            formats.add(BarcodeFormat.ALL_FORMATS.get(index));
+        }
+        if(mScannerView != null) {
+            mScannerView.setFormats(formats);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+        closeMessageDialog();
+        closeFormatsDialog();
+    }
+}

+ 218 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerFragment.java

@@ -0,0 +1,218 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.view.MenuItemCompat;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import me.dm7.barcodescanner.zbar.BarcodeFormat;
+import me.dm7.barcodescanner.zbar.Result;
+import me.dm7.barcodescanner.zbar.ZBarScannerView;
+
+public class FullScannerFragment extends Fragment implements MessageDialogFragment.MessageDialogListener,
+        ZBarScannerView.ResultHandler, FormatSelectorDialogFragment.FormatSelectorDialogListener,
+        CameraSelectorDialogFragment.CameraSelectorDialogListener {
+    private static final String FLASH_STATE = "FLASH_STATE";
+    private static final String AUTO_FOCUS_STATE = "AUTO_FOCUS_STATE";
+    private static final String SELECTED_FORMATS = "SELECTED_FORMATS";
+    private static final String CAMERA_ID = "CAMERA_ID";
+    private ZBarScannerView mScannerView;
+    private boolean mFlash;
+    private boolean mAutoFocus;
+    private ArrayList<Integer> mSelectedIndices;
+    private int mCameraId = -1;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+        mScannerView = new ZBarScannerView(getActivity());
+        if(state != null) {
+            mFlash = state.getBoolean(FLASH_STATE, false);
+            mAutoFocus = state.getBoolean(AUTO_FOCUS_STATE, true);
+            mSelectedIndices = state.getIntegerArrayList(SELECTED_FORMATS);
+            mCameraId = state.getInt(CAMERA_ID, -1);
+        } else {
+            mFlash = false;
+            mAutoFocus = true;
+            mSelectedIndices = null;
+            mCameraId = -1;
+        }
+        setupFormats();
+        return mScannerView;
+    }
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setHasOptionsMenu(true);
+    }
+
+    public void onCreateOptionsMenu (Menu menu, MenuInflater inflater) {
+        super.onCreateOptionsMenu(menu, inflater);
+
+        MenuItem menuItem;
+
+        if(mFlash) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_flash, 0, R.string.flash_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+
+        if(mAutoFocus) {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_on);
+        } else {
+            menuItem = menu.add(Menu.NONE, R.id.menu_auto_focus, 0, R.string.auto_focus_off);
+        }
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_formats, 0, R.string.formats);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+
+        menuItem = menu.add(Menu.NONE, R.id.menu_camera_selector, 0, R.string.select_camera);
+        MenuItemCompat.setShowAsAction(menuItem, MenuItem.SHOW_AS_ACTION_NEVER);
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        // Handle presses on the action bar items
+        switch (item.getItemId()) {
+            case R.id.menu_flash:
+                mFlash = !mFlash;
+                if(mFlash) {
+                    item.setTitle(R.string.flash_on);
+                } else {
+                    item.setTitle(R.string.flash_off);
+                }
+                mScannerView.setFlash(mFlash);
+                return true;
+            case R.id.menu_auto_focus:
+                mAutoFocus = !mAutoFocus;
+                if(mAutoFocus) {
+                    item.setTitle(R.string.auto_focus_on);
+                } else {
+                    item.setTitle(R.string.auto_focus_off);
+                }
+                mScannerView.setAutoFocus(mAutoFocus);
+                return true;
+            case R.id.menu_formats:
+                DialogFragment fragment = FormatSelectorDialogFragment.newInstance(this, mSelectedIndices);
+                fragment.show(getActivity().getSupportFragmentManager(), "format_selector");
+                return true;
+            case R.id.menu_camera_selector:
+                mScannerView.stopCamera();
+                DialogFragment cFragment = CameraSelectorDialogFragment.newInstance(this, mCameraId);
+                cFragment.show(getActivity().getSupportFragmentManager(), "camera_selector");
+                return true;
+            default:
+                return super.onOptionsItemSelected(item);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putBoolean(FLASH_STATE, mFlash);
+        outState.putBoolean(AUTO_FOCUS_STATE, mAutoFocus);
+        outState.putIntegerArrayList(SELECTED_FORMATS, mSelectedIndices);
+        outState.putInt(CAMERA_ID, mCameraId);
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        try {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+            Ringtone r = RingtoneManager.getRingtone(getActivity().getApplicationContext(), notification);
+            r.play();
+        } catch (Exception e) {}
+        showMessageDialog("Contents = " + rawResult.getContents() + ", Format = " + rawResult.getBarcodeFormat().getName());
+    }
+
+    public void showMessageDialog(String message) {
+        DialogFragment fragment = MessageDialogFragment.newInstance("Scan Results", message, this);
+        fragment.show(getActivity().getSupportFragmentManager(), "scan_results");
+    }
+
+    public void closeMessageDialog() {
+        closeDialog("scan_results");
+    }
+
+    public void closeFormatsDialog() {
+        closeDialog("format_selector");
+    }
+
+    public void closeDialog(String dialogName) {
+        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
+        DialogFragment fragment = (DialogFragment) fragmentManager.findFragmentByTag(dialogName);
+        if(fragment != null) {
+            fragment.dismiss();
+        }
+    }
+
+    @Override
+    public void onDialogPositiveClick(DialogFragment dialog) {
+        // Resume the camera
+        mScannerView.resumeCameraPreview(this);
+    }
+
+    @Override
+    public void onFormatsSaved(ArrayList<Integer> selectedIndices) {
+        mSelectedIndices = selectedIndices;
+        setupFormats();
+    }
+
+    @Override
+    public void onCameraSelected(int cameraId) {
+        mCameraId = cameraId;
+        mScannerView.startCamera(mCameraId);
+        mScannerView.setFlash(mFlash);
+        mScannerView.setAutoFocus(mAutoFocus);
+    }
+
+    public void setupFormats() {
+        List<BarcodeFormat> formats = new ArrayList<BarcodeFormat>();
+        if(mSelectedIndices == null || mSelectedIndices.isEmpty()) {
+            mSelectedIndices = new ArrayList<Integer>();
+            for(int i = 0; i < BarcodeFormat.ALL_FORMATS.size(); i++) {
+                mSelectedIndices.add(i);
+            }
+        }
+
+        for(int index : mSelectedIndices) {
+            formats.add(BarcodeFormat.ALL_FORMATS.get(index));
+        }
+        if(mScannerView != null) {
+            mScannerView.setFormats(formats);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+        closeMessageDialog();
+        closeFormatsDialog();
+    }
+}

+ 12 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/FullScannerFragmentActivity.java

@@ -0,0 +1,12 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.os.Bundle;
+
+public class FullScannerFragmentActivity extends BaseScannerActivity {
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_full_scanner_fragment);
+        setupToolbar();
+    }
+}

+ 73 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/MainActivity.java

@@ -0,0 +1,73 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.Manifest;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.View;
+import android.widget.Toast;
+
+public class MainActivity extends AppCompatActivity {
+    private static final int ZBAR_CAMERA_PERMISSION = 1;
+    private Class<?> mClss;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_main);
+        setupToolbar();
+    }
+
+    public void setupToolbar() {
+        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+        setSupportActionBar(toolbar);
+    }
+
+    public void launchSimpleActivity(View v) {
+        launchActivity(SimpleScannerActivity.class);
+    }
+
+    public void launchSimpleFragmentActivity(View v) {
+        launchActivity(SimpleScannerFragmentActivity.class);
+    }
+
+    public void launchFullActivity(View v) {
+        launchActivity(FullScannerActivity.class);
+    }
+
+    public void launchFullFragmentActivity(View v) {
+        launchActivity(FullScannerFragmentActivity.class);
+    }
+
+    public void launchActivity(Class<?> clss) {
+        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
+                != PackageManager.PERMISSION_GRANTED) {
+            mClss = clss;
+            ActivityCompat.requestPermissions(this,
+                    new String[]{Manifest.permission.CAMERA}, ZBAR_CAMERA_PERMISSION);
+        } else {
+            Intent intent = new Intent(this, clss);
+            startActivity(intent);
+        }
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode,  String permissions[], int[] grantResults) {
+        switch (requestCode) {
+            case ZBAR_CAMERA_PERMISSION:
+                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    if(mClss != null) {
+                        Intent intent = new Intent(this, mClss);
+                        startActivity(intent);
+                    }
+                } else {
+                    Toast.makeText(this, "Please grant camera permission to use the QR Scanner", Toast.LENGTH_SHORT).show();
+                }
+                return;
+        }
+    }
+}

+ 47 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/MessageDialogFragment.java

@@ -0,0 +1,47 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+public class MessageDialogFragment extends DialogFragment {
+    public interface MessageDialogListener {
+        public void onDialogPositiveClick(DialogFragment dialog);
+    }
+
+    private String mTitle;
+    private String mMessage;
+    private MessageDialogListener mListener;
+
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setRetainInstance(true);
+    }
+
+    public static MessageDialogFragment newInstance(String title, String message, MessageDialogListener listener) {
+        MessageDialogFragment fragment = new MessageDialogFragment();
+        fragment.mTitle = title;
+        fragment.mMessage = message;
+        fragment.mListener = listener;
+        return fragment;
+    }
+
+    @Override
+    public Dialog onCreateDialog(Bundle savedInstanceState) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+        builder.setMessage(mMessage)
+                .setTitle(mTitle);
+
+        builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int id) {
+                if(mListener != null) {
+                    mListener.onDialogPositiveClick(MessageDialogFragment.this);
+                }
+            }
+        });
+
+        return builder.create();
+    }
+}

+ 53 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerActivity.java

@@ -0,0 +1,53 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import me.dm7.barcodescanner.zbar.Result;
+import me.dm7.barcodescanner.zbar.ZBarScannerView;
+
+public class SimpleScannerActivity extends BaseScannerActivity implements ZBarScannerView.ResultHandler {
+    private ZBarScannerView mScannerView;
+
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_simple_scanner);
+        setupToolbar();
+        ViewGroup contentFrame = (ViewGroup) findViewById(R.id.content_frame);
+        mScannerView = new ZBarScannerView(this);
+        contentFrame.addView(mScannerView);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera();
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        Toast.makeText(this, "Contents = " + rawResult.getContents() +
+                ", Format = " + rawResult.getBarcodeFormat().getName(), Toast.LENGTH_SHORT).show();
+        // Note:
+        // * Wait 2 seconds to resume the preview.
+        // * On older devices continuously stopping and resuming camera preview can result in freezing the app.
+        // * I don't know why this is the case but I don't have the time to figure out.
+        Handler handler = new Handler();
+        handler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mScannerView.resumeCameraPreview(SimpleScannerActivity.this);
+            }
+        }, 2000);
+    }
+}

+ 52 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerFragment.java

@@ -0,0 +1,52 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import me.dm7.barcodescanner.zbar.Result;
+import me.dm7.barcodescanner.zbar.ZBarScannerView;
+
+public class SimpleScannerFragment extends Fragment implements ZBarScannerView.ResultHandler {
+    private ZBarScannerView mScannerView;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        mScannerView = new ZBarScannerView(getActivity());
+        return mScannerView;
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        mScannerView.setResultHandler(this);
+        mScannerView.startCamera();
+    }
+
+    @Override
+    public void handleResult(Result rawResult) {
+        Toast.makeText(getActivity(), "Contents = " + rawResult.getContents() +
+                ", Format = " + rawResult.getBarcodeFormat().getName(), Toast.LENGTH_SHORT).show();
+        // Note:
+        // * Wait 2 seconds to resume the preview.
+        // * On older devices continuously stopping and resuming camera preview can result in freezing the app.
+        // * I don't know why this is the case but I don't have the time to figure out.
+        Handler handler = new Handler();
+        handler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                mScannerView.resumeCameraPreview(SimpleScannerFragment.this);
+            }
+        }, 2000);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        mScannerView.stopCamera();
+    }
+}

+ 12 - 0
zbar-sample/src/main/java/me/dm7/barcodescanner/zbar/sample/SimpleScannerFragmentActivity.java

@@ -0,0 +1,12 @@
+package me.dm7.barcodescanner.zbar.sample;
+
+import android.os.Bundle;
+
+public class SimpleScannerFragmentActivity extends BaseScannerActivity {
+    @Override
+    public void onCreate(Bundle state) {
+        super.onCreate(state);
+        setContentView(R.layout.activity_simple_scanner_fragment);
+        setupToolbar();
+    }
+}

BIN
zbar-sample/src/main/res/drawable-hdpi/ic_launcher.png


BIN
zbar-sample/src/main/res/drawable-mdpi/ic_launcher.png


BIN
zbar-sample/src/main/res/drawable-xhdpi/ic_launcher.png


BIN
zbar-sample/src/main/res/drawable-xxhdpi/ic_launcher.png


+ 22 - 0
zbar-sample/src/main/res/layout/activity_full_scanner.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_gravity="top"
+        android:minHeight="?attr/actionBarSize"
+        android:background="@color/actionbar_opacity"
+        app:theme="@style/TransparentToolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+</FrameLayout>

+ 23 - 0
zbar-sample/src/main/res/layout/activity_full_scanner_fragment.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment android:name="me.dm7.barcodescanner.zbar.sample.FullScannerFragment"
+              android:id="@+id/scanner_fragment"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_gravity="top"
+        android:minHeight="?attr/actionBarSize"
+        android:background="@color/actionbar_opacity"
+        app:theme="@style/TransparentToolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+</FrameLayout>

+ 42 - 0
zbar-sample/src/main/res/layout/activity_main.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+    <android.support.design.widget.AppBarLayout
+        app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
+        app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <android.support.v7.widget.Toolbar
+            android:id="@+id/toolbar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"/>
+    </android.support.design.widget.AppBarLayout>
+
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center">
+        <Button android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:text="@string/simple_activity_sample"
+                android:onClick="launchSimpleActivity" />
+        <Button android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:text="@string/simple_fragment_sample"
+                android:onClick="launchSimpleFragmentActivity" />
+        <Button android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:text="@string/activity_sample"
+                android:onClick="launchFullActivity" />
+        <Button android:layout_height="wrap_content"
+                android:layout_width="match_parent"
+                android:text="@string/fragment_sample"
+                android:onClick="launchFullFragmentActivity" />
+    </LinearLayout>
+</LinearLayout>

+ 22 - 0
zbar-sample/src/main/res/layout/activity_simple_scanner.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/content_frame"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_gravity="top"
+        android:minHeight="?attr/actionBarSize"
+        android:background="@color/actionbar_opacity"
+        app:theme="@style/TransparentToolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+</FrameLayout>

+ 23 - 0
zbar-sample/src/main/res/layout/activity_simple_scanner_fragment.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <fragment android:name="me.dm7.barcodescanner.zbar.sample.SimpleScannerFragment"
+              android:id="@+id/scanner_fragment"
+              android:layout_weight="1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent" />
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_gravity="top"
+        android:minHeight="?attr/actionBarSize"
+        android:background="@color/actionbar_opacity"
+        app:theme="@style/TransparentToolbar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+</FrameLayout>

+ 12 - 0
zbar-sample/src/main/res/values/colors.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="actionbar_opacity">#33000000</color>
+    <color name="primary">#3F51B5</color>
+    <color name="primary_dark">#303F9F</color>
+    <color name="primary_light">#C5CAE9</color>
+    <color name="accent">#E040FB</color>
+    <color name="primary_text">#212121</color>
+    <color name="secondary_text">#727272</color>
+    <color name="icons">#FFFFFF</color>
+    <color name="divider">#B6B6B6</color>
+</resources>

+ 7 - 0
zbar-sample/src/main/res/values/ids.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <item type="id" name="menu_flash" />
+    <item type="id" name="menu_auto_focus" />
+    <item type="id" name="menu_formats" />
+    <item type="id" name="menu_camera_selector" />
+</resources>

+ 21 - 0
zbar-sample/src/main/res/values/strings.xml

@@ -0,0 +1,21 @@
+<resources>
+    <string name="app_name">ZBar Scanner Sample</string>
+    <string name="scanner_activity">ZBar Scanner - Full Activity</string>
+    <string name="scanner_fragment_activity">ZBar Scanner - Full Fragment Activity</string>
+    <string name="simple_scanner_activity">ZBar Scanner - Simple Activity</string>
+    <string name="simple_scanner_fragment_activity">ZBar Scanner - Simple Fragment Activity</string>
+    <string name="simple_activity_sample">Simple Activity Sample</string>
+    <string name="simple_fragment_sample">Simple Fragment Sample</string>
+    <string name="activity_sample">Full Activity Sample</string>
+    <string name="fragment_sample">Full Fragment Sample</string>
+    <string name="toggle_flash">Flash</string>
+    <string name="flash_on">Flash [ON]</string>
+    <string name="flash_off">Flash [OFF]</string>
+    <string name="auto_focus_on">Auto Focus [ON]</string>
+    <string name="auto_focus_off">Auto Focus [OFF]</string>
+    <string name="formats">Formats</string>
+    <string name="choose_formats">Choose Formats</string>
+    <string name="select_camera">Select a Camera</string>
+    <string name="ok_button">OK</string>
+    <string name="cancel_button">Cancel</string>
+</resources>

+ 36 - 0
zbar-sample/src/main/res/values/styles.xml

@@ -0,0 +1,36 @@
+<resources>
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <item name="colorPrimary">@color/primary</item>
+        <!-- darker variant for the status bar and contextual app bars -->
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <!--   theme UI controls like checkboxes and text fields e.g. FloatActionButton -->
+        <item name="colorAccent">@color/accent</item>
+        <!-- Title Text Color -->
+        <item name="android:textColorPrimary">@color/primary_text</item>
+        <!-- color of the menu overflow icon (three vertical dots) -->
+        <item name="android:textColorSecondary">@color/secondary_text</item>
+    </style>
+
+    <style name="AppOverlayTheme" parent="@style/Theme.AppCompat.Light">
+        <item name="windowNoTitle">true</item>
+        <item name="windowActionBar">false</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:windowContentOverlay">@null</item>
+
+        <item name="colorPrimary">@color/primary</item>
+        <!-- darker variant for the status bar and contextual app bars -->
+        <item name="colorPrimaryDark">@color/primary_dark</item>
+        <!--   theme UI controls like checkboxes and text fields e.g. FloatActionButton -->
+        <item name="colorAccent">@color/accent</item>
+        <!-- Title Text Color -->
+        <item name="android:textColorPrimary">@color/primary_text</item>
+        <!-- color of the menu overflow icon (three vertical dots) -->
+        <item name="android:textColorSecondary">@color/secondary_text</item>
+    </style>
+
+    <style name="TransparentToolbar" parent="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
+        <item name="android:windowActionBarOverlay">true</item>
+        <!-- Support Library compability -->
+        <item name="windowActionBarOverlay">true</item>
+    </style>
+</resources>

+ 19 - 0
zbar/build.gradle

@@ -0,0 +1,19 @@
+apply plugin: 'com.android.library'
+
+ext {
+    isLibrary = true
+    pomPackaging = "aar"
+    pomArtifactId = "zbar"
+    pomName = "ZBar Scanner View"
+    pomDescription = 'An android library project which contains the zbar barcode scanner view'
+}
+
+dependencies {
+    //compile project(":core")
+    compile libraries.barcodescanner_core
+    compile fileTree(dir: 'libs', include: '*.jar')
+}
+
+android {
+    buildToolsVersion '25.0.2'
+}

BIN
zbar/libs/zbar.jar


+ 5 - 0
zbar/src/main/AndroidManifest.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="me.dm7.barcodescanner.zbar">
+    <application></application>
+</manifest>

+ 72 - 0
zbar/src/main/java/me/dm7/barcodescanner/zbar/BarcodeFormat.java

@@ -0,0 +1,72 @@
+package me.dm7.barcodescanner.zbar;
+
+import net.sourceforge.zbar.Symbol;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class BarcodeFormat {
+    private int mId;
+    private String mName;
+
+    public static final BarcodeFormat NONE = new BarcodeFormat(Symbol.NONE, "NONE");
+    public static final BarcodeFormat PARTIAL = new BarcodeFormat(Symbol.PARTIAL, "PARTIAL");
+    public static final BarcodeFormat EAN8 = new BarcodeFormat(Symbol.EAN8, "EAN8");
+    public static final BarcodeFormat UPCE = new BarcodeFormat(Symbol.UPCE, "UPCE");
+    public static final BarcodeFormat ISBN10 = new BarcodeFormat(Symbol.ISBN10, "ISBN10");
+    public static final BarcodeFormat UPCA = new BarcodeFormat(Symbol.UPCA, "UPCA");
+    public static final BarcodeFormat EAN13 = new BarcodeFormat(Symbol.EAN13, "EAN13");
+    public static final BarcodeFormat ISBN13 = new BarcodeFormat(Symbol.ISBN13, "ISBN13");
+    public static final BarcodeFormat I25 = new BarcodeFormat(Symbol.I25, "I25");
+    public static final BarcodeFormat DATABAR = new BarcodeFormat(Symbol.DATABAR, "DATABAR");
+    public static final BarcodeFormat DATABAR_EXP = new BarcodeFormat(Symbol.DATABAR_EXP, "DATABAR_EXP");
+    public static final BarcodeFormat CODABAR = new BarcodeFormat(Symbol.CODABAR, "CODABAR");
+    public static final BarcodeFormat CODE39 = new BarcodeFormat(Symbol.CODE39, "CODE39");
+    public static final BarcodeFormat PDF417 = new BarcodeFormat(Symbol.PDF417, "PDF417");
+    public static final BarcodeFormat QRCODE = new BarcodeFormat(Symbol.QRCODE, "QRCODE");
+    public static final BarcodeFormat CODE93 = new BarcodeFormat(Symbol.CODE93, "CODE93");
+    public static final BarcodeFormat CODE128 = new BarcodeFormat(Symbol.CODE128, "CODE128");
+
+    public static final List<BarcodeFormat> ALL_FORMATS = new ArrayList<BarcodeFormat>();
+
+    static {
+        ALL_FORMATS.add(BarcodeFormat.PARTIAL);
+        ALL_FORMATS.add(BarcodeFormat.EAN8);
+        ALL_FORMATS.add(BarcodeFormat.UPCE);
+        ALL_FORMATS.add(BarcodeFormat.ISBN10);
+        ALL_FORMATS.add(BarcodeFormat.UPCA);
+        ALL_FORMATS.add(BarcodeFormat.EAN13);
+        ALL_FORMATS.add(BarcodeFormat.ISBN13);
+        ALL_FORMATS.add(BarcodeFormat.I25);
+        ALL_FORMATS.add(BarcodeFormat.DATABAR);
+        ALL_FORMATS.add(BarcodeFormat.DATABAR_EXP);
+        ALL_FORMATS.add(BarcodeFormat.CODABAR);
+        ALL_FORMATS.add(BarcodeFormat.CODE39);
+        ALL_FORMATS.add(BarcodeFormat.PDF417);
+        ALL_FORMATS.add(BarcodeFormat.QRCODE);
+        ALL_FORMATS.add(BarcodeFormat.CODE93);
+        ALL_FORMATS.add(BarcodeFormat.CODE128);
+    }
+
+    public BarcodeFormat(int id, String name) {
+        mId = id;
+        mName = name;
+    }
+
+    public int getId() {
+        return mId;
+    }
+
+    public String getName() {
+        return mName;
+    }
+
+    public static BarcodeFormat getFormatById(int id) {
+        for(BarcodeFormat format : ALL_FORMATS) {
+            if(format.getId() == id) {
+                return format;
+            }
+        }
+        return BarcodeFormat.NONE;
+    }
+}

+ 22 - 0
zbar/src/main/java/me/dm7/barcodescanner/zbar/Result.java

@@ -0,0 +1,22 @@
+package me.dm7.barcodescanner.zbar;
+
+public class Result {
+    private String mContents;
+    private BarcodeFormat mBarcodeFormat;
+
+    public void setContents(String contents) {
+        mContents = contents;
+    }
+
+    public void setBarcodeFormat(BarcodeFormat format) {
+        mBarcodeFormat = format;
+    }
+
+    public BarcodeFormat getBarcodeFormat() {
+        return mBarcodeFormat;
+    }
+
+    public String getContents() {
+        return mContents;
+    }
+}

+ 156 - 0
zbar/src/main/java/me/dm7/barcodescanner/zbar/ZBarScannerView.java

@@ -0,0 +1,156 @@
+package me.dm7.barcodescanner.zbar;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.hardware.Camera;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+
+import net.sourceforge.zbar.Config;
+import net.sourceforge.zbar.Image;
+import net.sourceforge.zbar.ImageScanner;
+import net.sourceforge.zbar.Symbol;
+import net.sourceforge.zbar.SymbolSet;
+
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import java.util.List;
+
+import me.dm7.barcodescanner.core.BarcodeScannerView;
+import me.dm7.barcodescanner.core.DisplayUtils;
+
+public class ZBarScannerView extends BarcodeScannerView {
+    private static final String TAG = "ZBarScannerView";
+
+    public interface ResultHandler {
+        public void handleResult(Result rawResult);
+    }
+
+    static {
+        System.loadLibrary("iconv");
+    }
+
+    private ImageScanner mScanner;
+    private List<BarcodeFormat> mFormats;
+    private ResultHandler mResultHandler;
+
+    public ZBarScannerView(Context context) {
+        super(context);
+        setupScanner();
+    }
+
+    public ZBarScannerView(Context context, AttributeSet attributeSet) {
+        super(context, attributeSet);
+        setupScanner();
+    }
+
+    public void setFormats(List<BarcodeFormat> formats) {
+        mFormats = formats;
+        setupScanner();
+    }
+
+    public void setResultHandler(ResultHandler resultHandler) {
+        mResultHandler = resultHandler;
+    }
+
+    public Collection<BarcodeFormat> getFormats() {
+        if(mFormats == null) {
+            return BarcodeFormat.ALL_FORMATS;
+        }
+        return mFormats;
+    }
+
+    public void setupScanner() {
+        mScanner = new ImageScanner();
+        mScanner.setConfig(0, Config.X_DENSITY, 3);
+        mScanner.setConfig(0, Config.Y_DENSITY, 3);
+
+        mScanner.setConfig(Symbol.NONE, Config.ENABLE, 0);
+        for(BarcodeFormat format : getFormats()) {
+            mScanner.setConfig(format.getId(), Config.ENABLE, 1);
+        }
+    }
+
+    @Override
+    public void onPreviewFrame(byte[] data, Camera camera) {
+        if(mResultHandler == null) {
+            return;
+        }
+
+        try {
+            Camera.Parameters parameters = camera.getParameters();
+            Camera.Size size = parameters.getPreviewSize();
+            int width = size.width;
+            int height = size.height;
+
+            if(DisplayUtils.getScreenOrientation(getContext()) == Configuration.ORIENTATION_PORTRAIT) {
+                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;
+            }
+
+            Image barcode = new Image(width, height, "Y800");
+            barcode.setData(data);
+
+            int result = mScanner.scanImage(barcode);
+
+            if (result != 0) {
+                SymbolSet syms = mScanner.getResults();
+                final Result rawResult = new Result();
+                for (Symbol sym : syms) {
+                    // In order to retreive QR codes containing null bytes we need to
+                    // use getDataBytes() rather than getData() which uses C strings.
+                    // Weirdly ZBar transforms all data to UTF-8, even the data returned
+                    // by getDataBytes() so we have to decode it as UTF-8.
+                    String symData;
+                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
+                        symData = new String(sym.getDataBytes(), StandardCharsets.UTF_8);
+                    } else {
+                        symData = sym.getData();
+                    }
+                    if (!TextUtils.isEmpty(symData)) {
+                        rawResult.setContents(symData);
+                        rawResult.setBarcodeFormat(BarcodeFormat.getFormatById(sym.getType()));
+                        break;
+                    }
+                }
+
+                Handler handler = new Handler(Looper.getMainLooper());
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        // Stopping the preview can take a little long.
+                        // So we want to set result handler to null to discard subsequent calls to
+                        // onPreviewFrame.
+                        ResultHandler tmpResultHandler = mResultHandler;
+                        mResultHandler = null;
+                        
+                        stopCameraPreview();
+                        if (tmpResultHandler != null) {
+                            tmpResultHandler.handleResult(rawResult);
+                        }
+                    }
+                });
+            } else {
+                camera.setOneShotPreviewCallback(this);
+            }
+        } catch(RuntimeException e) {
+            // TODO: Terrible hack. It is possible that this method is invoked after camera is released.
+            Log.e(TAG, e.toString(), e);
+        }
+    }
+
+    public void resumeCameraPreview(ResultHandler resultHandler) {
+        mResultHandler = resultHandler;
+        super.resumeCameraPreview();
+    }
+}

BIN
zbar/src/main/jniLibs/arm64-v8a/libiconv.so


BIN
zbar/src/main/jniLibs/arm64-v8a/libzbarjni.so


BIN
zbar/src/main/jniLibs/armeabi-v7a/libiconv.so


BIN
zbar/src/main/jniLibs/armeabi-v7a/libzbarjni.so


BIN
zbar/src/main/jniLibs/armeabi/libiconv.so


BIN
zbar/src/main/jniLibs/armeabi/libzbarjni.so


BIN
zbar/src/main/jniLibs/mips/libiconv.so


BIN
zbar/src/main/jniLibs/mips/libzbarjni.so


BIN
zbar/src/main/jniLibs/mips64/libiconv.so


BIN
zbar/src/main/jniLibs/mips64/libzbarjni.so


BIN
zbar/src/main/jniLibs/x86/libiconv.so


+ 0 - 0
zbar/src/main/jniLibs/x86/libzbarjni.so


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor