@scr2em/capacitor-scanner 6.0.31 → 6.0.34
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 +1 -1
- package/android/src/main/java/com/leadliaion/capacitorscanner/CapacitorScannerPlugin.java +47 -38
- package/dist/docs.json +1 -1
- package/dist/esm/definitions.d.ts +4 -3
- package/dist/esm/definitions.js.map +1 -1
- package/ios/Sources/CapacitorScannerPlugin/CapacitorScannerPlugin.swift +63 -64
- package/ios/Sources/CapacitorScannerPlugin/RectangleDetector.swift +19 -60
- package/package.json +16 -18
package/README.md
CHANGED
|
@@ -262,7 +262,7 @@ Set the camera zoom level.
|
|
|
262
262
|
|
|
263
263
|
#### StartOptions
|
|
264
264
|
|
|
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; /** * 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; /** *
|
|
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; /** * 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; /** * Cooldown in seconds after any detection event when both barcode and object detection are enabled. * After a barcode or rectangle is emitted, ALL detection pauses for this duration. * When set, barcode dedup TTL becomes 2x this value and rectangle emit interval becomes 2x this value. * Only applies when both barcodeDetection and objectDetection are true. * Defaults to 0 (no throttle, both detections run simultaneously). */ detectionThrottleSeconds?: number; }</code>
|
|
266
266
|
|
|
267
267
|
|
|
268
268
|
#### CapturePhotoOptions
|
|
@@ -92,7 +92,7 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
92
92
|
private ProcessCameraProvider cameraProvider;
|
|
93
93
|
private BarcodeScanner scanner;
|
|
94
94
|
private RectangleDetector rectangleDetector;
|
|
95
|
-
private
|
|
95
|
+
private TTLMap<String, VoteStatus> scannedCodesVotes = new TTLMap<>(5000); // 5 second TTL
|
|
96
96
|
private final int voteThreshold = 2;
|
|
97
97
|
private final Executor executor = Executors.newSingleThreadExecutor();
|
|
98
98
|
private final AtomicBoolean isScanning = new AtomicBoolean(false);
|
|
@@ -143,9 +143,10 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
143
143
|
// Configurable interval between rectangle detection event emissions (in milliseconds)
|
|
144
144
|
private long rectangleEmitIntervalMs = 0; // Default: 0 (no throttling)
|
|
145
145
|
|
|
146
|
-
// Detection throttle: pause
|
|
146
|
+
// Detection throttle: pause ALL detection after any emit (when both types enabled)
|
|
147
147
|
private long detectionThrottleMs = 0; // Default: 0 (no throttle)
|
|
148
|
-
private long
|
|
148
|
+
private long lastDetectionEmitTime = 0;
|
|
149
|
+
private boolean bothDetectionsEnabled = false;
|
|
149
150
|
|
|
150
151
|
/**
|
|
151
152
|
* Calculates the processing interval in milliseconds based on the desired
|
|
@@ -213,39 +214,48 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
|
|
217
|
+
// Read detection throttle
|
|
218
|
+
if (call.getData().has("detectionThrottleSeconds")) {
|
|
219
|
+
try {
|
|
220
|
+
double throttleSeconds = call.getData().getDouble("detectionThrottleSeconds");
|
|
221
|
+
detectionThrottleMs = Math.max(0, (long) (throttleSeconds * 1000));
|
|
222
|
+
} catch (Exception e) {
|
|
223
|
+
detectionThrottleMs = 0;
|
|
224
|
+
}
|
|
225
|
+
} else {
|
|
226
|
+
detectionThrottleMs = 0;
|
|
227
|
+
}
|
|
228
|
+
lastDetectionEmitTime = 0;
|
|
229
|
+
bothDetectionsEnabled = enableBarcodeDetection && enableObjectDetection;
|
|
230
|
+
|
|
216
231
|
// Enable object detection (businessCard) if requested
|
|
217
232
|
if (enableObjectDetection) {
|
|
218
233
|
enabledDetectionTypes.add("businessCard");
|
|
219
234
|
if (enableObjectHighlight) {
|
|
220
235
|
enabledHighlightTypes.add("businessCard");
|
|
221
236
|
}
|
|
222
|
-
// Read rectangleEmitIntervalSeconds
|
|
237
|
+
// Read rectangleEmitIntervalSeconds: use 2X when both enabled with throttle
|
|
238
|
+
long emitInterval = 0;
|
|
223
239
|
if (call.getData().has("rectangleEmitIntervalSeconds")) {
|
|
224
240
|
try {
|
|
225
241
|
double intervalSeconds = call.getData().getDouble("rectangleEmitIntervalSeconds");
|
|
226
|
-
|
|
227
|
-
echo("Using custom rectangleEmitIntervalSeconds: " + intervalSeconds + "s (" + rectangleEmitIntervalMs + "ms)");
|
|
242
|
+
emitInterval = Math.max(0, (long) (intervalSeconds * 1000));
|
|
228
243
|
} catch (Exception e) {
|
|
229
|
-
|
|
230
|
-
rectangleEmitIntervalMs = 0;
|
|
244
|
+
emitInterval = 0;
|
|
231
245
|
}
|
|
232
|
-
} else {
|
|
233
|
-
rectangleEmitIntervalMs = 0;
|
|
234
246
|
}
|
|
247
|
+
if (bothDetectionsEnabled && detectionThrottleMs > 0) {
|
|
248
|
+
emitInterval = Math.max(emitInterval, detectionThrottleMs * 2);
|
|
249
|
+
}
|
|
250
|
+
rectangleEmitIntervalMs = emitInterval;
|
|
235
251
|
}
|
|
236
252
|
|
|
237
|
-
//
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
double throttleSeconds = call.getData().getDouble("detectionThrottleSeconds");
|
|
241
|
-
detectionThrottleMs = Math.max(0, (long) (throttleSeconds * 1000));
|
|
242
|
-
} catch (Exception e) {
|
|
243
|
-
detectionThrottleMs = 0;
|
|
244
|
-
}
|
|
253
|
+
// When both enabled with throttle, set barcode votes TTL to 2X
|
|
254
|
+
if (bothDetectionsEnabled && detectionThrottleMs > 0) {
|
|
255
|
+
scannedCodesVotes = new TTLMap<>(detectionThrottleMs * 2);
|
|
245
256
|
} else {
|
|
246
|
-
|
|
257
|
+
scannedCodesVotes = new TTLMap<>(5000);
|
|
247
258
|
}
|
|
248
|
-
lastSuccessfulBarcodeScanTime = 0;
|
|
249
259
|
|
|
250
260
|
try {
|
|
251
261
|
String cameraDirectionStr = call.getString("cameraDirection", "BACK");
|
|
@@ -481,23 +491,18 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
481
491
|
try {
|
|
482
492
|
InputImage image = InputImage.fromMediaImage(mediaImage, rotationDegrees);
|
|
483
493
|
|
|
484
|
-
|
|
485
|
-
boolean
|
|
486
|
-
&&
|
|
487
|
-
&&
|
|
494
|
+
// Check if detection is throttled (cooldown after any emit when both types enabled)
|
|
495
|
+
boolean isThrottled = bothDetectionsEnabled
|
|
496
|
+
&& detectionThrottleMs > 0
|
|
497
|
+
&& lastDetectionEmitTime > 0
|
|
498
|
+
&& (System.currentTimeMillis() - lastDetectionEmitTime) < detectionThrottleMs;
|
|
499
|
+
|
|
500
|
+
boolean barcodeEnabled = scanner != null
|
|
501
|
+
&& enabledDetectionTypes.contains("barcode")
|
|
502
|
+
&& !isThrottled;
|
|
488
503
|
boolean rectangleEnabled = rectangleDetector != null
|
|
489
504
|
&& enabledDetectionTypes.contains("businessCard")
|
|
490
|
-
&& !
|
|
491
|
-
|
|
492
|
-
// Clear rectangle overlay when throttled
|
|
493
|
-
if (isRectangleThrottled && enabledDetectionTypes.contains("businessCard")) {
|
|
494
|
-
getActivity().runOnUiThread(() -> {
|
|
495
|
-
synchronized (overlayLock) {
|
|
496
|
-
currentRectangleRects.clear();
|
|
497
|
-
updateOverlayDetections(currentBarcodeRects, currentRectangleRects);
|
|
498
|
-
}
|
|
499
|
-
});
|
|
500
|
-
}
|
|
505
|
+
&& !isThrottled;
|
|
501
506
|
|
|
502
507
|
// Rectangle detection (synchronous) - runs first while imageProxy is still open
|
|
503
508
|
if (rectangleEnabled) {
|
|
@@ -909,8 +914,8 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
909
914
|
data.put("format", BarcodeFormatHelper.barcodeFormatToString(format));
|
|
910
915
|
notifyListeners("barcodeScanned", data, true);
|
|
911
916
|
|
|
912
|
-
// Record
|
|
913
|
-
|
|
917
|
+
// Record emit time for detection throttle
|
|
918
|
+
lastDetectionEmitTime = System.currentTimeMillis();
|
|
914
919
|
|
|
915
920
|
echo("Barcode " + rawValue + " scanned with format: "
|
|
916
921
|
+ BarcodeFormatHelper.barcodeFormatToString(format));
|
|
@@ -949,6 +954,8 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
949
954
|
|
|
950
955
|
// Update the last notification timestamp
|
|
951
956
|
lastRectangleNotificationTime = currentTime;
|
|
957
|
+
// Record emit time for detection throttle
|
|
958
|
+
lastDetectionEmitTime = currentTime;
|
|
952
959
|
} else {
|
|
953
960
|
echo("Skipping notification - last one was less than 1 second ago");
|
|
954
961
|
}
|
|
@@ -1187,7 +1194,9 @@ public class CapacitorScannerPlugin extends Plugin {
|
|
|
1187
1194
|
lastRectangleDetectionTime = 0;
|
|
1188
1195
|
rectangleEmitIntervalMs = 0;
|
|
1189
1196
|
detectionThrottleMs = 0;
|
|
1190
|
-
|
|
1197
|
+
lastDetectionEmitTime = 0;
|
|
1198
|
+
bothDetectionsEnabled = false;
|
|
1199
|
+
scannedCodesVotes = new TTLMap<>(5000);
|
|
1191
1200
|
|
|
1192
1201
|
// Clear detection histories and caches
|
|
1193
1202
|
enabledDetectionTypes.clear();
|
package/dist/docs.json
CHANGED
|
@@ -344,7 +344,7 @@
|
|
|
344
344
|
"docs": "",
|
|
345
345
|
"types": [
|
|
346
346
|
{
|
|
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 /**\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 *
|
|
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 /**\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 * Cooldown in seconds after any detection event when both barcode and object detection are enabled.\n * After a barcode or rectangle is emitted, ALL detection pauses for this duration.\n * When set, barcode dedup TTL becomes 2x this value and rectangle emit interval becomes 2x this value.\n * Only applies when both barcodeDetection and objectDetection are true.\n * Defaults to 0 (no throttle, both detections run simultaneously).\n */\n detectionThrottleSeconds?: number;\n}",
|
|
348
348
|
"complexTypes": [
|
|
349
349
|
"BarcodeFormat"
|
|
350
350
|
]
|
|
@@ -115,9 +115,10 @@ export declare type StartOptions = {
|
|
|
115
115
|
*/
|
|
116
116
|
regexFlags?: string;
|
|
117
117
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
118
|
+
* Cooldown in seconds after any detection event when both barcode and object detection are enabled.
|
|
119
|
+
* After a barcode or rectangle is emitted, ALL detection pauses for this duration.
|
|
120
|
+
* When set, barcode dedup TTL becomes 2x this value and rectangle emit interval becomes 2x this value.
|
|
121
|
+
* Only applies when both barcodeDetection and objectDetection are true.
|
|
121
122
|
* Defaults to 0 (no throttle, both detections run simultaneously).
|
|
122
123
|
*/
|
|
123
124
|
detectionThrottleSeconds?: number;
|
|
@@ -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":"AA0KA,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 /**\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 * Cooldown in seconds after any detection event when both barcode and object detection are enabled.\n * After a barcode or rectangle is emitted, ALL detection pauses for this duration.\n * When set, barcode dedup TTL becomes 2x this value and rectangle emit interval becomes 2x this value.\n * Only applies when both barcodeDetection and objectDetection are true.\n * Defaults to 0 (no throttle, both detections run simultaneously).\n */\n detectionThrottleSeconds?: number;\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}"]}
|
|
@@ -51,9 +51,10 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
51
51
|
private var lastBarcodeDetectionTime: Date?
|
|
52
52
|
private let barcodeVisibilityTimeout: TimeInterval = 1.0
|
|
53
53
|
|
|
54
|
-
// Detection throttle: pause
|
|
54
|
+
// Detection throttle: pause ALL detection after any emit (when both types enabled)
|
|
55
55
|
private var detectionThrottleSeconds: TimeInterval = 0
|
|
56
|
-
private var
|
|
56
|
+
private var lastDetectionEmitTime: Date?
|
|
57
|
+
private var bothDetectionsEnabled: Bool = false
|
|
57
58
|
|
|
58
59
|
// Detection state (controls what detection runs)
|
|
59
60
|
private var enabledDetectionTypes: Set<String> = []
|
|
@@ -245,31 +246,24 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
245
246
|
AVVideoCompressionPropertiesKey: [AVVideoQualityKey: qualityNumber]])
|
|
246
247
|
|
|
247
248
|
let maxPrioritization = photoOutput.maxPhotoQualityPrioritization
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
249
|
+
if maxPrioritization.rawValue >= AVCapturePhotoOutput.QualityPrioritization.quality.rawValue {
|
|
250
|
+
photoSettings.photoQualityPrioritization = .quality
|
|
251
|
+
} else if maxPrioritization.rawValue >= AVCapturePhotoOutput.QualityPrioritization.balanced.rawValue {
|
|
251
252
|
photoSettings.photoQualityPrioritization = .balanced
|
|
252
253
|
} else {
|
|
253
|
-
// Use the lowest prioritization (.speed)
|
|
254
254
|
photoSettings.photoQualityPrioritization = .speed
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
// Enable high-resolution capture for sharper photos
|
|
258
|
+
photoSettings.isHighResolutionPhotoEnabled = true
|
|
259
|
+
|
|
257
260
|
} else {
|
|
258
261
|
print("JPEG codec not supported for photo capture. Using default settings.")
|
|
259
262
|
// photoSettings remains the default initialized one
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
self.capturePhotoCall = call
|
|
263
|
-
|
|
264
|
-
// If business card detection is enabled, wait for 1 second before capturing
|
|
265
|
-
if self.enabledDetectionTypes.contains("businessCard") {
|
|
266
|
-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
|
|
267
|
-
photoOutput.capturePhoto(with: photoSettings, delegate: self)
|
|
268
|
-
}
|
|
269
|
-
} else {
|
|
270
|
-
// Otherwise capture immediately
|
|
271
|
-
photoOutput.capturePhoto(with: photoSettings, delegate: self)
|
|
272
|
-
}
|
|
266
|
+
photoOutput.capturePhoto(with: photoSettings, delegate: self)
|
|
273
267
|
}
|
|
274
268
|
|
|
275
269
|
public func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
|
|
@@ -296,37 +290,22 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
296
290
|
if self.enabledDetectionTypes.contains("businessCard"),
|
|
297
291
|
isRectangleCurrentlyDetected,
|
|
298
292
|
let detectedRect = self.rectangleDetector?.lastDetectedRectangle {
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
return
|
|
305
|
-
}
|
|
306
|
-
let base64String = croppedImageData.base64EncodedString()
|
|
307
|
-
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
308
|
-
} else {
|
|
309
|
-
let base64String = imageData.base64EncodedString()
|
|
310
|
-
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
311
|
-
}
|
|
312
|
-
} else {
|
|
313
|
-
// Use the captured photo if no snapshot available
|
|
314
|
-
guard let image = UIImage(data: imageData) else {
|
|
315
|
-
self.capturePhotoCall?.reject("Unable to create image from data")
|
|
316
|
-
return
|
|
317
|
-
}
|
|
293
|
+
// Always use the high-resolution captured photo for cropping (not the video frame snapshot)
|
|
294
|
+
guard let image = UIImage(data: imageData) else {
|
|
295
|
+
self.capturePhotoCall?.reject("Unable to create image from data")
|
|
296
|
+
return
|
|
297
|
+
}
|
|
318
298
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
}
|
|
324
|
-
let base64String = croppedImageData.base64EncodedString()
|
|
325
|
-
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
326
|
-
} else {
|
|
327
|
-
let base64String = imageData.base64EncodedString()
|
|
328
|
-
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
299
|
+
if let croppedImage = self.cropImage(image, toRectangle: detectedRect) {
|
|
300
|
+
guard let croppedImageData = croppedImage.jpegData(compressionQuality: 0.9) else {
|
|
301
|
+
self.capturePhotoCall?.reject("Unable to convert cropped image to data")
|
|
302
|
+
return
|
|
329
303
|
}
|
|
304
|
+
let base64String = croppedImageData.base64EncodedString()
|
|
305
|
+
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
306
|
+
} else {
|
|
307
|
+
let base64String = imageData.base64EncodedString()
|
|
308
|
+
self.capturePhotoCall?.resolve(["imageBase64": "data:image/jpeg;base64, \(base64String)"])
|
|
330
309
|
}
|
|
331
310
|
} else {
|
|
332
311
|
// No rectangle detected, return original image
|
|
@@ -451,6 +430,11 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
451
430
|
}
|
|
452
431
|
}
|
|
453
432
|
|
|
433
|
+
// Read detection throttle
|
|
434
|
+
self.detectionThrottleSeconds = call.getDouble("detectionThrottleSeconds") ?? 0
|
|
435
|
+
self.lastDetectionEmitTime = nil
|
|
436
|
+
self.bothDetectionsEnabled = enableBarcodeDetection && enableObjectDetection
|
|
437
|
+
|
|
454
438
|
// Enable object detection (businessCard) if requested
|
|
455
439
|
if enableObjectDetection {
|
|
456
440
|
self.enabledDetectionTypes.insert("businessCard")
|
|
@@ -463,14 +447,18 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
463
447
|
if enableObjectHighlight {
|
|
464
448
|
self.rectangleDetector?.isHighlightEnabled = true
|
|
465
449
|
}
|
|
466
|
-
// Set emit interval
|
|
467
|
-
|
|
450
|
+
// Set emit interval: use 2X when both enabled with throttle, otherwise use provided value
|
|
451
|
+
var emitInterval = call.getDouble("rectangleEmitIntervalSeconds") ?? 0
|
|
452
|
+
if self.bothDetectionsEnabled && self.detectionThrottleSeconds > 0 {
|
|
453
|
+
emitInterval = max(emitInterval, self.detectionThrottleSeconds * 2)
|
|
454
|
+
}
|
|
468
455
|
self.rectangleDetector?.setEmitInterval(emitInterval)
|
|
469
456
|
}
|
|
470
457
|
|
|
471
|
-
//
|
|
472
|
-
self.
|
|
473
|
-
|
|
458
|
+
// When both enabled with throttle, set barcode votes TTL to 2X
|
|
459
|
+
if self.bothDetectionsEnabled && self.detectionThrottleSeconds > 0 {
|
|
460
|
+
self.scannedCodesVotes = TTLMap<String, VoteStatus>(ttlSeconds: self.detectionThrottleSeconds * 2)
|
|
461
|
+
}
|
|
474
462
|
|
|
475
463
|
// Handle Regex Filter
|
|
476
464
|
if let pattern = call.getString("regex") {
|
|
@@ -530,6 +518,8 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
530
518
|
self.photoOutput = photoOutput
|
|
531
519
|
if captureSession.canAddOutput(photoOutput) {
|
|
532
520
|
captureSession.addOutput(photoOutput)
|
|
521
|
+
photoOutput.maxPhotoQualityPrioritization = .quality
|
|
522
|
+
photoOutput.isHighResolutionCaptureEnabled = true
|
|
533
523
|
} else {
|
|
534
524
|
call.reject("Unable to add photo output to capture session")
|
|
535
525
|
return
|
|
@@ -604,8 +594,10 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
604
594
|
self.enabledDetectionTypes.removeAll()
|
|
605
595
|
self.enabledHighlightTypes.removeAll()
|
|
606
596
|
self.lastBarcodeDetectionTime = nil
|
|
607
|
-
self.
|
|
597
|
+
self.lastDetectionEmitTime = nil
|
|
608
598
|
self.detectionThrottleSeconds = 0
|
|
599
|
+
self.bothDetectionsEnabled = false
|
|
600
|
+
self.scannedCodesVotes = TTLMap<String, VoteStatus>(ttlSeconds: 5)
|
|
609
601
|
self.removeOrientationChangeObserver()
|
|
610
602
|
}
|
|
611
603
|
|
|
@@ -821,11 +813,14 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
821
813
|
"format": CapacitorScannerHelpers.convertBarcodeScannerFormatToString(bestObservation.symbology),
|
|
822
814
|
])
|
|
823
815
|
|
|
824
|
-
// Record
|
|
825
|
-
self.
|
|
816
|
+
// Record emit time for detection throttle
|
|
817
|
+
self.lastDetectionEmitTime = Date()
|
|
826
818
|
|
|
827
|
-
//
|
|
828
|
-
|
|
819
|
+
// When both enabled: keep done flag for dedup (TTL handles expiry)
|
|
820
|
+
// When barcode only: clear to allow immediate re-voting
|
|
821
|
+
if !self.bothDetectionsEnabled {
|
|
822
|
+
self.scannedCodesVotes.clear()
|
|
823
|
+
}
|
|
829
824
|
}
|
|
830
825
|
}
|
|
831
826
|
|
|
@@ -842,6 +837,9 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
|
|
|
842
837
|
"bottomLeft": ["x": corners.bottomLeft.x, "y": corners.bottomLeft.y],
|
|
843
838
|
"bottomRight": ["x": corners.bottomRight.x, "y": corners.bottomRight.y],
|
|
844
839
|
])
|
|
840
|
+
|
|
841
|
+
// Record emit time for detection throttle
|
|
842
|
+
self.lastDetectionEmitTime = Date()
|
|
845
843
|
}
|
|
846
844
|
|
|
847
845
|
@objc func flipCamera(_ call: CAPPluginCall) {
|
|
@@ -1033,20 +1031,21 @@ extension CapacitorScannerPlugin: AVCaptureVideoDataOutputSampleBufferDelegate {
|
|
|
1033
1031
|
// Prepare detection requests
|
|
1034
1032
|
var requests: [VNRequest] = []
|
|
1035
1033
|
|
|
1036
|
-
if
|
|
1034
|
+
// Check if detection is throttled (cooldown after any emit when both types enabled)
|
|
1035
|
+
let isThrottled: Bool = {
|
|
1036
|
+
guard self.bothDetectionsEnabled,
|
|
1037
|
+
self.detectionThrottleSeconds > 0,
|
|
1038
|
+
let lastEmit = self.lastDetectionEmitTime else { return false }
|
|
1039
|
+
return Date().timeIntervalSince(lastEmit) < self.detectionThrottleSeconds
|
|
1040
|
+
}()
|
|
1041
|
+
|
|
1042
|
+
if !isThrottled, self.enabledDetectionTypes.contains("barcode") {
|
|
1037
1043
|
requests.append(self.barcodeDetectionRequest)
|
|
1038
1044
|
}
|
|
1039
1045
|
|
|
1040
|
-
|
|
1041
|
-
guard self.detectionThrottleSeconds > 0,
|
|
1042
|
-
let lastScan = self.lastSuccessfulBarcodeScanTime else { return false }
|
|
1043
|
-
return Date().timeIntervalSince(lastScan) < self.detectionThrottleSeconds
|
|
1044
|
-
}()
|
|
1045
|
-
|
|
1046
|
-
if !isRectangleThrottled,
|
|
1046
|
+
if !isThrottled,
|
|
1047
1047
|
self.enabledDetectionTypes.contains("businessCard"),
|
|
1048
1048
|
let detector = self.rectangleDetector {
|
|
1049
|
-
detector.updatePixelBuffer(pixelBuffer)
|
|
1050
1049
|
requests.append(detector.visionRequest)
|
|
1051
1050
|
}
|
|
1052
1051
|
|
|
@@ -18,14 +18,6 @@ struct RectangleCorners {
|
|
|
18
18
|
/// Handles rectangle/business card detection using Vision framework
|
|
19
19
|
class RectangleDetector {
|
|
20
20
|
|
|
21
|
-
// MARK: - Types
|
|
22
|
-
|
|
23
|
-
/// Stores detected rectangle with optional snapshot for cropping
|
|
24
|
-
struct DetectedRectangle {
|
|
25
|
-
let observation: VNRectangleObservation
|
|
26
|
-
let snapshot: UIImage?
|
|
27
|
-
}
|
|
28
|
-
|
|
29
21
|
// MARK: - Configuration
|
|
30
22
|
|
|
31
23
|
private let confidenceThreshold: Float = 0.8
|
|
@@ -38,10 +30,8 @@ class RectangleDetector {
|
|
|
38
30
|
|
|
39
31
|
// MARK: - State
|
|
40
32
|
|
|
41
|
-
private(set) var lastDetectedRectangle:
|
|
33
|
+
private(set) var lastDetectedRectangle: VNRectangleObservation?
|
|
42
34
|
private(set) var lastDetectionTime: Date?
|
|
43
|
-
private var currentPixelBuffer: CVPixelBuffer?
|
|
44
|
-
private let ciContext = CIContext(options: nil)
|
|
45
35
|
|
|
46
36
|
// Stability tracking
|
|
47
37
|
private var previousCorners: RectangleCorners?
|
|
@@ -61,7 +51,7 @@ class RectangleDetector {
|
|
|
61
51
|
let request = VNDetectRectanglesRequest { [weak self] request, error in
|
|
62
52
|
self?.processDetection(request, error: error)
|
|
63
53
|
}
|
|
64
|
-
request.maximumObservations =
|
|
54
|
+
request.maximumObservations = 10
|
|
65
55
|
request.minimumAspectRatio = 0.5
|
|
66
56
|
request.maximumAspectRatio = 0.8
|
|
67
57
|
request.minimumSize = 0.2
|
|
@@ -100,11 +90,6 @@ class RectangleDetector {
|
|
|
100
90
|
emitIntervalSeconds = max(0, seconds)
|
|
101
91
|
}
|
|
102
92
|
|
|
103
|
-
/// Update the current pixel buffer for snapshot capture
|
|
104
|
-
func updatePixelBuffer(_ pixelBuffer: CVPixelBuffer) {
|
|
105
|
-
self.currentPixelBuffer = pixelBuffer
|
|
106
|
-
}
|
|
107
|
-
|
|
108
93
|
/// Check for detection timeout and update overlay
|
|
109
94
|
func checkTimeout() {
|
|
110
95
|
smoother?.checkTimeout()
|
|
@@ -120,7 +105,6 @@ class RectangleDetector {
|
|
|
120
105
|
func reset() {
|
|
121
106
|
lastDetectedRectangle = nil
|
|
122
107
|
lastDetectionTime = nil
|
|
123
|
-
currentPixelBuffer = nil
|
|
124
108
|
previousCorners = nil
|
|
125
109
|
stabilityStartTime = nil
|
|
126
110
|
lastEmitTime = nil
|
|
@@ -149,22 +133,16 @@ class RectangleDetector {
|
|
|
149
133
|
return
|
|
150
134
|
}
|
|
151
135
|
|
|
152
|
-
// Find the rectangle
|
|
153
|
-
guard let bestCandidate = observations
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// Check if all corners are inside the screen with buffer
|
|
159
|
-
guard isInsideScreen(bestCandidate) else {
|
|
136
|
+
// Find the largest rectangle by area
|
|
137
|
+
guard let bestCandidate = observations
|
|
138
|
+
.filter({ $0.confidence >= confidenceThreshold && isInsideScreen($0) })
|
|
139
|
+
.max(by: { area(of: $0) < area(of: $1) }) else {
|
|
160
140
|
// Don't reset stability here - let checkTimeout() handle the grace period
|
|
161
141
|
return
|
|
162
142
|
}
|
|
163
143
|
|
|
164
|
-
// Store rectangle
|
|
165
|
-
|
|
166
|
-
captureSnapshot(for: bestCandidate)
|
|
167
|
-
}
|
|
144
|
+
// Store rectangle observation for cropping
|
|
145
|
+
lastDetectedRectangle = bestCandidate
|
|
168
146
|
|
|
169
147
|
// Update detection time
|
|
170
148
|
lastDetectionTime = Date()
|
|
@@ -265,6 +243,17 @@ class RectangleDetector {
|
|
|
265
243
|
return sqrt(dx * dx + dy * dy)
|
|
266
244
|
}
|
|
267
245
|
|
|
246
|
+
/// Approximate area of a rectangle observation using the shoelace formula (normalized coordinates)
|
|
247
|
+
private func area(of obs: VNRectangleObservation) -> CGFloat {
|
|
248
|
+
let pts = [obs.topLeft, obs.topRight, obs.bottomRight, obs.bottomLeft]
|
|
249
|
+
var sum: CGFloat = 0
|
|
250
|
+
for i in 0..<pts.count {
|
|
251
|
+
let j = (i + 1) % pts.count
|
|
252
|
+
sum += pts[i].x * pts[j].y - pts[j].x * pts[i].y
|
|
253
|
+
}
|
|
254
|
+
return abs(sum) / 2.0
|
|
255
|
+
}
|
|
256
|
+
|
|
268
257
|
private func isInsideScreen(_ observation: VNRectangleObservation) -> Bool {
|
|
269
258
|
let corners = [
|
|
270
259
|
observation.topLeft,
|
|
@@ -282,34 +271,4 @@ class RectangleDetector {
|
|
|
282
271
|
}
|
|
283
272
|
}
|
|
284
273
|
|
|
285
|
-
private func captureSnapshot(for observation: VNRectangleObservation) {
|
|
286
|
-
guard let pixelBuffer = currentPixelBuffer else {
|
|
287
|
-
lastDetectedRectangle = DetectedRectangle(observation: observation, snapshot: nil)
|
|
288
|
-
return
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
|
|
292
|
-
|
|
293
|
-
guard let cgImage = ciContext.createCGImage(ciImage, from: ciImage.extent) else {
|
|
294
|
-
lastDetectedRectangle = DetectedRectangle(observation: observation, snapshot: nil)
|
|
295
|
-
return
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Create UIImage with correct orientation
|
|
299
|
-
let deviceOrientation = UIDevice.current.orientation
|
|
300
|
-
let uiOrientation: UIImage.Orientation
|
|
301
|
-
switch deviceOrientation {
|
|
302
|
-
case .landscapeLeft:
|
|
303
|
-
uiOrientation = .right
|
|
304
|
-
case .landscapeRight:
|
|
305
|
-
uiOrientation = .left
|
|
306
|
-
case .portraitUpsideDown:
|
|
307
|
-
uiOrientation = .down
|
|
308
|
-
default:
|
|
309
|
-
uiOrientation = .up
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
let snapshot = UIImage(cgImage: cgImage, scale: 1.0, orientation: uiOrientation)
|
|
313
|
-
lastDetectedRectangle = DetectedRectangle(observation: observation, snapshot: snapshot)
|
|
314
|
-
}
|
|
315
274
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scr2em/capacitor-scanner",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.0.34",
|
|
4
4
|
"description": "scan codes",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
@@ -29,23 +29,6 @@
|
|
|
29
29
|
"plugin",
|
|
30
30
|
"native"
|
|
31
31
|
],
|
|
32
|
-
"scripts": {
|
|
33
|
-
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
34
|
-
"verify:ios": "xcodebuild -scheme CapacitorScanner -destination generic/platform=iOS",
|
|
35
|
-
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
36
|
-
"verify:web": "npm run build",
|
|
37
|
-
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
38
|
-
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
39
|
-
"eslint": "eslint . --ext ts",
|
|
40
|
-
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
41
|
-
"swiftlint": "node-swiftlint",
|
|
42
|
-
"docgen": "docgen --api CapacitorScannerPlugin --output-readme README.md --output-json dist/docs.json",
|
|
43
|
-
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
44
|
-
"clean": "rimraf ./dist",
|
|
45
|
-
"watch": "tsc --watch",
|
|
46
|
-
"prepublishOnly": "npm run build",
|
|
47
|
-
"prepare": "npm run build"
|
|
48
|
-
},
|
|
49
32
|
"devDependencies": {
|
|
50
33
|
"@capacitor/android": "6.0.0",
|
|
51
34
|
"@capacitor/cli": "6.0.0",
|
|
@@ -78,5 +61,20 @@
|
|
|
78
61
|
"android": {
|
|
79
62
|
"src": "android"
|
|
80
63
|
}
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
67
|
+
"verify:ios": "xcodebuild -scheme CapacitorScanner -destination generic/platform=iOS",
|
|
68
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
69
|
+
"verify:web": "npm run build",
|
|
70
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
71
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
72
|
+
"eslint": "eslint . --ext ts",
|
|
73
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
74
|
+
"swiftlint": "node-swiftlint",
|
|
75
|
+
"docgen": "docgen --api CapacitorScannerPlugin --output-readme README.md --output-json dist/docs.json",
|
|
76
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
77
|
+
"clean": "rimraf ./dist",
|
|
78
|
+
"watch": "tsc --watch"
|
|
81
79
|
}
|
|
82
80
|
}
|