@scr2em/capacitor-scanner 6.0.24 → 6.0.26
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.
- package/README.md +10 -6
- package/android/src/main/java/com/leadliaion/capacitorscanner/CapacitorScannerPlugin.java +42 -32
- package/dist/docs.json +13 -8
- package/dist/esm/definitions.d.ts +23 -15
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapacitorScannerPlugin/CapacitorScannerPlugin.swift +18 -14
- package/ios/Sources/CapacitorScannerPlugin/RectangleDetector.swift +32 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -183,12 +183,14 @@ removeAllListeners() => any
|
|
|
183
183
|
### enableObjectDetection(...)
|
|
184
184
|
|
|
185
185
|
```typescript
|
|
186
|
-
enableObjectDetection(options
|
|
186
|
+
enableObjectDetection(options?: ObjectDetectionOptions | undefined) => any
|
|
187
187
|
```
|
|
188
188
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
|
189
|
+
Enable object detection (business card). This will start detecting business cards.
|
|
190
|
+
|
|
191
|
+
| Param | Type | Description |
|
|
192
|
+
| ------------- | ------------------------------------------------------------------------- | --------------------------------------------- |
|
|
193
|
+
| **`options`** | <code><a href="#objectdetectionoptions">ObjectDetectionOptions</a></code> | - Optional configuration for object detection |
|
|
192
194
|
|
|
193
195
|
**Returns:** <code>any</code>
|
|
194
196
|
|
|
@@ -201,6 +203,8 @@ enableObjectDetection(options: ObjectDetectionOptions) => any
|
|
|
201
203
|
disableObjectDetection() => any
|
|
202
204
|
```
|
|
203
205
|
|
|
206
|
+
Disable object detection. This will stop detecting business cards.
|
|
207
|
+
|
|
204
208
|
**Returns:** <code>any</code>
|
|
205
209
|
|
|
206
210
|
--------------------
|
|
@@ -258,7 +262,7 @@ Set the camera zoom level.
|
|
|
258
262
|
|
|
259
263
|
#### StartOptions
|
|
260
264
|
|
|
261
|
-
<code>{ /** * Barcode formats to detect when barcodeDetection is enabled. */ formats?: BarcodeFormat[]; /** * Camera direction to use. Defaults to 'BACK'. */ cameraDirection?: 'BACK' | 'FRONT'; /** * Enable barcode detection on start. Defaults to false. * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events. */ barcodeDetection?: boolean; /** * Whether to show the highlight overlay for detected barcodes. Defaults to false. * Only applies when barcodeDetection is true. */ barcodeHighlight?: boolean; /** * Enable object detection (business card) on start. Defaults to false. * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events. */ objectDetection?: boolean; /** * Whether to show the highlight overlay for detected business cards. Defaults to true. * Only applies when objectDetection is true. */ objectHighlight?: boolean; debounceTimeInMilli?: number; /** * Optional regex pattern to filter scanned barcodes. * If provided, only barcodes matching this pattern will be reported. */ regex?: string; /** * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline). */ regexFlags?: string; }</code>
|
|
265
|
+
<code>{ /** * Barcode formats to detect when barcodeDetection is enabled. */ formats?: BarcodeFormat[]; /** * Camera direction to use. Defaults to 'BACK'. */ cameraDirection?: 'BACK' | 'FRONT'; /** * Enable barcode detection on start. Defaults to false. * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events. */ barcodeDetection?: boolean; /** * Whether to show the highlight overlay for detected barcodes. Defaults to false. * Only applies when barcodeDetection is true. */ barcodeHighlight?: boolean; /** * Enable object detection (business card) on start. Defaults to false. * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events. */ objectDetection?: boolean; /** * Whether to show the highlight overlay for detected business cards. Defaults to true. * Only applies when objectDetection is true. */ objectHighlight?: boolean; /** * Minimum interval in seconds between consecutive 'rectangleDetected' events. * Only applies when objectDetection is true. * Defaults to 0 (no throttling beyond initial stability check). */ rectangleEmitIntervalSeconds?: number; debounceTimeInMilli?: number; /** * Optional regex pattern to filter scanned barcodes. * If provided, only barcodes matching this pattern will be reported. */ regex?: string; /** * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline). */ regexFlags?: string; }</code>
|
|
262
266
|
|
|
263
267
|
|
|
264
268
|
#### CapturePhotoOptions
|
|
@@ -293,7 +297,7 @@ Set the camera zoom level.
|
|
|
293
297
|
|
|
294
298
|
#### ObjectDetectionOptions
|
|
295
299
|
|
|
296
|
-
<code>{ /** *
|
|
300
|
+
<code>{ /** * Whether to show the highlight overlay for detected business cards. * Defaults to true. */ showHighlight?: boolean, /** * Minimum interval in seconds between consecutive 'rectangleDetected' events. * After an event is emitted, no new events will be emitted until this interval passes. * Set to 0 to emit events as soon as stability is detected. * Defaults to 0 (no throttling beyond initial stability check). */ emitIntervalSeconds?: number }</code>
|
|
297
301
|
|
|
298
302
|
|
|
299
303
|
#### BarcodeDetectionOptions
|
|
@@ -140,6 +140,9 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
140
140
|
private long lastRectangleDetectionTime = 0; // Tracks when we last had a valid rectangle detection
|
|
141
141
|
private int processingFrequency = 10; // Default: 10 rectangle detections per second
|
|
142
142
|
|
|
143
|
+
// Configurable interval between rectangle detection event emissions (in milliseconds)
|
|
144
|
+
private long rectangleEmitIntervalMs = 0; // Default: 0 (no throttling)
|
|
145
|
+
|
|
143
146
|
/**
|
|
144
147
|
* Calculates the processing interval in milliseconds based on the desired
|
|
145
148
|
* frequency per second.
|
|
@@ -212,6 +215,19 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
212
215
|
if (enableObjectHighlight) {
|
|
213
216
|
enabledHighlightTypes.add("businessCard");
|
|
214
217
|
}
|
|
218
|
+
// Read rectangleEmitIntervalSeconds if provided
|
|
219
|
+
if (call.getData().has("rectangleEmitIntervalSeconds")) {
|
|
220
|
+
try {
|
|
221
|
+
double intervalSeconds = call.getData().getDouble("rectangleEmitIntervalSeconds");
|
|
222
|
+
rectangleEmitIntervalMs = Math.max(0, (long) (intervalSeconds * 1000));
|
|
223
|
+
echo("Using custom rectangleEmitIntervalSeconds: " + intervalSeconds + "s (" + rectangleEmitIntervalMs + "ms)");
|
|
224
|
+
} catch (Exception e) {
|
|
225
|
+
echo("Error parsing rectangleEmitIntervalSeconds: " + e.getMessage() + ". Using default 0ms");
|
|
226
|
+
rectangleEmitIntervalMs = 0;
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
rectangleEmitIntervalMs = 0;
|
|
230
|
+
}
|
|
215
231
|
}
|
|
216
232
|
|
|
217
233
|
try {
|
|
@@ -867,10 +883,8 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
867
883
|
long currentTime = System.currentTimeMillis();
|
|
868
884
|
|
|
869
885
|
if (!rectangles.isEmpty()) {
|
|
870
|
-
// Only notify if
|
|
871
|
-
|
|
872
|
-
long NOTIFICATION_INTERVAL_MS = 1000;
|
|
873
|
-
if (currentTime - lastRectangleNotificationTime >= NOTIFICATION_INTERVAL_MS) {
|
|
886
|
+
// Only notify if the configured interval has passed since last notification
|
|
887
|
+
if (currentTime - lastRectangleNotificationTime >= rectangleEmitIntervalMs) {
|
|
874
888
|
// Find the largest rectangle if multiple are detected
|
|
875
889
|
Rectangle largestRectangle = null;
|
|
876
890
|
float largestArea = 0;
|
|
@@ -1127,6 +1141,7 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1127
1141
|
lastRectangleNotificationTime = 0;
|
|
1128
1142
|
lastRectangleProcessingTime = 0;
|
|
1129
1143
|
lastRectangleDetectionTime = 0;
|
|
1144
|
+
rectangleEmitIntervalMs = 0;
|
|
1130
1145
|
|
|
1131
1146
|
// Clear detection histories and caches
|
|
1132
1147
|
enabledDetectionTypes.clear();
|
|
@@ -1147,9 +1162,12 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1147
1162
|
@PluginMethod
|
|
1148
1163
|
public void enableObjectDetection(PluginCall call) {
|
|
1149
1164
|
echo("enableObjectDetection");
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1165
|
+
|
|
1166
|
+
// If object detection is already enabled, just return success
|
|
1167
|
+
if (enabledDetectionTypes.contains("businessCard")) {
|
|
1168
|
+
JSObject result = new JSObject();
|
|
1169
|
+
result.put("enabled", true);
|
|
1170
|
+
call.resolve(result);
|
|
1153
1171
|
return;
|
|
1154
1172
|
}
|
|
1155
1173
|
|
|
@@ -1172,27 +1190,25 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1172
1190
|
// Read showHighlight option (defaults to true for businessCard)
|
|
1173
1191
|
boolean showHighlight = call.getBoolean("showHighlight", true);
|
|
1174
1192
|
|
|
1175
|
-
//
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
String type = types.getString(i);
|
|
1180
|
-
if ("businessCard".equals(type)) {
|
|
1181
|
-
enabledDetectionTypes.add(type);
|
|
1182
|
-
if (showHighlight) {
|
|
1183
|
-
enabledHighlightTypes.add(type);
|
|
1184
|
-
}
|
|
1185
|
-
addedTypes.add(type);
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
} catch (Exception e) {
|
|
1189
|
-
call.reject("Error processing types array.", e);
|
|
1190
|
-
return;
|
|
1193
|
+
// Enable businessCard detection
|
|
1194
|
+
enabledDetectionTypes.add("businessCard");
|
|
1195
|
+
if (showHighlight) {
|
|
1196
|
+
enabledHighlightTypes.add("businessCard");
|
|
1191
1197
|
}
|
|
1192
1198
|
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1199
|
+
// Read the optional emitIntervalSeconds parameter
|
|
1200
|
+
if (call.getData().has("emitIntervalSeconds")) {
|
|
1201
|
+
try {
|
|
1202
|
+
double intervalSeconds = call.getData().getDouble("emitIntervalSeconds");
|
|
1203
|
+
rectangleEmitIntervalMs = Math.max(0, (long) (intervalSeconds * 1000));
|
|
1204
|
+
echo("Using custom emitIntervalSeconds: " + intervalSeconds + "s (" + rectangleEmitIntervalMs + "ms)");
|
|
1205
|
+
} catch (Exception e) {
|
|
1206
|
+
echo("Error parsing emitIntervalSeconds: " + e.getMessage() + ". Using default 0ms");
|
|
1207
|
+
rectangleEmitIntervalMs = 0;
|
|
1208
|
+
}
|
|
1209
|
+
} else {
|
|
1210
|
+
// Reset to default if not specified
|
|
1211
|
+
rectangleEmitIntervalMs = 0;
|
|
1196
1212
|
}
|
|
1197
1213
|
|
|
1198
1214
|
// Read the optional paddingRatio parameter
|
|
@@ -1222,11 +1238,6 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1222
1238
|
|
|
1223
1239
|
JSObject result = new JSObject();
|
|
1224
1240
|
result.put("enabled", true);
|
|
1225
|
-
try {
|
|
1226
|
-
result.put("types", new JSArray(addedTypes));
|
|
1227
|
-
} catch (Exception e) {
|
|
1228
|
-
// this should not happen
|
|
1229
|
-
}
|
|
1230
1241
|
|
|
1231
1242
|
if (enabledHighlightTypes.contains("businessCard")) {
|
|
1232
1243
|
getActivity().runOnUiThread(() -> {
|
|
@@ -1261,7 +1272,6 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1261
1272
|
|
|
1262
1273
|
JSObject result = new JSObject();
|
|
1263
1274
|
result.put("enabled", false);
|
|
1264
|
-
result.put("types", new JSArray());
|
|
1265
1275
|
call.resolve(result);
|
|
1266
1276
|
}
|
|
1267
1277
|
|
package/dist/docs.json
CHANGED
|
@@ -171,17 +171,22 @@
|
|
|
171
171
|
},
|
|
172
172
|
{
|
|
173
173
|
"name": "enableObjectDetection",
|
|
174
|
-
"signature": "(options
|
|
174
|
+
"signature": "(options?: ObjectDetectionOptions | undefined) => any",
|
|
175
175
|
"parameters": [
|
|
176
176
|
{
|
|
177
177
|
"name": "options",
|
|
178
|
-
"docs": "",
|
|
179
|
-
"type": "ObjectDetectionOptions"
|
|
178
|
+
"docs": "- Optional configuration for object detection",
|
|
179
|
+
"type": "ObjectDetectionOptions | undefined"
|
|
180
180
|
}
|
|
181
181
|
],
|
|
182
182
|
"returns": "any",
|
|
183
|
-
"tags": [
|
|
184
|
-
|
|
183
|
+
"tags": [
|
|
184
|
+
{
|
|
185
|
+
"name": "param",
|
|
186
|
+
"text": "options - Optional configuration for object detection"
|
|
187
|
+
}
|
|
188
|
+
],
|
|
189
|
+
"docs": "Enable object detection (business card). This will start detecting business cards.",
|
|
185
190
|
"complexTypes": [
|
|
186
191
|
"ObjectDetectionOptions"
|
|
187
192
|
],
|
|
@@ -193,7 +198,7 @@
|
|
|
193
198
|
"parameters": [],
|
|
194
199
|
"returns": "any",
|
|
195
200
|
"tags": [],
|
|
196
|
-
"docs": "",
|
|
201
|
+
"docs": "Disable object detection. This will stop detecting business cards.",
|
|
197
202
|
"complexTypes": [],
|
|
198
203
|
"slug": "disableobjectdetection"
|
|
199
204
|
},
|
|
@@ -339,7 +344,7 @@
|
|
|
339
344
|
"docs": "",
|
|
340
345
|
"types": [
|
|
341
346
|
{
|
|
342
|
-
"text": "{\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n debounceTimeInMilli?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n}",
|
|
347
|
+
"text": "{\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n debounceTimeInMilli?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n}",
|
|
343
348
|
"complexTypes": [
|
|
344
349
|
"BarcodeFormat"
|
|
345
350
|
]
|
|
@@ -418,7 +423,7 @@
|
|
|
418
423
|
"docs": "",
|
|
419
424
|
"types": [
|
|
420
425
|
{
|
|
421
|
-
"text": "{\n /**\n *
|
|
426
|
+
"text": "{\n /**\n * Whether to show the highlight overlay for detected business cards.\n * Defaults to true.\n */\n showHighlight?: boolean,\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * After an event is emitted, no new events will be emitted until this interval passes.\n * Set to 0 to emit events as soon as stability is detected.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n emitIntervalSeconds?: number\n}",
|
|
422
427
|
"complexTypes": []
|
|
423
428
|
}
|
|
424
429
|
]
|
|
@@ -17,10 +17,18 @@ export interface CapacitorScannerPlugin {
|
|
|
17
17
|
addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;
|
|
18
18
|
addListener(event: 'rectangleDetected', listenerFunc: (result: RectangleDetectedEvent) => void): Promise<void>;
|
|
19
19
|
removeAllListeners(): Promise<void>;
|
|
20
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Enable object detection (business card). This will start detecting business cards.
|
|
22
|
+
* @param options - Optional configuration for object detection
|
|
23
|
+
*/
|
|
24
|
+
enableObjectDetection(options?: ObjectDetectionOptions): Promise<{
|
|
25
|
+
enabled: true;
|
|
26
|
+
}>;
|
|
27
|
+
/**
|
|
28
|
+
* Disable object detection. This will stop detecting business cards.
|
|
29
|
+
*/
|
|
21
30
|
disableObjectDetection(): Promise<{
|
|
22
31
|
enabled: false;
|
|
23
|
-
types: [];
|
|
24
32
|
}>;
|
|
25
33
|
/**
|
|
26
34
|
* Enable barcode detection. This will start detecting barcodes.
|
|
@@ -49,24 +57,18 @@ export declare type BarcodeDetectionOptions = {
|
|
|
49
57
|
showHighlight?: boolean;
|
|
50
58
|
};
|
|
51
59
|
export declare type ObjectDetectionOptions = {
|
|
52
|
-
/**
|
|
53
|
-
* Detection types to enable. Only 'businessCard' is supported.
|
|
54
|
-
* For barcode detection, use enableBarcodeDetection() instead.
|
|
55
|
-
*/
|
|
56
|
-
types: ('businessCard')[];
|
|
57
|
-
/**
|
|
58
|
-
* Optional padding ratio to apply around detected rectangles when cropping.
|
|
59
|
-
* Value must be between 0 and 1, where:
|
|
60
|
-
* - 0 = no padding
|
|
61
|
-
* - 1 = 100% padding (not recommended)
|
|
62
|
-
* Default is 0.01 (1%) if not specified.
|
|
63
|
-
*/
|
|
64
|
-
paddingRatio?: number;
|
|
65
60
|
/**
|
|
66
61
|
* Whether to show the highlight overlay for detected business cards.
|
|
67
62
|
* Defaults to true.
|
|
68
63
|
*/
|
|
69
64
|
showHighlight?: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* Minimum interval in seconds between consecutive 'rectangleDetected' events.
|
|
67
|
+
* After an event is emitted, no new events will be emitted until this interval passes.
|
|
68
|
+
* Set to 0 to emit events as soon as stability is detected.
|
|
69
|
+
* Defaults to 0 (no throttling beyond initial stability check).
|
|
70
|
+
*/
|
|
71
|
+
emitIntervalSeconds?: number;
|
|
70
72
|
};
|
|
71
73
|
export declare type StartOptions = {
|
|
72
74
|
/**
|
|
@@ -97,6 +99,12 @@ export declare type StartOptions = {
|
|
|
97
99
|
* Only applies when objectDetection is true.
|
|
98
100
|
*/
|
|
99
101
|
objectHighlight?: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Minimum interval in seconds between consecutive 'rectangleDetected' events.
|
|
104
|
+
* Only applies when objectDetection is true.
|
|
105
|
+
* Defaults to 0 (no throttling beyond initial stability check).
|
|
106
|
+
*/
|
|
107
|
+
rectangleEmitIntervalSeconds?: number;
|
|
100
108
|
debounceTimeInMilli?: number;
|
|
101
109
|
/**
|
|
102
110
|
* Optional regex pattern to filter scanned barcodes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAmKA,MAAM,CAAN,IAAY,aAYX;AAZD,WAAY,aAAa;IACvB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,qCAAoB,CAAA;IACpB,2CAA0B,CAAA;IAC1B,+BAAc,CAAA;IACd,iCAAgB,CAAA;IAChB,gCAAe,CAAA;IACf,mCAAkB,CAAA;IAClB,mCAAkB,CAAA;IAClB,+BAAc,CAAA;AAChB,CAAC,EAZW,aAAa,KAAb,aAAa,QAYxB","sourcesContent":["export interface CapacitorScannerPlugin {\n /**\n * Start the camera preview with optional barcode and object detection.\n * @param options - Configuration options for the camera and detection\n */\n start(options?: StartOptions): Promise<void>;\n\n /**\n * Stop the camera preview and all detection.\n */\n stop(): Promise<void>;\n\n openSettings(): Promise<void>;\n\n capturePhoto(options?: CapturePhotoOptions): Promise<CapturePhotoResult>;\n\n checkPermissions(): Promise<PermissionsResult>;\n\n requestPermissions(): Promise<PermissionsResult>;\n\n flipCamera(): Promise<void>;\n\n toggleFlash(): Promise<FlashResult>;\n\n addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;\n\n addListener(event: 'rectangleDetected', listenerFunc: (result: RectangleDetectedEvent) => void): Promise<void>;\n\n removeAllListeners(): Promise<void>;\n\n /**\n * Enable object detection (business card). This will start detecting business cards.\n * @param options - Optional configuration for object detection\n */\n enableObjectDetection(options?: ObjectDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable object detection. This will stop detecting business cards.\n */\n disableObjectDetection(): Promise<{ enabled: false }>;\n\n /**\n * Enable barcode detection. This will start detecting barcodes.\n * @param options - Optional configuration for barcode detection\n */\n enableBarcodeDetection(options?: BarcodeDetectionOptions): Promise<{ enabled: true }>;\n\n /**\n * Disable barcode detection. This will stop detecting barcodes and clear cached barcodes.\n */\n disableBarcodeDetection(): Promise<{ enabled: false }>;\n\n /**\n * Set the camera zoom level.\n * @param options - The zoom options containing the level (1, 2, or 3)\n */\n zoom(options: ZoomOptions): Promise<ZoomResult>;\n}\n\nexport type BarcodeDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected barcodes.\n * Defaults to false.\n */\n showHighlight?: boolean;\n}\n\nexport type ObjectDetectionOptions = {\n /**\n * Whether to show the highlight overlay for detected business cards.\n * Defaults to true.\n */\n showHighlight?: boolean,\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * After an event is emitted, no new events will be emitted until this interval passes.\n * Set to 0 to emit events as soon as stability is detected.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n emitIntervalSeconds?: number\n}\n\nexport type StartOptions = {\n /**\n * Barcode formats to detect when barcodeDetection is enabled.\n */\n formats?: BarcodeFormat[];\n /**\n * Camera direction to use. Defaults to 'BACK'.\n */\n cameraDirection?: 'BACK' | 'FRONT';\n /**\n * Enable barcode detection on start. Defaults to false.\n * When enabled, the camera will automatically detect barcodes and emit 'barcodeScanned' events.\n */\n barcodeDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected barcodes. Defaults to false.\n * Only applies when barcodeDetection is true.\n */\n barcodeHighlight?: boolean;\n /**\n * Enable object detection (business card) on start. Defaults to false.\n * When enabled, the camera will automatically detect business cards and emit 'rectangleDetected' events.\n */\n objectDetection?: boolean;\n /**\n * Whether to show the highlight overlay for detected business cards. Defaults to true.\n * Only applies when objectDetection is true.\n */\n objectHighlight?: boolean;\n /**\n * Minimum interval in seconds between consecutive 'rectangleDetected' events.\n * Only applies when objectDetection is true.\n * Defaults to 0 (no throttling beyond initial stability check).\n */\n rectangleEmitIntervalSeconds?: number;\n debounceTimeInMilli?: number;\n /**\n * Optional regex pattern to filter scanned barcodes.\n * If provided, only barcodes matching this pattern will be reported.\n */\n regex?: string;\n /**\n * Optional regex flags (e.g., 'i' for case-insensitive, 'm' for multiline).\n */\n regexFlags?: string;\n};\n\nexport type BarcodeScannedEvent = { scannedCode: string; format: string };\n\n\nexport type RectangleDetectedEvent = {\n detected: true\n};\n\nexport type CapturePhotoOptions = {\n /**\n * The desired quality of the captured image, expressed as a value between 0.0 (lowest quality, smallest file size)\n * and 1.0 (highest quality, largest file size). Defaults to 1.0.\n * This parameter directly influences the compression level of the resulting JPEG image.\n */\n qualityRatio?: number;\n};\n\nexport type PermissionsResult = { camera: 'prompt' | 'denied' | 'granted' };\n\nexport type CapturePhotoResult = { imageBase64: string };\n\nexport type FlashResult = { enabled: boolean };\n\nexport type ZoomOptions = {\n /**\n * The zoom level to set. Must be 1, 2, or 3.\n * - 1 = 1.0x (no zoom)\n * - 2 = 2.0x\n * - 3 = 3.0x\n */\n level: 1 | 2 | 3;\n};\n\nexport type ZoomResult = { level: number };\n\nexport enum BarcodeFormat {\n Aztec = 'AZTEC',\n Code39 = 'CODE_39',\n Code93 = 'CODE_93',\n Code128 = 'CODE_128',\n DataMatrix = 'DATA_MATRIX',\n Ean8 = 'EAN_8',\n Ean13 = 'EAN_13',\n Itf14 = 'ITF14',\n Pdf417 = 'PDF_417',\n QrCode = 'QR_CODE',\n UpcE = 'UPC_E',\n}\n\ndeclare global {\n interface PluginRegistry {\n QRScanner: CapacitorScannerPlugin;\n }\n}"]}
|
|
@@ -76,20 +76,16 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
76
76
|
}()
|
|
77
77
|
|
|
78
78
|
@objc func enableObjectDetection(_ call: CAPPluginCall) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Only accept "businessCard" - barcode detection is handled separately
|
|
85
|
-
let filteredTypes = types.filter { $0 == "businessCard" }
|
|
86
|
-
|
|
87
|
-
if filteredTypes.isEmpty {
|
|
88
|
-
call.reject("No valid detection types provided. Only 'businessCard' is supported. Use enableBarcodeDetection() for barcode detection.")
|
|
79
|
+
// If object detection is already enabled, just return success
|
|
80
|
+
if self.enabledDetectionTypes.contains("businessCard") {
|
|
81
|
+
call.resolve([
|
|
82
|
+
"enabled": true,
|
|
83
|
+
])
|
|
89
84
|
return
|
|
90
85
|
}
|
|
91
86
|
|
|
92
87
|
let showHighlight = call.getBool("showHighlight", true)
|
|
88
|
+
let emitIntervalSeconds = call.getDouble("emitIntervalSeconds") ?? 0
|
|
93
89
|
|
|
94
90
|
DispatchQueue.main.async {
|
|
95
91
|
self.enabledDetectionTypes.insert("businessCard")
|
|
@@ -100,15 +96,17 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
100
96
|
self.rectangleDetector?.delegate = self
|
|
101
97
|
}
|
|
102
98
|
|
|
99
|
+
// Set the emit interval
|
|
100
|
+
self.rectangleDetector?.setEmitInterval(emitIntervalSeconds)
|
|
101
|
+
|
|
103
102
|
if showHighlight {
|
|
104
103
|
self.enabledHighlightTypes.insert("businessCard")
|
|
104
|
+
self.rectangleDetector?.isHighlightEnabled = true
|
|
105
105
|
self.setupOverlayView()
|
|
106
|
-
self.rectangleDetector?.enableHighlight()
|
|
107
106
|
}
|
|
108
107
|
|
|
109
108
|
call.resolve([
|
|
110
109
|
"enabled": true,
|
|
111
|
-
"types": filteredTypes,
|
|
112
110
|
])
|
|
113
111
|
}
|
|
114
112
|
}
|
|
@@ -124,14 +122,17 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
124
122
|
|
|
125
123
|
call.resolve([
|
|
126
124
|
"enabled": false,
|
|
127
|
-
"types": [],
|
|
128
125
|
])
|
|
129
126
|
}
|
|
130
127
|
}
|
|
131
128
|
|
|
132
129
|
private func setupOverlayView() {
|
|
133
130
|
guard let cameraView = self.cameraView,
|
|
134
|
-
let previewLayer = self.previewLayer else {
|
|
131
|
+
let previewLayer = self.previewLayer else {
|
|
132
|
+
print("[CapacitorScanner] setupOverlayView: cameraView or previewLayer is nil")
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
print("[CapacitorScanner] setupOverlayView: setting up overlay")
|
|
135
136
|
|
|
136
137
|
// Remove existing overlay if present
|
|
137
138
|
self.overlayView?.removeFromSuperview()
|
|
@@ -432,6 +433,9 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
432
433
|
if enableObjectHighlight {
|
|
433
434
|
self.rectangleDetector?.isHighlightEnabled = true
|
|
434
435
|
}
|
|
436
|
+
// Set emit interval if provided
|
|
437
|
+
let emitInterval = call.getDouble("rectangleEmitIntervalSeconds") ?? 0
|
|
438
|
+
self.rectangleDetector?.setEmitInterval(emitInterval)
|
|
435
439
|
}
|
|
436
440
|
|
|
437
441
|
// Handle Regex Filter
|
|
@@ -33,6 +33,9 @@ class RectangleDetector {
|
|
|
33
33
|
private let stabilityThreshold: CGFloat = 0.2 // Max movement (normalized) to be considered stable
|
|
34
34
|
private let stabilityDuration: TimeInterval = 0.8 // Seconds of stability before emitting event
|
|
35
35
|
|
|
36
|
+
/// Minimum interval between consecutive event emissions. Set via `setEmitInterval(_:)`.
|
|
37
|
+
private var emitIntervalSeconds: TimeInterval = 0
|
|
38
|
+
|
|
36
39
|
// MARK: - State
|
|
37
40
|
|
|
38
41
|
private(set) var lastDetectedRectangle: DetectedRectangle?
|
|
@@ -42,7 +45,7 @@ class RectangleDetector {
|
|
|
42
45
|
// Stability tracking
|
|
43
46
|
private var previousCorners: RectangleCorners?
|
|
44
47
|
private var stabilityStartTime: Date?
|
|
45
|
-
private var
|
|
48
|
+
private var lastEmitTime: Date?
|
|
46
49
|
|
|
47
50
|
private var smoother: RectangleSmoother?
|
|
48
51
|
private weak var overlayParentLayer: CALayer?
|
|
@@ -70,6 +73,7 @@ class RectangleDetector {
|
|
|
70
73
|
|
|
71
74
|
/// Configure the detector with overlay support
|
|
72
75
|
func configure(overlayLayer: CALayer, previewLayer: AVCaptureVideoPreviewLayer) {
|
|
76
|
+
print("[RectangleDetector] configure called, isHighlightEnabled: \(isHighlightEnabled)")
|
|
73
77
|
self.overlayParentLayer = overlayLayer
|
|
74
78
|
self.previewLayer = previewLayer
|
|
75
79
|
|
|
@@ -91,6 +95,11 @@ class RectangleDetector {
|
|
|
91
95
|
smoother = nil
|
|
92
96
|
}
|
|
93
97
|
|
|
98
|
+
/// Set the minimum interval between consecutive event emissions
|
|
99
|
+
func setEmitInterval(_ seconds: TimeInterval) {
|
|
100
|
+
emitIntervalSeconds = max(0, seconds)
|
|
101
|
+
}
|
|
102
|
+
|
|
94
103
|
/// Update the current pixel buffer for snapshot capture
|
|
95
104
|
func updatePixelBuffer(_ pixelBuffer: CVPixelBuffer) {
|
|
96
105
|
self.currentPixelBuffer = pixelBuffer
|
|
@@ -114,7 +123,7 @@ class RectangleDetector {
|
|
|
114
123
|
currentPixelBuffer = nil
|
|
115
124
|
previousCorners = nil
|
|
116
125
|
stabilityStartTime = nil
|
|
117
|
-
|
|
126
|
+
lastEmitTime = nil
|
|
118
127
|
smoother?.clear()
|
|
119
128
|
smoother = nil
|
|
120
129
|
}
|
|
@@ -122,7 +131,11 @@ class RectangleDetector {
|
|
|
122
131
|
// MARK: - Private Implementation
|
|
123
132
|
|
|
124
133
|
private func setupSmoother() {
|
|
125
|
-
guard let parentLayer = overlayParentLayer, smoother == nil else {
|
|
134
|
+
guard let parentLayer = overlayParentLayer, smoother == nil else {
|
|
135
|
+
print("[RectangleDetector] setupSmoother: skipped (parentLayer nil: \(overlayParentLayer == nil), smoother exists: \(smoother != nil))")
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
print("[RectangleDetector] setupSmoother: creating smoother")
|
|
126
139
|
smoother = RectangleSmoother()
|
|
127
140
|
smoother?.setup(in: parentLayer)
|
|
128
141
|
}
|
|
@@ -191,14 +204,13 @@ class RectangleDetector {
|
|
|
191
204
|
// MARK: - Stability Tracking
|
|
192
205
|
|
|
193
206
|
private func updateStability(with corners: RectangleCorners) {
|
|
194
|
-
print("[RectangleDetector] updateStability - hasPrevious: \(previousCorners != nil), timerActive: \(stabilityStartTime != nil)
|
|
207
|
+
print("[RectangleDetector] updateStability - hasPrevious: \(previousCorners != nil), timerActive: \(stabilityStartTime != nil)")
|
|
195
208
|
|
|
196
209
|
// First detection - just store corners, don't start timer yet
|
|
197
210
|
guard let previous = previousCorners else {
|
|
198
211
|
print("[RectangleDetector] First detection - storing corners, no timer yet")
|
|
199
212
|
previousCorners = corners
|
|
200
213
|
stabilityStartTime = nil
|
|
201
|
-
hasEmittedForCurrentStability = false
|
|
202
214
|
return
|
|
203
215
|
}
|
|
204
216
|
|
|
@@ -213,7 +225,6 @@ class RectangleDetector {
|
|
|
213
225
|
if movement >= stabilityThreshold {
|
|
214
226
|
print("[RectangleDetector] Too much movement (\(movement) >= \(stabilityThreshold)) - resetting timer")
|
|
215
227
|
stabilityStartTime = nil
|
|
216
|
-
hasEmittedForCurrentStability = false
|
|
217
228
|
return
|
|
218
229
|
}
|
|
219
230
|
|
|
@@ -231,9 +242,22 @@ class RectangleDetector {
|
|
|
231
242
|
let elapsed = now.timeIntervalSince(stabilityStartTime!)
|
|
232
243
|
print("[RectangleDetector] Stability elapsed: \(String(format: "%.2f", elapsed))s / \(stabilityDuration)s required")
|
|
233
244
|
|
|
234
|
-
|
|
245
|
+
// Need to be stable for the required duration before any emission
|
|
246
|
+
guard elapsed >= stabilityDuration else { return }
|
|
247
|
+
|
|
248
|
+
// Check if we can emit (either first time, or interval has passed)
|
|
249
|
+
let canEmit: Bool
|
|
250
|
+
if let lastEmit = lastEmitTime {
|
|
251
|
+
let timeSinceLastEmit = now.timeIntervalSince(lastEmit)
|
|
252
|
+
canEmit = timeSinceLastEmit >= emitIntervalSeconds
|
|
253
|
+
print("[RectangleDetector] Time since last emit: \(String(format: "%.2f", timeSinceLastEmit))s, interval: \(emitIntervalSeconds)s")
|
|
254
|
+
} else {
|
|
255
|
+
canEmit = true
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if canEmit {
|
|
235
259
|
print("[RectangleDetector] STABLE for \(elapsed)s - emitting event")
|
|
236
|
-
|
|
260
|
+
lastEmitTime = now
|
|
237
261
|
delegate?.rectangleDetector(self, didDetect: corners)
|
|
238
262
|
}
|
|
239
263
|
}
|
|
@@ -244,7 +268,6 @@ class RectangleDetector {
|
|
|
244
268
|
print("[RectangleDetector] No detection - resetting stability timer")
|
|
245
269
|
}
|
|
246
270
|
stabilityStartTime = nil
|
|
247
|
-
hasEmittedForCurrentStability = false
|
|
248
271
|
previousCorners = nil
|
|
249
272
|
}
|
|
250
273
|
|