diff --git a/android/ZBar.java b/android/ZBar.java new file mode 100644 index 0000000..519fd60 --- /dev/null +++ b/android/ZBar.java @@ -0,0 +1,76 @@ +package org.cloudsky.cordovaPlugins; + +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CallbackContext; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; + +import android.app.Activity; +import android.content.Intent; +import android.content.Context; + +import org.cloudsky.cordovaPlugins.ZBarScannerActivity; + +public class ZBar extends CordovaPlugin { + + // Configuration --------------------------------------------------- + + private static int SCAN_CODE = 1; + + + // State ----------------------------------------------------------- + + private boolean isInProgress = false; + private CallbackContext scanCallbackContext; + + + // Plugin API ------------------------------------------------------ + + @Override + public boolean execute (String action, JSONArray args, CallbackContext callbackContext) + throws JSONException + { + if(action.equals("scan")) { + if(isInProgress) { + callbackContext.error("A scan is already in progress!"); + } else { + isInProgress = true; + scanCallbackContext = callbackContext; + Context appCtx = cordova.getActivity().getApplicationContext(); + Intent scanIntent = new Intent(appCtx, ZBarScannerActivity.class); + cordova.startActivityForResult(this, scanIntent, SCAN_CODE); + } + return true; + } else { + return false; + } + } + + + // External results handler ---------------------------------------- + + @Override + public void onActivityResult (int requestCode, int resultCode, Intent result) + { + if(requestCode == SCAN_CODE) { + switch(resultCode) { + case Activity.RESULT_OK: + String barcodeValue = result.getStringExtra(ZBarScannerActivity.EXTRA_QRVALUE); + scanCallbackContext.success(barcodeValue); + break; + case Activity.RESULT_CANCELED: + scanCallbackContext.error("cancelled"); + break; + case ZBarScannerActivity.RESULT_ERROR: + scanCallbackContext.error("Scan failed due to an error"); + break; + default: + scanCallbackContext.error("Unknown error"); + } + isInProgress = false; + scanCallbackContext = null; + } + } +} diff --git a/android/ZBarScannerActivity.java b/android/ZBarScannerActivity.java new file mode 100644 index 0000000..a972d85 --- /dev/null +++ b/android/ZBarScannerActivity.java @@ -0,0 +1,295 @@ +package org.cloudsky.cordovaPlugins; + +import java.io.IOException; + +import android.app.Activity; +import android.content.Intent; +import android.content.res.Resources; +import android.hardware.Camera; +import android.hardware.Camera.PreviewCallback; +import android.hardware.Camera.AutoFocusCallback; +import android.os.Bundle; +import android.os.Handler; +import android.view.Gravity; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.Toast; + +import net.sourceforge.zbar.ImageScanner; +import net.sourceforge.zbar.Image; +import net.sourceforge.zbar.Symbol; +import net.sourceforge.zbar.SymbolSet; +import net.sourceforge.zbar.Config; + +public class ZBarScannerActivity extends Activity +implements SurfaceHolder.Callback { + + // Config ---------------------------------------------------------- + + private static int autoFocusInterval = 500; // Interval between AFcallback and next AF attempt. + + // Public Constants ------------------------------------------------ + + public static final String EXTRA_QRVALUE = "qrValue"; + public static final int RESULT_ERROR = RESULT_FIRST_USER + 1; + + // State ----------------------------------------------------------- + + private Camera camera; + private Handler autoFocusHandler; + private SurfaceView scannerSurface; + private SurfaceHolder holder; + private ImageScanner scanner; + private int surfW, surfH; + + // For retrieving R.* resources, from the actual app package + // (we can't use actual.application.package.R.* in our code as we + // don't know the applciation package name when writing this plugin). + private String package_name; + private Resources resources; + + // Static initialisers (class) ------------------------------------- + + static { + // Needed by ZBar?? + System.loadLibrary("iconv"); + } + + // Activity Lifecycle ---------------------------------------------- + + @Override + public void onCreate (Bundle savedInstanceState) + { + super.onCreate(savedInstanceState); + + // Initiate instance variables + autoFocusHandler = new Handler(); + scanner = new ImageScanner(); + scanner.setConfig(0, Config.X_DENSITY, 3); + scanner.setConfig(0, Config.Y_DENSITY, 3); + + // Set content view + setContentView(getResourceId("layout/cszbarscanner")); + + // Create preview SurfaceView + scannerSurface = new SurfaceView (this) { + @Override + public void onSizeChanged (int w, int h, int oldW, int oldH) { + surfW = w; + surfH = h; + matchSurfaceToPreviewRatio(); + } + }; + scannerSurface.setLayoutParams(new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + Gravity.CENTER + )); + scannerSurface.getHolder().addCallback(this); + + // Add preview SurfaceView to the screen + ((FrameLayout) findViewById(getResourceId("id/csZbarScannerView"))).addView(scannerSurface); + findViewById(getResourceId("id/csZbarScannerTitle")).bringToFront(); + findViewById(getResourceId("id/csZbarScannerInstructions")).bringToFront(); + } + + @Override + public void onResume () + { + super.onResume(); + + try { + camera = Camera.open(); + } catch (Exception e){ + die("Error: Could not open the camera."); + return; + } + + tryStartPreview(); + } + + @Override + public void onPause () + { + releaseCamera(); + super.onPause(); + } + + @Override + public void onDestroy () + { + scanner.destroy(); + super.onDestroy(); + } + + // Event handlers -------------------------------------------------- + + @Override + public void onBackPressed () + { + setResult(RESULT_CANCELED); + super.onBackPressed(); + } + + // SurfaceHolder.Callback implementation --------------------------- + + @Override + public void surfaceCreated (SurfaceHolder hld) + { + tryStopPreview(); + holder = hld; + tryStartPreview(); + } + + @Override + public void surfaceDestroyed (SurfaceHolder holder) + { + // No surface == no preview == no point being in this Activity. + die("The camera surface was destroyed"); + } + + @Override + public void surfaceChanged (SurfaceHolder hld, int fmt, int w, int h) + { + // Sanity check - holder must have a surface... + if(hld.getSurface() == null) die("There is no camera surface"); + + surfW = w; + surfH = h; + matchSurfaceToPreviewRatio(); + + tryStopPreview(); + holder = hld; + tryStartPreview(); + } + + // Continuously auto-focus ----------------------------------------- + + private AutoFocusCallback autoFocusCb = new AutoFocusCallback() + { + public void onAutoFocus(boolean success, Camera camera) { + autoFocusHandler.postDelayed(doAutoFocus, autoFocusInterval); + } + }; + + private Runnable doAutoFocus = new Runnable() + { + public void run() { + if(camera != null) camera.autoFocus(autoFocusCb); + } + }; + + // Camera callbacks ------------------------------------------------ + + // Receives frames from the camera and checks for barcodes. + private PreviewCallback previewCb = new PreviewCallback() + { + public void onPreviewFrame(byte[] data, Camera camera) { + Camera.Parameters parameters = camera.getParameters(); + Camera.Size size = parameters.getPreviewSize(); + + Image barcode = new Image(size.width, size.height, "Y800"); + barcode.setData(data); + + if (scanner.scanImage(barcode) != 0) { + String qrValue = ""; + + SymbolSet syms = scanner.getResults(); + for (Symbol sym : syms) { + qrValue = sym.getData(); + + // Return 1st found QR code value to the calling Activity. + Intent result = new Intent (); + result.putExtra(EXTRA_QRVALUE, qrValue); + setResult(Activity.RESULT_OK, result); + finish(); + } + + } + } + }; + + // Misc ------------------------------------------------------------ + + // finish() due to error + private void die (String msg) + { + setResult(RESULT_ERROR); + finish(); + } + + private int getResourceId (String typeAndName) + { + if(package_name == null) package_name = getApplication().getPackageName(); + if(resources == null) resources = getApplication().getResources(); + return resources.getIdentifier(typeAndName, null, package_name); + } + + // Release the camera resources and state. + private void releaseCamera () + { + if (camera != null) { + autoFocusHandler.removeCallbacks(doAutoFocus); + camera.setPreviewCallback(null); + camera.release(); + camera = null; + } + } + + // Match the aspect ratio of the preview SurfaceView with the camera's preview aspect ratio, + // so that the displayed preview is not stretched/squashed. + private void matchSurfaceToPreviewRatio () { + if(camera == null) return; + if(surfW == 0 || surfH == 0) return; + + // Resize SurfaceView to match camera preview ratio (avoid stretching). + Camera.Parameters params = camera.getParameters(); + Camera.Size size = params.getPreviewSize(); + float previewRatio = (float) size.height / size.width; // swap h and w as the preview is rotated 90 degrees + float surfaceRatio = (float) surfW / surfH; + + if(previewRatio > surfaceRatio) { + scannerSurface.setLayoutParams(new FrameLayout.LayoutParams( + surfW, + Math.round((float) surfW / previewRatio), + Gravity.CENTER + )); + } else if(previewRatio < surfaceRatio) { + scannerSurface.setLayoutParams(new FrameLayout.LayoutParams( + Math.round((float) surfH * previewRatio), + surfH, + Gravity.CENTER + )); + } + } + + // Stop the camera preview safely. + private void tryStopPreview () { + // Stop camera preview before making changes. + try { + camera.stopPreview(); + } catch (Exception e){ + // Preview was not running. Ignore the error. + } + } + + // Start the camera preview if possible. + // If start is attempted but fails, exit with error message. + private void tryStartPreview () { + if(holder != null) { + try { + // 90 degrees rotation for Portrait orientation Activity. + camera.setDisplayOrientation(90); + + camera.setPreviewDisplay(holder); + camera.setPreviewCallback(previewCb); + camera.startPreview(); + camera.autoFocus(autoFocusCb); + } catch (IOException e) { + die("Could not start camera preview: " + e.getMessage()); + } + } + } +} diff --git a/android/libs/armeabi-v7a/libiconv.so b/android/libs/armeabi-v7a/libiconv.so new file mode 100644 index 0000000..2bcbb70 Binary files /dev/null and b/android/libs/armeabi-v7a/libiconv.so differ diff --git a/android/libs/armeabi-v7a/libzbarjni.so b/android/libs/armeabi-v7a/libzbarjni.so new file mode 100644 index 0000000..2693dbb Binary files /dev/null and b/android/libs/armeabi-v7a/libzbarjni.so differ diff --git a/android/libs/armeabi/libiconv.so b/android/libs/armeabi/libiconv.so new file mode 100644 index 0000000..9c7150d Binary files /dev/null and b/android/libs/armeabi/libiconv.so differ diff --git a/android/libs/armeabi/libzbarjni.so b/android/libs/armeabi/libzbarjni.so new file mode 100644 index 0000000..3d4a8ac Binary files /dev/null and b/android/libs/armeabi/libzbarjni.so differ diff --git a/android/libs/x86/libiconv.so b/android/libs/x86/libiconv.so new file mode 100644 index 0000000..6ab43e5 Binary files /dev/null and b/android/libs/x86/libiconv.so differ diff --git a/android/libs/x86/libzbarjni.so b/android/libs/x86/libzbarjni.so new file mode 100644 index 0000000..d64f517 Binary files /dev/null and b/android/libs/x86/libzbarjni.so differ diff --git a/android/libs/zbar.jar b/android/libs/zbar.jar new file mode 100644 index 0000000..7d50b95 Binary files /dev/null and b/android/libs/zbar.jar differ diff --git a/android/res/layout/cszbarscanner.xml b/android/res/layout/cszbarscanner.xml new file mode 100644 index 0000000..cd98afe --- /dev/null +++ b/android/res/layout/cszbarscanner.xml @@ -0,0 +1,38 @@ + + + + + + + + diff --git a/plugin.xml b/plugin.xml new file mode 100644 index 0000000..1e33ca8 --- /dev/null +++ b/plugin.xml @@ -0,0 +1,58 @@ + + + + + + + + ZBar barcode scanner + TJ Woon (tj@cloudsky.org) + Plugin to integrate with the ZBar barcode scanning library. + Apache 2.0 + cszbar,zbar,barcode,qr,qr code,scanner + + + + + + + + + + + + + + + + + + + + + + + + Scan QR Code + Please point your camera at the QR code. + #ffffff + #88000000 + #000000 + + + + + + + + + + + + + + diff --git a/www/zBar.js b/www/zBar.js new file mode 100644 index 0000000..c46e40f --- /dev/null +++ b/www/zBar.js @@ -0,0 +1,16 @@ +var argscheck = require('cordova/argscheck'), + exec = require('cordova/exec'); + +function ZBar () {}; + +ZBar.prototype = { + + scan: function (params, success, failure) + { + argscheck.checkArgs('*fF', 'CsZBar.scan', arguments); + exec(success, failure, 'CsZBar', 'scan', [params]); + }, + +}; + +module.exports = new ZBar;