@biso_gmbh/capacitor-plugin-ml-kit-barcode-scanner 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/CapacitorPluginMlKitBarcodeScanner.podspec +20 -0
  2. package/README.md +231 -0
  3. package/android/build.gradle +65 -0
  4. package/android/src/main/AndroidManifest.xml +8 -0
  5. package/android/src/main/assets/beep.mp3 +0 -0
  6. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/BarcodeAnalyzer.java +119 -0
  7. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/BarcodeFormat.java +40 -0
  8. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/BarcodeFormats.java +38 -0
  9. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/BarcodeType.java +37 -0
  10. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/BarcodesListener.java +8 -0
  11. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/CameraOverlay.java +161 -0
  12. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/CaptureActivity.java +164 -0
  13. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/DetectedBarcode.java +174 -0
  14. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/MlKitBarcodeScannerPlugin.java +142 -0
  15. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/ScannerSettings.java +348 -0
  16. package/android/src/main/java/com/biso/capacitor/plugins/mlkit/barcode/scanner/Utils.java +118 -0
  17. package/android/src/main/res/.gitkeep +0 -0
  18. package/android/src/main/res/drawable/torch_active.xml +12 -0
  19. package/android/src/main/res/drawable/torch_inactive.xml +12 -0
  20. package/android/src/main/res/drawable-mdpi/flashlight.png +0 -0
  21. package/android/src/main/res/layout/capture_activity.xml +28 -0
  22. package/dist/docs.json +279 -0
  23. package/dist/esm/definitions.d.ts +72 -0
  24. package/dist/esm/definitions.js +2 -0
  25. package/dist/esm/definitions.js.map +1 -0
  26. package/dist/esm/index.d.ts +4 -0
  27. package/dist/esm/index.js +7 -0
  28. package/dist/esm/index.js.map +1 -0
  29. package/dist/esm/web.d.ts +5 -0
  30. package/dist/esm/web.js +8 -0
  31. package/dist/esm/web.js.map +1 -0
  32. package/dist/plugin.cjs.js +22 -0
  33. package/dist/plugin.cjs.js.map +1 -0
  34. package/dist/plugin.js +25 -0
  35. package/dist/plugin.js.map +1 -0
  36. package/ios/Plugin/Assets.xcassets/Contents.json +6 -0
  37. package/ios/Plugin/Assets.xcassets/beep.dataset/Contents.json +12 -0
  38. package/ios/Plugin/Assets.xcassets/beep.dataset/beep.mp3 +0 -0
  39. package/ios/Plugin/Assets.xcassets/flashlight.imageset/Contents.json +12 -0
  40. package/ios/Plugin/Assets.xcassets/flashlight.imageset/flashlight.png +0 -0
  41. package/ios/Plugin/BarcodeFormat.swift +45 -0
  42. package/ios/Plugin/BarcodeFormats.swift +26 -0
  43. package/ios/Plugin/BarcodeType.swift +45 -0
  44. package/ios/Plugin/BarcodesAnalyzer.swift +79 -0
  45. package/ios/Plugin/CameraOverlay.swift +119 -0
  46. package/ios/Plugin/CameraViewController.swift +301 -0
  47. package/ios/Plugin/DetectedBarcode.swift +70 -0
  48. package/ios/Plugin/Info.plist +24 -0
  49. package/ios/Plugin/MlKitBarcodeScannerPlugin.h +10 -0
  50. package/ios/Plugin/MlKitBarcodeScannerPlugin.m +8 -0
  51. package/ios/Plugin/MlKitBarcodeScannerPlugin.swift +69 -0
  52. package/ios/Plugin/PrivacyInfo.xcprivacy +27 -0
  53. package/ios/Plugin/ScannerSettings.swift +171 -0
  54. package/ios/Plugin/Utils.swift +145 -0
  55. package/package.json +84 -0
