From 123b8f666f18378bcf75a65caa10e2fa6fb8f5f5 Mon Sep 17 00:00:00 2001 From: Akashdeep Singh Kaur Date: Mon, 2 Sep 2024 14:00:39 +0100 Subject: [PATCH] feat(ENG-669): Upgrade Barcode-Scanning plugin --- packages/barcode-scanning/README.md | 32 +++++++++-- packages/barcode-scanning/android/.project | 34 +++++++++++ .../mlkit/barcodescanning/BarcodeScanner.java | 37 +++++++++++- .../barcodescanning/BarcodeScannerPlugin.java | 56 ++++++++++++++++++- .../ios/Plugin/BarcodeScanner.swift | 27 +++++++++ .../ios/Plugin/BarcodeScannerPlugin.m | 2 + .../ios/Plugin/BarcodeScannerPlugin.swift | 26 +++++++++ packages/barcode-scanning/src/definitions.ts | 8 +++ packages/barcode-scanning/src/web.ts | 7 +++ 9 files changed, 221 insertions(+), 8 deletions(-) create mode 100644 packages/barcode-scanning/android/.project diff --git a/packages/barcode-scanning/README.md b/packages/barcode-scanning/README.md index fd94220..73752a4 100644 --- a/packages/barcode-scanning/README.md +++ b/packages/barcode-scanning/README.md @@ -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) @@ -364,10 +365,10 @@ Only available on Android and iOS. -------------------- -### scan(...) +### readBarcodeBase64(...) ```typescript -scan(options?: ScanOptions | undefined) => Promise +readBarcodeBase64(options: ReadBarcodeFromBase64) => Promise ``` Scan a barcode with a ready-to-use interface without WebView customization. @@ -380,14 +381,29 @@ by using `isGoogleBarcodeScannerModuleAvailable()`. Only available on Android and iOS. +| Param | Type | +| ------------- | ----------------------------------------------------------------------- | +| **`options`** | ReadBarcodeFromBase64 | + +**Returns:** Promise<ReadBarcodesFromImageResult> + +**Since:** 0.0.1 + +-------------------- + + +### scan(...) + +```typescript +scan(options?: ScanOptions | undefined) => Promise +``` + | Param | Type | | ------------- | --------------------------------------------------- | | **`options`** | ScanOptions | **Returns:** Promise<ScanResult> -**Since:** 0.0.1 - -------------------- @@ -761,6 +777,14 @@ Remove all listeners for this plugin. | **`path`** | string | The local path to the image file. | 0.0.1 | +#### ReadBarcodeFromBase64 + +| Prop | Type | +| ------------- | ---------------------------- | +| **`formats`** | BarcodeFormat[] | +| **`base64`** | string | + + #### ScanResult | Prop | Type | Description | Since | diff --git a/packages/barcode-scanning/android/.project b/packages/barcode-scanning/android/.project new file mode 100644 index 0000000..51f2aeb --- /dev/null +++ b/packages/barcode-scanning/android/.project @@ -0,0 +1,34 @@ + + + capacitor-mlkit-barcode-scanning + Project capacitor-mlkit-barcode-scanning created by Buildship. + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.buildship.core.gradleprojectnature + + + + 1705440963785 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScanner.java b/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScanner.java index 24786f5..94ca23c 100644 --- a/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScanner.java +++ b/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScanner.java @@ -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; @@ -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; @@ -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); @@ -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); diff --git a/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScannerPlugin.java b/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScannerPlugin.java index 0c8163e..bfd218d 100644 --- a/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScannerPlugin.java +++ b/packages/barcode-scanning/android/src/main/java/io/capawesome/capacitorjs/plugins/mlkit/barcodescanning/BarcodeScannerPlugin.java @@ -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; @@ -34,7 +36,6 @@ public class BarcodeScannerPlugin extends Plugin { public static final String TAG = "BarcodeScanner"; - // Permission alias constants public static final String CAMERA = "camera"; @@ -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."; @@ -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 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 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); } } diff --git a/packages/barcode-scanning/ios/Plugin/BarcodeScanner.swift b/packages/barcode-scanning/ios/Plugin/BarcodeScanner.swift index 9de324c..701aab9 100644 --- a/packages/barcode-scanning/ios/Plugin/BarcodeScanner.swift +++ b/packages/barcode-scanning/ios/Plugin/BarcodeScanner.swift @@ -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() diff --git a/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.m b/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.m index 5c5a1e1..4eb5088 100644 --- a/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.m +++ b/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.m @@ -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); @@ -23,4 +24,5 @@ CAP_PLUGIN_METHOD(installGoogleBarcodeScannerModule, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise); CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise); + CAP_PLUGIN_METHOD(removeAllListeners, CAPPluginReturnPromise); ) diff --git a/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.swift b/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.swift index 526034d..f3aa3d6 100644 --- a/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.swift +++ b/packages/barcode-scanning/ios/Plugin/BarcodeScannerPlugin.swift @@ -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 ?? []) diff --git a/packages/barcode-scanning/src/definitions.ts b/packages/barcode-scanning/src/definitions.ts index eebc43d..3c174fd 100644 --- a/packages/barcode-scanning/src/definitions.ts +++ b/packages/barcode-scanning/src/definitions.ts @@ -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; + scan(options?: ScanOptions): Promise; /** * Returns whether or not the barcode scanner is supported. @@ -249,6 +252,11 @@ export interface ReadBarcodesFromImageOptions { path: string; } +export interface ReadBarcodeFromBase64 { + formats?: BarcodeFormat[]; + + base64: string; +}; /** * @since 0.0.1 */ diff --git a/packages/barcode-scanning/src/web.ts b/packages/barcode-scanning/src/web.ts index 2bee463..2e0f660 100644 --- a/packages/barcode-scanning/src/web.ts +++ b/packages/barcode-scanning/src/web.ts @@ -10,6 +10,7 @@ import type { IsTorchAvailableResult, IsTorchEnabledResult, PermissionStatus, + ReadBarcodeFromBase64, ReadBarcodesFromImageOptions, ReadBarcodesFromImageResult, ScanResult, @@ -35,6 +36,12 @@ export class BarcodeScannerWeb throw this.createUnavailableException(); } + async readBarcodeBase64( + _options: ReadBarcodeFromBase64, + ): Promise { + throw this.createUnavailableException(); + } + async scan(): Promise { throw this.createUnavailableException(); }