Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ENG-669): Upgrade Barcode-Scanning plugin #181

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions packages/barcode-scanning/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ If you can't see the camera view, make sure all elements in the DOM are not visi
* [`startScan(...)`](#startscan)
* [`stopScan()`](#stopscan)
* [`readBarcodesFromImage(...)`](#readbarcodesfromimage)
* [`readBarcodeBase64(...)`](#readbarcodebase64)
* [`scan(...)`](#scan)
* [`isSupported()`](#issupported)
* [`enableTorch()`](#enabletorch)
Expand Down Expand Up @@ -364,10 +365,10 @@ Only available on Android and iOS.
--------------------


### scan(...)
### readBarcodeBase64(...)

```typescript
scan(options?: ScanOptions | undefined) => Promise<ScanResult>
readBarcodeBase64(options: ReadBarcodeFromBase64) => Promise<ReadBarcodesFromImageResult>
```

Scan a barcode with a ready-to-use interface without WebView customization.
Expand All @@ -380,14 +381,29 @@ by using `isGoogleBarcodeScannerModuleAvailable()`.

Only available on Android and iOS.

| Param | Type |
| ------------- | ----------------------------------------------------------------------- |
| **`options`** | <code><a href="#readbarcodefrombase64">ReadBarcodeFromBase64</a></code> |

**Returns:** <code>Promise&lt;<a href="#readbarcodesfromimageresult">ReadBarcodesFromImageResult</a>&gt;</code>

**Since:** 0.0.1

--------------------


### scan(...)

```typescript
scan(options?: ScanOptions | undefined) => Promise<ScanResult>
```

| Param | Type |
| ------------- | --------------------------------------------------- |
| **`options`** | <code><a href="#scanoptions">ScanOptions</a></code> |

**Returns:** <code>Promise&lt;<a href="#scanresult">ScanResult</a>&gt;</code>

**Since:** 0.0.1

--------------------


Expand Down Expand Up @@ -761,6 +777,14 @@ Remove all listeners for this plugin.
| **`path`** | <code>string</code> | The local path to the image file. | 0.0.1 |


#### ReadBarcodeFromBase64

| Prop | Type |
| ------------- | ---------------------------- |
| **`formats`** | <code>BarcodeFormat[]</code> |
| **`base64`** | <code>string</code> |


#### ScanResult

| Prop | Type | Description | Since |
Expand Down
34 changes: 34 additions & 0 deletions packages/barcode-scanning/android/.project
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>capacitor-mlkit-barcode-scanning</name>
<comment>Project capacitor-mlkit-barcode-scanning created by Buildship.</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
<filteredResources>
<filter>
<id>1705440963785</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.Rect;
import android.media.Image;
import android.net.Uri;
import android.provider.Settings;
import android.util.Base64;
import android.util.Size;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
Expand All @@ -22,9 +29,11 @@
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.CameraController;
import androidx.camera.view.PreviewView;
import androidx.core.content.ContextCompat;
import androidx.lifecycle.LifecycleOwner;
import com.getcapacitor.Logger;
import com.getcapacitor.PermissionState;
import com.getcapacitor.PluginCall;
import com.google.android.gms.common.moduleinstall.InstallStatusListener;
Expand Down Expand Up @@ -111,7 +120,6 @@ public void startScan(ScanSettings scanSettings, StartScanResultCallback callbac
// Start the camera
camera =
processCameraProvider.bindToLifecycle((LifecycleOwner) plugin.getContext(), cameraSelector, preview, imageAnalysis);

callback.success();
} catch (Exception exception) {
callback.error(exception);
Expand Down Expand Up @@ -162,6 +170,33 @@ public void readBarcodesFromImage(String path, ScanSettings scanSettings, ReadBa
);
}

public void readBarcodeBase64(String base64, ScanSettings scanSettings, ReadBarcodesFromImageResultCallback callback)
throws Exception {
InputImage inputImage;
try {
byte[] decodedBytes = Base64.decode(base64, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(decodedBytes, 0, decodedBytes.length);
inputImage = InputImage.fromBitmap(bitmap, 0);
} catch (Exception exception) {
throw new Exception(BarcodeScannerPlugin.ERROR_LOAD_IMAGE_FAILED);
}

BarcodeScannerOptions options = buildBarcodeScannerOptions(scanSettings);
com.google.mlkit.vision.barcode.BarcodeScanner barcodeScannerInstance = BarcodeScanning.getClient(options);
barcodeScannerInstance
.process(inputImage)
.addOnSuccessListener(
barcodes -> {
callback.success(barcodes);
}
)
.addOnFailureListener(
exception -> {
callback.error(exception);
}
);
}

public void scan(ScanSettings scanSettings, ScanResultCallback callback) {
GmsBarcodeScannerOptions options = buildGmsBarcodeScannerOptions(scanSettings);
GmsBarcodeScanner scanner = GmsBarcodeScanning.getClient(plugin.getContext(), options);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import com.getcapacitor.annotation.CapacitorPlugin;
import com.getcapacitor.annotation.Permission;
import com.getcapacitor.annotation.PermissionCallback;
import com.google.android.gms.common.moduleinstall.ModuleInstallStatusUpdate;
import com.google.mlkit.vision.barcode.common.Barcode;
import com.google.mlkit.vision.common.InputImage;
import io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.classes.options.SetZoomRatioOptions;
import io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.classes.results.GetMaxZoomRatioResult;
import io.capawesome.capacitorjs.plugins.mlkit.barcodescanning.classes.results.GetMinZoomRatioResult;
Expand All @@ -34,7 +36,6 @@
public class BarcodeScannerPlugin extends Plugin {

public static final String TAG = "BarcodeScanner";

// Permission alias constants
public static final String CAMERA = "camera";

Expand All @@ -43,6 +44,7 @@ public class BarcodeScannerPlugin extends Plugin {
public static final String GOOGLE_BARCODE_SCANNER_MODULE_INSTALL_PROGRESS_EVENT = "googleBarcodeScannerModuleInstallProgress";
public static final String ERROR_SCAN_CANCELED = "scan canceled.";
public static final String ERROR_PATH_MISSING = "path must be provided.";
public static final String ERROR_BASE64_STRING_MISSING = "base64 image string missing";
public static final String ERROR_LOAD_IMAGE_FAILED = "The image could not be loaded.";
public static final String ERROR_ZOOM_RATIO_MISSING = "zoomRatio must be provided.";
public static final String ERROR_NO_ACTIVE_SCAN_SESSION = "There is no active scan session.";
Expand Down Expand Up @@ -162,8 +164,56 @@ public void error(Exception exception) {
}
);
} catch (Exception exception) {
Logger.error(TAG, exception.getMessage(), exception);
call.reject(exception.getMessage());
String message = exception.getMessage();
Logger.error(TAG, message, exception);
call.reject(message);
}
}



@PluginMethod
public void readBarcodeBase64(PluginCall call) {
try {
String base64 = call.getString("base64");
if (base64 == null) {
call.reject(ERROR_BASE64_STRING_MISSING);
return;
}

List<String> formatsOption = call.getArray("formats", new JSArray()).toList();
int[] formats = BarcodeScannerHelper.convertStringsToBarcodeScannerFormats(formatsOption.toArray(new String[0]));

ScanSettings scanSettings = new ScanSettings();
scanSettings.formats = formats;

implementation.readBarcodeBase64(
base64,
scanSettings,
new ReadBarcodesFromImageResultCallback() {
@Override
public void success(List<Barcode> barcodes) {
JSArray barcodeResults = new JSArray();
for (Barcode barcode : barcodes) {
barcodeResults.put(BarcodeScannerHelper.createBarcodeResultForBarcode(barcode, null, null));
}

JSObject result = new JSObject();
result.put("barcodes", barcodeResults);
call.resolve(result);
}

@Override
public void error(Exception exception) {
Logger.error(TAG, "readBarcodeBase64 failed.", exception);
call.reject(exception.getMessage());
}
}
);
} catch (Exception exception) {
String message = exception.getMessage();
Logger.error(TAG, message, exception);
call.reject(message);
}
}

Expand Down
27 changes: 27 additions & 0 deletions packages/barcode-scanning/ios/Plugin/BarcodeScanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,37 @@ typealias MLKitBarcodeScanner = MLKitBarcodeScanning.BarcodeScanner
completion(nil, error.localizedDescription)
return
}
CAPLog.print("\(features?.count ?? 0)")
guard let features = features, !features.isEmpty else {
return
}
completion(features, nil)
}
}

@objc public func readBarcodeFromBase64(base64: String, settings: ScanSettings, completion: @escaping ([Barcode]?, String?) -> Void) {
if let data = Data(base64Encoded: base64), let image = UIImage(data: data) {
// Image is successfully created from Base64 data
let visionImage = VisionImage(image: image)
let barcodeScannerInstance = MLKitBarcodeScanner.barcodeScanner(options: BarcodeScannerOptions(formats: BarcodeFormat(settings.formats)))
barcodeScannerInstance.process(visionImage) { features, error in
if let error = error {
CAPLog.print(error.localizedDescription, error)
completion(nil, error.localizedDescription)
return
}
CAPLog.print("\(features?.count ?? 0)")
guard let features = features, !features.isEmpty else {
completion(nil, error?.localizedDescription);
return;
}
completion(features, nil)
}
} else {
completion(nil, plugin.errorInvalidImage)
}
}

public func scan(settings: ScanSettings, completion: @escaping (([Barcode]?, AVCaptureVideoOrientation?, String?) -> Void)) {
self.stopScan()

Expand Down
2 changes: 2 additions & 0 deletions packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
CAP_PLUGIN_METHOD(startScan, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(stopScan, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(readBarcodesFromImage, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(readBarcodeBase64, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(scan, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(isSupported, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(enableTorch, CAPPluginReturnPromise);
Expand All @@ -23,4 +24,5 @@
CAP_PLUGIN_METHOD(installGoogleBarcodeScannerModule, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise);
CAP_PLUGIN_METHOD(removeAllListeners, CAPPluginReturnPromise);
)
26 changes: 26 additions & 0 deletions packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,32 @@ public class BarcodeScannerPlugin: CAPPlugin {
})
}

@objc func readBarcodeBase64(_ call: CAPPluginCall) {
let formatsOption = call.getArray("formats") as? [String]
let formats = BarcodeScannerHelper.convertStringsToBarcodeScannerFormats(formatsOption ?? [])
guard let base64 = call.getString("base64") else {
call.reject(errorPathMissing)
return
}

let settings = ScanSettings()
settings.formats = formats

implementation?.readBarcodeFromBase64(base64: base64, settings: settings, completion: { barcodes, errorMessage in
if let errorMessage = errorMessage {
call.reject(errorMessage)
return
}
var barcodeResults = JSArray()
for barcode in barcodes ?? [] {
barcodeResults.append(BarcodeScannerHelper.createBarcodeResultForBarcode(barcode, imageSize: nil, videoOrientation: nil))
}
call.resolve([
"barcodes": barcodeResults
])
})
}

@objc func scan(_ call: CAPPluginCall) {
let formatsOption = call.getArray("formats") as? [String]
let formats = BarcodeScannerHelper.convertStringsToBarcodeScannerFormats(formatsOption ?? [])
Expand Down
8 changes: 8 additions & 0 deletions packages/barcode-scanning/src/definitions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ export interface BarcodeScannerPlugin {
* @since 0.0.1
* @experimental This method is experimental and may change in the future.
*/

readBarcodeBase64(options: ReadBarcodeFromBase64): Promise<ReadBarcodesFromImageResult>;

scan(options?: ScanOptions): Promise<ScanResult>;
/**
* Returns whether or not the barcode scanner is supported.
Expand Down Expand Up @@ -249,6 +252,11 @@ export interface ReadBarcodesFromImageOptions {
path: string;
}

export interface ReadBarcodeFromBase64 {
formats?: BarcodeFormat[];

base64: string;
};
/**
* @since 0.0.1
*/
Expand Down
7 changes: 7 additions & 0 deletions packages/barcode-scanning/src/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
IsTorchAvailableResult,
IsTorchEnabledResult,
PermissionStatus,
ReadBarcodeFromBase64,
ReadBarcodesFromImageOptions,
ReadBarcodesFromImageResult,
ScanResult,
Expand All @@ -35,6 +36,12 @@ export class BarcodeScannerWeb
throw this.createUnavailableException();
}

async readBarcodeBase64(
_options: ReadBarcodeFromBase64,
): Promise<ReadBarcodesFromImageResult> {
throw this.createUnavailableException();
}

async scan(): Promise<ScanResult> {
throw this.createUnavailableException();
}
Expand Down