@@ -0,0 +1,20 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'CapacitorPluginMlKitBarcodeScanner'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.license = package['license']
10
+ s.homepage = package['repository']['url']
11
+ s.author = package['author']
12
+ s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
13
+ s.static_framework = true
14
+ s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}'
15
+ s.ios.deployment_target = '15.0'
16
+ s.dependency 'Capacitor'
17
+ s.dependency 'GoogleMLKit/BarcodeScanning', '~> 5.0.0'
18
+ s.swift_version = '5.1'
19
+ s.resources = [ 'ios/Plugin/**/*.xcassets']
20
+ end
package/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # capacitor-plugin-mlkit-barcode-scanner
2
+ A really configurable implementation of googles MLKit Vision barcode scanning API for Android/IOS for capacitor.
3
+
4
+ This is a capacitor port of [bi-so-gmbh / cordova-plugin-mlkit-barcode-scanner](https://github.com/bi-so-gmbh/cordova-plugin-mlkit-barcode-scanner)
5
+
6
+ ## Supported Versions
7
+
8
+ ### iOS
9
+ A version of 1.15.0 or higher is needed. This is because that is the oldest version I can build the app on an emulator to test if the build runs through.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install https://github.com/bi-so-gmbh/capacitor-plugin-mlkit-barcode-scanner.git
15
+ npx cap sync
16
+ ```
17
+
18
+ ### Permissions
19
+
20
+ #### IOS
21
+ On IOS the camera permission needs to be added to the Info.plist of the project using the plugin.
22
+ ```
23
+ <key>NSCameraUsageDescription</key>
24
+ <string>Scan Barcodes</string>
25
+ ```
26
+
27
+ #### Android
28
+ On Android the permission has to be added in the AndroidManifest.xml of the project using the plugin.
29
+ ```
30
+ <uses-feature android:name="android.hardware.camera" android:required="false"/>
31
+ <uses-permission android:name="android.permission.CAMERA"/>
32
+ ```
33
+
34
+ ## Barcode Support
35
+
36
+ | 1d formats | Android | iOS |
37
+ |--------------|---------|-----|
38
+ | Codabar | ✓ | ✓ |
39
+ | Code 39 | ✓ | ✓ |
40
+ | Code 93 | ✓ | ✓ |
41
+ | Code 128 | ✓ | ✓ |
42
+ | EAN-8. | ✓ | ✓ |
43
+ | EAN-13 | ✓ | ✓ |
44
+ | ITF | ✓ | ✓ |
45
+ | MSI | ✗ | ✗ |
46
+ | RSS Expanded | ✗ | ✗ |
47
+ | RSS-14 | ✗ | ✗ |
48
+ | UPC-A | ✓ | ✓ |
49
+ | UPC-E | ✓ | ✓ |
50
+
51
+ | 2d formats | Android | iOS |
52
+ |-------------|---------|-----|
53
+ | Aztec | ✓ | ✓ |
54
+ | Codablock | ✗ | ✗ |
55
+ | Data Matrix | ✓ | ✓ |
56
+ | MaxiCode | ✗ | ✗ |
57
+ | PDF417 | ✓ | ✓ |
58
+ | QR Code | ✓ | ✓ |
59
+
60
+ #### MLKit does not detect the following barcodes:
61
+
62
+ - 1D Barcodes with only one character
63
+ - Barcodes in ITF format with fewer than six characters
64
+ - Barcodes encoded with FNC2, FNC3 or FNC4
65
+ - QR codes generated in the ECI mode
66
+
67
+ ### Plugin Options
68
+
69
+ The default options are shown below.
70
+ All values are optional.
71
+
72
+ Note that the `detectorSize` value must be between `0` and `1`, because it determines how many percent of the screen should be covered by the detector.
73
+ If the value is greater than 1 the detector will not be visible on the screen.
74
+
75
+ ```javascript
76
+ const defaultOptions = {
77
+ barcodeFormats: {
78
+ Code128: true,
79
+ Code39: true,
80
+ Code93: true,
81
+ CodaBar: true,
82
+ DataMatrix: true,
83
+ EAN13: true,
84
+ EAN8: true,
85
+ ITF: true,
86
+ QRCode: true,
87
+ UPCA: true,
88
+ UPCE: true,
89
+ PDF417: true,
90
+ Aztec: true,
91
+ },
92
+ beepOnSuccess: false,
93
+ vibrateOnSuccess: false,
94
+ detectorSize: 0.6,
95
+ detectorAspectRatio: "1:1",
96
+ drawFocusRect: true,
97
+ focusRectColor: "#FFFFFF",
98
+ focusRectBorderRadius: 100,
99
+ focusRectBorderThickness: 5,
100
+ drawFocusLine: false,
101
+ focusLineColor: "#ff2d37",
102
+ focusLineThickness: 2,
103
+ drawFocusBackground: false,
104
+ focusBackgroundColor: "#66FFFFFF",
105
+ stableThreshold: 5,
106
+ debugOverlay: false,
107
+ ignoreRotatedBarcodes: false
108
+ };
109
+ ```
110
+
111
+ ### Barcode Detection
112
+
113
+ All barcodes that are on the image are detected by MLKit. Once the barcodes detected haven't changed for a configurable amount of images (`stableThreshold`) they enter a secondary logic that checks if the barcodes found are in the scan area. Barcodes are considered inside if the center line of the barcode fits into the scan area completely. If the option `ignoreRotatedBarcodes` is set to `true` then barcodes additionally have to have a center line that matches the device rotation. This is done to be able to sort out barcodes rotated by 90 degree.
114
+
115
+ ## API
116
+
117
+ <docgen-index>
118
+
119
+ * [`scan(...)`](#scan)
120
+ * [Interfaces](#interfaces)
121
+
122
+ </docgen-index>
123
+
124
+ <docgen-api>
125
+ <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
126
+
127
+ ### scan(...)
128
+
129
+ ```typescript
130
+ scan(settings: ISettings) => Promise<IResult>
131
+ ```
132
+
133
+ Opens a camera preview for barcode scanning and automatically detects barcodes in the scan area.
134
+ In case of unsuccessful scan the reason is thrown as an error.
135
+
136
+ __Possible errors:__
137
+ * CANCELED
138
+ * NO_CAMERA
139
+ * NO_CAMERA_PERMISSION
140
+ * JSON_EXCEPTION
141
+ * PLATFORM_NOT_SUPPORTED
142
+
143
+ | Param | Type | Description |
144
+ | -------------- | ----------------------------------------------- | -------------------------------- |
145
+ | **`settings`** | <code><a href="#isettings">ISettings</a></code> | settings to be used for the scan |
146
+
147
+ **Returns:** <code>Promise&lt;<a href="#iresult">IResult</a>&gt;</code>
148
+
149
+ --------------------
150
+
151
+
152
+ ### Interfaces
153
+
154
+
155
+ #### IResult
156
+
157
+ The result of the plugin is an object with a list of detected barcodes, sorted by
158
+ distanceToCenter with the lowest value being first. DistanceToCenter is calculated by how
159
+ far away the center of the barcode is from the center of the scan area.
160
+
161
+ | Prop | Type |
162
+ | -------------- | ----------------------------------------------------------------------------------------- |
163
+ | **`barcodes`** | <code>[{ value: string; format: string; type: string; distanceToCenter: number; }]</code> |
164
+
165
+
166
+ #### ISettings
167
+
168
+ All settings that can be passed to the plugin. The `detectorSize` value must be between
169
+ `0` and `1`, because it determines how many percent of the screen should be covered by
170
+ the detector.
171
+ If the value is greater than 1 the detector will not be visible on the screen.
172
+
173
+ | Prop | Type |
174
+ | ------------------------------ | ----------------------------------------------------------- |
175
+ | **`barcodeFormats`** | <code><a href="#ibarcodeformats">IBarcodeFormats</a></code> |
176
+ | **`beepOnSuccess`** | <code>boolean</code> |
177
+ | **`vibrateOnSuccess`** | <code>boolean</code> |
178
+ | **`detectorSize`** | <code>number</code> |
179
+ | **`detectorAspectRatio`** | <code>string</code> |
180
+ | **`drawFocusRect`** | <code>boolean</code> |
181
+ | **`focusRectColor`** | <code>string</code> |
182
+ | **`focusRectBorderRadius`** | <code>number</code> |
183
+ | **`focusRectBorderThickness`** | <code>number</code> |
184
+ | **`drawFocusLine`** | <code>boolean</code> |
185
+ | **`focusLineColor`** | <code>string</code> |
186
+ | **`focusLineThickness`** | <code>number</code> |
187
+ | **`drawFocusBackground`** | <code>boolean</code> |
188
+ | **`focusBackgroundColor`** | <code>string</code> |
189
+ | **`stableThreshold`** | <code>number</code> |
190
+ | **`debugOverlay`** | <code>boolean</code> |
191
+
192
+
193
+ #### IBarcodeFormats
194
+
195
+ Options to make it possible to narrow down the barcode types scanned.
196
+
197
+ | Prop | Type |
198
+ | ---------------- | -------------------- |
199
+ | **`Aztec`** | <code>boolean</code> |
200
+ | **`CodaBar`** | <code>boolean</code> |
201
+ | **`Code39`** | <code>boolean</code> |
202
+ | **`Code93`** | <code>boolean</code> |
203
+ | **`Code128`** | <code>boolean</code> |
204
+ | **`DataMatrix`** | <code>boolean</code> |
205
+ | **`EAN8`** | <code>boolean</code> |
206
+ | **`EAN13`** | <code>boolean</code> |
207
+ | **`ITF`** | <code>boolean</code> |
208
+ | **`PDF417`** | <code>boolean</code> |
209
+ | **`QRCode`** | <code>boolean</code> |
210
+ | **`UPCA`** | <code>boolean</code> |
211
+ | **`UPCE`** | <code>boolean</code> |
212
+
213
+ </docgen-api>
214
+
215
+ ## Known Issues
216
+
217
+ ### Sometimes the orientation of the barcode is detected wrongly
218
+
219
+ This is an issue with the fact that MLKit doesn't actually expose the barcode orientation, so any orientation that is used by this plugin basically just checks for the longest side of the barcode bounds. With square barcodes this causes some display issues in the debug overlay, and detection issues with `ignoreRotatedBarcodes` set to `true`. If you expect square barcodes (or qr codes) don't use that setting.
220
+
221
+ ### Barcodes are only read partially
222
+
223
+ Another MLKit "feature". Happens especially often on ITF barcodes because the barcode checksum check isn't implemented in MLKit. Should be somewhat mitigated by the detection using the center line of the barcode, but sometimes only parts of the barcode will be read anyway. Only way to deal with it is to play around with `stableThreshold` until you have enough time to get the whole barcode in the scan area.
224
+
225
+ ### Barcode detection doesn't detect all barcodes
226
+
227
+ More MLKit issues. When having multiple barcodes on screen, not all of them are detected all the time. If you are wondering why some barcodes are missing, activate `debugOverlay` and you will see which barcodes MLKIt found.
228
+
229
+ ### [Android] Torch Button is behind the drawn background
230
+
231
+ On android, I use a SurfaceView to draw the overlay, including the full background, on. The only way to get this to be transparent so the camera can be seen behind it is to use ```setZOrderOnTop(true)``` on it, which makes it impossible for the button to be shown over it. This becomes a problem when a (nearly) opaque background is selected because then the button can't be seen anymore. NOTE: The button still works, it is just rendered behind the background.
@@ -0,0 +1,65 @@
1
+ ext {
2
+ androidxCameraVersion = project.hasProperty('androidxCameraVersion') ? rootProject.ext.androidxCameraVersion : '1.3.2'
3
+ mlkitBarcodeScanningVersion = project.hasProperty('mlkitBarcodeScanningVersion') ? rootProject.ext.mlkitBarcodeScanningVersion : '17.2.0'
4
+ mlKitBarcodeScanningPlayServiceVersion = project.hasProperty('mlKitBarcodeScanningPlayServiceVersion') ? rootProject.ext.mlKitBarcodeScanningPlayServiceVersion : '18.3.0'
5
+ androidxConstraintLayoutVersion = project.hasProperty('androidxConstraintLayoutVersion') ? rootProject.ext.androidxConstraintLayoutVersion : '2.1.4'
6
+ androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.6.1'
7
+ }
8
+
9
+ buildscript {
10
+ repositories {
11
+ google()
12
+ mavenCentral()
13
+ }
14
+ dependencies {
15
+ classpath 'com.android.tools.build:gradle:8.1.4'
16
+ }
17
+ }
18
+
19
+ apply plugin: 'com.android.library'
20
+
21
+ android {
22
+ namespace "com.biso.capacitor.plugins.mlkit.barcode.scanner"
23
+ defaultConfig {
24
+ compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 34
25
+ minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 26
26
+ targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 34
27
+ versionCode 107
28
+ versionName "1.0.7"
29
+ }
30
+ buildTypes {
31
+ release {
32
+ minifyEnabled false
33
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
34
+ }
35
+ }
36
+ lintOptions {
37
+ abortOnError false
38
+ }
39
+ compileOptions {
40
+ sourceCompatibility JavaVersion.VERSION_17
41
+ targetCompatibility JavaVersion.VERSION_17
42
+ }
43
+ buildFeatures {
44
+ viewBinding true
45
+ }
46
+ }
47
+
48
+ repositories {
49
+ google()
50
+ mavenCentral()
51
+ }
52
+
53
+
54
+ dependencies {
55
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
56
+ implementation project(':capacitor-android')
57
+ implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
58
+ implementation "androidx.constraintlayout:constraintlayout:$androidxConstraintLayoutVersion"
59
+ implementation "androidx.camera:camera-camera2:$androidxCameraVersion"
60
+ implementation "androidx.camera:camera-core:$androidxCameraVersion"
61
+ implementation "androidx.camera:camera-lifecycle:$androidxCameraVersion"
62
+ implementation "androidx.camera:camera-view:$androidxCameraVersion"
63
+ implementation "com.google.mlkit:barcode-scanning:$mlkitBarcodeScanningVersion"
64
+ implementation "com.google.android.gms:play-services-mlkit-barcode-scanning:$mlKitBarcodeScanningPlayServiceVersion"
65
+ }
@@ -0,0 +1,8 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ <uses-permission android:name="android.permission.VIBRATE" />
3
+ <application android:hardwareAccelerated="true" android:supportsRtl="true">
4
+ <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
5
+ <meta-data android:name="com.google.android.gms.vision.DEPENDENCIES" android:value="barcode" />
6
+ <activity android:label="Read Barcode" android:name=".CaptureActivity" android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
7
+ </application>
8
+ </manifest>
Binary file
@@ -0,0 +1,119 @@
1
+ package com.biso.capacitor.plugins.mlkit.barcode.scanner;
2
+
3
+ import static com.biso.capacitor.plugins.mlkit.barcode.scanner.Utils.getTranslationMatrix;
4
+ import static com.biso.capacitor.plugins.mlkit.barcode.scanner.Utils.mapRect;
5
+
6
+ import android.annotation.SuppressLint;
7
+ import android.content.Intent;
8
+ import android.graphics.Matrix;
9
+ import android.graphics.RectF;
10
+ import android.media.Image;
11
+ import android.util.Log;
12
+ import androidx.annotation.NonNull;
13
+ import androidx.camera.core.ImageAnalysis.Analyzer;
14
+ import androidx.camera.core.ImageProxy;
15
+ import com.google.android.gms.tasks.Task;
16
+ import com.google.android.gms.tasks.Tasks;
17
+ import com.google.mlkit.vision.barcode.BarcodeScanner;
18
+ import com.google.mlkit.vision.barcode.BarcodeScannerOptions;
19
+ import com.google.mlkit.vision.barcode.BarcodeScanning;
20
+ import com.google.mlkit.vision.barcode.common.Barcode;
21
+ import com.google.mlkit.vision.common.InputImage;
22
+ import java.util.ArrayList;
23
+ import java.util.HashSet;
24
+ import java.util.List;
25
+ import java.util.concurrent.ExecutionException;
26
+ import java.util.stream.Collectors;
27
+
28
+ public class BarcodeAnalyzer implements Analyzer {
29
+
30
+ public static final String BARCODES = "barcodes";
31
+ private static final String ANALYZER = "BarcodeAnalyzer";
32
+ private List<DetectedBarcode> lastBarcodes;
33
+ private int stableCounter = 0;
34
+ private final BarcodeScanner scanner;
35
+ private final CameraOverlay cameraOverlay;
36
+ private final BarcodesListener barcodesListener;
37
+ private final ScannerSettings settings;
38
+
39
+ public BarcodeAnalyzer(ScannerSettings settings, BarcodesListener barcodesListener,
40
+ CameraOverlay cameraOverlay) {
41
+ int useBarcodeFormats = settings.getBarcodeFormats();
42
+ if (useBarcodeFormats == 0 || useBarcodeFormats == 1234) {
43
+ useBarcodeFormats = (Barcode.FORMAT_CODE_39 | Barcode.FORMAT_DATA_MATRIX);
44
+ }
45
+ scanner = BarcodeScanning
46
+ .getClient(
47
+ new BarcodeScannerOptions.Builder().setBarcodeFormats(useBarcodeFormats).build());
48
+ this.settings = settings;
49
+ this.lastBarcodes = new ArrayList<>();
50
+ this.barcodesListener = barcodesListener;
51
+ this.cameraOverlay = cameraOverlay;
52
+ }
53
+
54
+ @Override
55
+ public void analyze(@NonNull ImageProxy imageProxy) {
56
+ @SuppressLint("UnsafeOptInUsageError")
57
+ Image image = imageProxy.getImage();
58
+ if (image == null) {
59
+ imageProxy.close();
60
+ return;
61
+ }
62
+ InputImage inputImage = InputImage.fromMediaImage(image, imageProxy.getImageInfo().getRotationDegrees());
63
+ Task<List<Barcode>> scannerTask = scanner.process(inputImage);
64
+ try {
65
+ List<Barcode> barcodes = Tasks.await(scannerTask);
66
+
67
+ RectF source;
68
+ if (inputImage.getRotationDegrees() == 90) {
69
+ source = new RectF(0, 0, inputImage.getHeight(), inputImage.getWidth());
70
+ } else {
71
+ source = new RectF(0, 0, inputImage.getWidth(), inputImage.getHeight());
72
+ }
73
+ Matrix matrix = getTranslationMatrix(source, cameraOverlay.getSurfaceArea());
74
+
75
+ List<DetectedBarcode> detectedBarcodes = barcodes.stream().map(
76
+ barcode -> new DetectedBarcode(barcode, mapRect(barcode.getBoundingBox(), matrix),
77
+ cameraOverlay.getScanArea().centerX(), cameraOverlay.getScanArea().centerY()))
78
+ .collect(Collectors.toList());
79
+
80
+ if (settings.isDebugOverlay()) {
81
+ cameraOverlay.drawDebugOverlay(detectedBarcodes);
82
+ }
83
+
84
+ if (areBarcodesStable(detectedBarcodes) && stableCounter >= settings.getStableThreshold()) {
85
+ ArrayList<DetectedBarcode> barcodesInScanArea = (ArrayList<DetectedBarcode>) detectedBarcodes.stream()
86
+ .filter(barcode -> barcode.isInScanArea(cameraOverlay.getScanArea(), settings.isIgnoreRotatedBarcodes())).sorted()
87
+ .collect(Collectors.toList());
88
+
89
+ if (!barcodesInScanArea.isEmpty()) {
90
+ Intent data = new Intent();
91
+ data.putParcelableArrayListExtra(BARCODES, barcodesInScanArea);
92
+ barcodesListener.onBarcodesFound(data);
93
+ } else {
94
+ stableCounter = 0;
95
+ lastBarcodes = new ArrayList<>();
96
+ }
97
+ }
98
+ } catch (ExecutionException e) {
99
+ Log.e(ANALYZER, e.getMessage());
100
+ } catch (InterruptedException e) {
101
+ Log.e(ANALYZER, e.getMessage());
102
+ Thread.currentThread().interrupt();
103
+ }
104
+ imageProxy.close();
105
+ }
106
+
107
+ private boolean areBarcodesStable(List<DetectedBarcode> barcodes) {
108
+ if (!barcodes.isEmpty() && (barcodes.size() == lastBarcodes.size())
109
+ && (new HashSet<>(lastBarcodes).containsAll(barcodes))) {
110
+ stableCounter++;
111
+ Log.d(ANALYZER,
112
+ "barcodes stable for " + stableCounter + "/" + settings.getStableThreshold());
113
+ return true;
114
+ }
115
+ lastBarcodes = barcodes;
116
+ stableCounter = 0;
117
+ return false;
118
+ }
119
+ }
@@ -0,0 +1,40 @@
1
+ package com.biso.capacitor.plugins.mlkit.barcode.scanner;
2
+
3
+ import com.google.mlkit.vision.barcode.common.Barcode;
4
+
5
+ // suppress uppercase rule for enum naming (this matches js and swift code)
6
+ @SuppressWarnings("java:S115")
7
+ public enum BarcodeFormat {
8
+ Code128(Barcode.FORMAT_CODE_128),
9
+ Code39(Barcode.FORMAT_CODE_39),
10
+ Code93(Barcode.FORMAT_CODE_93),
11
+ CodaBar(Barcode.FORMAT_CODABAR),
12
+ DataMatrix(Barcode.FORMAT_DATA_MATRIX),
13
+ EAN13(Barcode.FORMAT_EAN_13),
14
+ EAN8(Barcode.FORMAT_EAN_8),
15
+ ITF(Barcode.FORMAT_ITF),
16
+ QRCode(Barcode.FORMAT_QR_CODE),
17
+ UPCA(Barcode.FORMAT_UPC_A),
18
+ UPCE(Barcode.FORMAT_UPC_E),
19
+ PDF417(Barcode.FORMAT_PDF417),
20
+ Aztec(Barcode.FORMAT_AZTEC);
21
+
22
+ private final int format;
23
+
24
+ BarcodeFormat(int formatFlag) {
25
+ this.format = formatFlag;
26
+ }
27
+
28
+ public int getAsInt() {
29
+ return format;
30
+ }
31
+
32
+ public static BarcodeFormat getFromInt(int intValue) {
33
+ for (BarcodeFormat value : BarcodeFormat.values()) {
34
+ if(value.format == intValue) {
35
+ return value;
36
+ }
37
+ }
38
+ return null;
39
+ }
40
+ }
@@ -0,0 +1,38 @@
1
+ package com.biso.capacitor.plugins.mlkit.barcode.scanner;
2
+
3
+ import com.getcapacitor.JSObject;
4
+ import java.util.Iterator;
5
+ import org.json.JSONException;
6
+ import org.json.JSONObject;
7
+
8
+ public class BarcodeFormats {
9
+
10
+ private final JSObject formats = new JSObject();
11
+
12
+ public BarcodeFormats(JSONObject barcodeFormats) {
13
+ if (barcodeFormats == null) {
14
+ barcodeFormats = new JSObject();
15
+ }
16
+ for (BarcodeFormat value : BarcodeFormat.values()) {
17
+ boolean format = barcodeFormats.optBoolean(String.valueOf(value), barcodeFormats.length() == 0);
18
+ formats.put(String.valueOf(value), format);
19
+ }
20
+ }
21
+
22
+ public int getFormatFlags() {
23
+ int flags = 0;
24
+ try {
25
+ Iterator<String> iterator = formats.keys();
26
+ while (iterator.hasNext()) {
27
+ String key = iterator.next();
28
+ BarcodeFormat format = BarcodeFormat.valueOf(key);
29
+ if (formats.has(key) && formats.getBoolean(key)) {
30
+ flags += format.getAsInt();
31
+ }
32
+ }
33
+ } catch (JSONException e) {
34
+ // should not happen, the json is only used in this class
35
+ }
36
+ return flags;
37
+ }
38
+ }
@@ -0,0 +1,37 @@
1
+ package com.biso.capacitor.plugins.mlkit.barcode.scanner;
2
+
3
+ import com.google.mlkit.vision.barcode.common.Barcode;
4
+
5
+ public enum BarcodeType {
6
+ UNKNOWN(Barcode.TYPE_UNKNOWN),
7
+ CONTACT_INFO(Barcode.TYPE_CONTACT_INFO),
8
+ EMAIL(Barcode.TYPE_EMAIL),
9
+ ISBN(Barcode.TYPE_ISBN),
10
+ PHONE(Barcode.TYPE_PHONE),
11
+ PRODUCT(Barcode.TYPE_PRODUCT),
12
+ SMS(Barcode.TYPE_SMS),
13
+ TEXT(Barcode.TYPE_TEXT),
14
+ URL(Barcode.TYPE_URL),
15
+ WIFI(Barcode.TYPE_WIFI),
16
+ GEO(Barcode.TYPE_GEO),
17
+ CALENDAR_EVENT(Barcode.TYPE_CALENDAR_EVENT),
18
+ DRIVER_LICENSE(Barcode.TYPE_DRIVER_LICENSE);
19
+ private final int type;
20
+
21
+ BarcodeType(int type) {
22
+ this.type = type;
23
+ }
24
+
25
+ public int getAsInt() {
26
+ return type;
27
+ }
28
+
29
+ public static BarcodeType getFromInt(int intValue) {
30
+ for (BarcodeType value : BarcodeType.values()) {
31
+ if(value.type == intValue) {
32
+ return value;
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+ }
@@ -0,0 +1,8 @@
1
+ package com.biso.capacitor.plugins.mlkit.barcode.scanner;
2
+
3
+ import android.content.Intent;
4
+
5
+ public interface BarcodesListener {
6
+
7
+ void onBarcodesFound(Intent barcodes);
8
+ }