@scr2em/capacitor-scanner 6.0.6 → 6.0.7

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 CHANGED
@@ -19,6 +19,8 @@ npx cap sync
19
19
  * [`capturePhoto()`](#capturephoto)
20
20
  * [`checkPermissions()`](#checkpermissions)
21
21
  * [`requestPermissions()`](#requestpermissions)
22
+ * [`flipCamera()`](#flipcamera)
23
+ * [`toggleFlash()`](#toggleflash)
22
24
  * [`addListener('barcodeScanned', ...)`](#addlistenerbarcodescanned-)
23
25
  * [`removeAllListeners()`](#removealllisteners)
24
26
  * [Type Aliases](#type-aliases)
@@ -93,6 +95,26 @@ requestPermissions() => Promise<PermissionsResult>
93
95
  --------------------
94
96
 
95
97
 
98
+ ### flipCamera()
99
+
100
+ ```typescript
101
+ flipCamera() => Promise<void>
102
+ ```
103
+
104
+ --------------------
105
+
106
+
107
+ ### toggleFlash()
108
+
109
+ ```typescript
110
+ toggleFlash() => Promise<FlashResult>
111
+ ```
112
+
113
+ **Returns:** <code>Promise&lt;<a href="#flashresult">FlashResult</a>&gt;</code>
114
+
115
+ --------------------
116
+
117
+
96
118
  ### addListener('barcodeScanned', ...)
97
119
 
98
120
  ```typescript
@@ -134,6 +156,11 @@ removeAllListeners() => Promise<void>
134
156
  <code>{ camera: 'prompt' | 'denied' | 'granted' }</code>
135
157
 
136
158
 
159
+ #### FlashResult
160
+
161
+ <code>{ enabled: boolean }</code>
162
+
163
+
137
164
  #### BarcodeScannedEvent
138
165
 
139
166
  <code>{ scannedCode: string; format: string }</code>
@@ -2,6 +2,7 @@ package com.leadliaion.capacitorscanner;
2
2
 
3
3
  import android.Manifest;
4
4
  import android.content.Intent;
5
+ import android.content.pm.PackageManager;
5
6
  import android.graphics.Color;
6
7
  import android.net.Uri;
7
8
  import android.provider.Settings;
@@ -17,6 +18,7 @@ import android.view.View;
17
18
  import com.getcapacitor.JSArray;
18
19
 
19
20
  import androidx.annotation.NonNull;
21
+ import androidx.camera.core.Camera;
20
22
  import androidx.camera.core.CameraSelector;
21
23
 
22
24
  import androidx.camera.core.ExperimentalGetImage;
@@ -79,6 +81,10 @@ public class CapacitorScannerPlugin extends Plugin {
79
81
  private ImageAnalysis imageAnalysis;
80
82
  private ImageCapture imageCapture;
81
83
 
84
+ private boolean isFlashEnabled = false;
85
+ private int currentLensFacing = CameraSelector.LENS_FACING_BACK;
86
+
87
+
82
88
 
83
89
  @Override
84
90
  public void load() {
@@ -209,6 +215,9 @@ public class CapacitorScannerPlugin extends Plugin {
209
215
  });
210
216
  }
211
217
 
218
+ private Camera camera;
219
+
220
+
212
221
  private void bindCamera(@NonNull ProcessCameraProvider cameraProvider, PreviewView previewView, int lensFacing, PluginCall call) {
213
222
  cameraProvider.unbindAll();
214
223
 
@@ -239,16 +248,17 @@ public class CapacitorScannerPlugin extends Plugin {
239
248
  .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
240
249
  .setTargetRotation(rotation)
241
250
  .build();
242
-
243
251
  imageAnalysis.setAnalyzer(executor, new BarcodeAnalyzer(call));
244
252
 
245
253
  imageCapture = new ImageCapture.Builder()
246
254
  .setTargetResolution(targetResolution)
247
255
  .setTargetRotation(rotation)
256
+ .setFlashMode(isFlashEnabled ? ImageCapture.FLASH_MODE_ON : ImageCapture.FLASH_MODE_OFF)
248
257
  .build();
249
258
 
250
259
  try {
251
- cameraProvider.bindToLifecycle(getActivity(), cameraSelector, preview, imageAnalysis, imageCapture);
260
+ // Store the Camera instance for later use
261
+ camera = cameraProvider.bindToLifecycle(getActivity(), cameraSelector, preview, imageAnalysis, imageCapture);
252
262
  preview.setSurfaceProvider(previewView.getSurfaceProvider());
253
263
  } catch (Exception e) {
254
264
  echo("Failed to bind camera to lifecycle: " + e.getMessage());
@@ -466,6 +476,61 @@ public class CapacitorScannerPlugin extends Plugin {
466
476
  }
467
477
  }
468
478
 
479
+
480
+ @PluginMethod
481
+ public void flipCamera(PluginCall call) {
482
+ if (!isScanning.get()) {
483
+ call.reject("Camera is not running");
484
+ return;
485
+ }
486
+
487
+ getActivity().runOnUiThread(() -> {
488
+ try {
489
+ currentLensFacing = (currentLensFacing == CameraSelector.LENS_FACING_BACK)
490
+ ? CameraSelector.LENS_FACING_FRONT
491
+ : CameraSelector.LENS_FACING_BACK;
492
+
493
+ bindCamera(cameraProvider, previewView, currentLensFacing, call);
494
+ call.resolve();
495
+ } catch (Exception e) {
496
+ call.reject("Failed to flip camera: " + e.getMessage());
497
+ }
498
+ });
499
+ }
500
+
501
+ @PluginMethod
502
+ public void toggleFlash(PluginCall call) {
503
+ if (!isScanning.get()) {
504
+ call.reject("Camera is not running");
505
+ return;
506
+ }
507
+
508
+ if (!getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)) {
509
+ call.reject("Device doesn't have flash capability");
510
+ return;
511
+ }
512
+
513
+ getActivity().runOnUiThread(() -> {
514
+ try {
515
+ if (camera != null) {
516
+ // Toggle the flash state
517
+ isFlashEnabled = !isFlashEnabled;
518
+ camera.getCameraControl().enableTorch(isFlashEnabled);
519
+
520
+ JSObject ret = new JSObject();
521
+ ret.put("enabled", isFlashEnabled);
522
+ call.resolve(ret);
523
+ } else {
524
+ call.reject("Camera is not initialized");
525
+ }
526
+ } catch (Exception e) {
527
+ call.reject("Failed to toggle flash: " + e.getMessage());
528
+ }
529
+ });
530
+ }
531
+
532
+
533
+
469
534
  private void logLongMessage(String message) {
470
535
  if (message.length() > 4000) {
471
536
  int chunkCount = message.length() / 4000;
package/dist/docs.json CHANGED
@@ -79,6 +79,28 @@
79
79
  ],
80
80
  "slug": "requestpermissions"
81
81
  },
82
+ {
83
+ "name": "flipCamera",
84
+ "signature": "() => Promise<void>",
85
+ "parameters": [],
86
+ "returns": "Promise<void>",
87
+ "tags": [],
88
+ "docs": "",
89
+ "complexTypes": [],
90
+ "slug": "flipcamera"
91
+ },
92
+ {
93
+ "name": "toggleFlash",
94
+ "signature": "() => Promise<FlashResult>",
95
+ "parameters": [],
96
+ "returns": "Promise<FlashResult>",
97
+ "tags": [],
98
+ "docs": "",
99
+ "complexTypes": [
100
+ "FlashResult"
101
+ ],
102
+ "slug": "toggleflash"
103
+ },
82
104
  {
83
105
  "name": "addListener",
84
106
  "signature": "(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void) => Promise<void>",
@@ -226,6 +248,17 @@
226
248
  }
227
249
  ]
228
250
  },
251
+ {
252
+ "name": "FlashResult",
253
+ "slug": "flashresult",
254
+ "docs": "",
255
+ "types": [
256
+ {
257
+ "text": "{ enabled: boolean }",
258
+ "complexTypes": []
259
+ }
260
+ ]
261
+ },
229
262
  {
230
263
  "name": "BarcodeScannedEvent",
231
264
  "slug": "barcodescannedevent",
@@ -5,6 +5,8 @@ export interface CapacitorScannerPlugin {
5
5
  capturePhoto(): Promise<CapturePhotoResult>;
6
6
  checkPermissions(): Promise<PermissionsResult>;
7
7
  requestPermissions(): Promise<PermissionsResult>;
8
+ flipCamera(): Promise<void>;
9
+ toggleFlash(): Promise<FlashResult>;
8
10
  addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;
9
11
  removeAllListeners(): Promise<void>;
10
12
  }
@@ -23,6 +25,9 @@ export declare type PermissionsResult = {
23
25
  export declare type CapturePhotoResult = {
24
26
  imageBase64: string;
25
27
  };
28
+ export declare type FlashResult = {
29
+ enabled: boolean;
30
+ };
26
31
  export declare enum BarcodeFormat {
27
32
  Aztec = "AZTEC",
28
33
  Code39 = "CODE_39",
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AAuBA,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 startScanning(options?: ScannerOptions): Promise<void>;\n stopScanning(): Promise<void>;\n openSettings(): Promise<void>;\n capturePhoto(): Promise<CapturePhotoResult>;\n checkPermissions(): Promise<PermissionsResult>;\n requestPermissions(): Promise<PermissionsResult>;\n addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;\n removeAllListeners(): Promise<void>;\n}\n\nexport type ScannerOptions = {\n formats?: BarcodeFormat[];\n cameraDirection?: 'BACK' | 'FRONT';\n debounceTimeInMilli?: number\n};\n\nexport type BarcodeScannedEvent = { scannedCode: string; format: string };\n\nexport type PermissionsResult = { camera: 'prompt' | 'denied' | 'granted' };\n\nexport type CapturePhotoResult = { imageBase64: string };\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}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"AA2BA,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 startScanning(options?: ScannerOptions): Promise<void>;\n stopScanning(): Promise<void>;\n openSettings(): Promise<void>;\n capturePhoto(): Promise<CapturePhotoResult>;\n checkPermissions(): Promise<PermissionsResult>;\n requestPermissions(): Promise<PermissionsResult>;\n flipCamera(): Promise<void>;\n toggleFlash(): Promise<FlashResult>;\n addListener(event: 'barcodeScanned', listenerFunc: (result: BarcodeScannedEvent) => void): Promise<void>;\n removeAllListeners(): Promise<void>;\n}\n\nexport type ScannerOptions = {\n formats?: BarcodeFormat[];\n cameraDirection?: 'BACK' | 'FRONT';\n debounceTimeInMilli?: number\n};\n\nexport type BarcodeScannedEvent = { scannedCode: string; format: string };\n\nexport type PermissionsResult = { camera: 'prompt' | 'denied' | 'granted' };\n\nexport type CapturePhotoResult = { imageBase64: string };\n\nexport type FlashResult = { enabled: boolean };\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}"]}
@@ -23,6 +23,8 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
23
23
  CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
24
24
  CAPPluginMethod(name: "openSettings", returnType: CAPPluginReturnPromise),
25
25
  CAPPluginMethod(name: "capturePhoto", returnType: CAPPluginReturnPromise),
26
+ CAPPluginMethod(name: "flipCamera", returnType: CAPPluginReturnPromise),
27
+ CAPPluginMethod(name: "toggleFlash", returnType: CAPPluginReturnPromise),
26
28
  ]
27
29
 
28
30
  private var captureSession: AVCaptureSession?
@@ -376,6 +378,89 @@ public class CapacitorScannerPlugin: CAPPlugin, CAPBridgedPlugin, AVCaptureMetad
376
378
  self.scannedCodesVotes[payload] = voteStatus
377
379
  }
378
380
  }
381
+
382
+ @objc func flipCamera(_ call: CAPPluginCall) {
383
+ guard let session = self.captureSession else {
384
+ call.reject("Scanning session not active.")
385
+ return
386
+ }
387
+
388
+ DispatchQueue.global(qos: .userInitiated).async {
389
+ session.beginConfiguration()
390
+ // Defer ensures commitConfiguration is called even if errors occur
391
+ defer { session.commitConfiguration() }
392
+
393
+ guard let currentInput = session.inputs.first as? AVCaptureDeviceInput else {
394
+ call.reject("Could not get current camera input.")
395
+ return
396
+ }
397
+
398
+ let currentPosition = currentInput.device.position
399
+ let preferredPosition: AVCaptureDevice.Position = (currentPosition == .back) ? .front : .back
400
+
401
+ guard let newDevice = self.getCaptureDevice(position: preferredPosition) else {
402
+ call.reject("Could not find camera for position: \(preferredPosition).")
403
+ return
404
+ }
405
+
406
+ guard let newInput = try? AVCaptureDeviceInput(device: newDevice) else {
407
+ call.reject("Could not create input for new camera device.")
408
+ return
409
+ }
410
+
411
+ // Remove the existing input
412
+ session.removeInput(currentInput)
413
+
414
+ // Add the new input
415
+ if session.canAddInput(newInput) {
416
+ session.addInput(newInput)
417
+
418
+ // Session automatically handles connecting outputs (like photoOutput and videoDataOutput)
419
+ // to the new input. No explicit reconnection needed here.
420
+
421
+ // Ensure preview layer orientation is updated for the new camera
422
+ DispatchQueue.main.async {
423
+ self.updatePreviewOrientation()
424
+ call.resolve() // Resolve the call on the main thread after UI update
425
+ }
426
+
427
+ } else {
428
+ // Important: If adding the new input fails, try to add the old one back!
429
+ print("Failed to add new input, attempting to restore previous input.")
430
+ if session.canAddInput(currentInput) {
431
+ session.addInput(currentInput)
432
+ }
433
+ call.reject("Could not add new camera input to session.")
434
+ }
435
+ }
436
+ }
437
+
438
+ @objc func toggleFlash(_ call: CAPPluginCall) {
439
+ guard let session = self.captureSession, let currentInput = session.inputs.first as? AVCaptureDeviceInput else {
440
+ call.reject("Scanning session not active or camera input not found.")
441
+ return
442
+ }
443
+
444
+ let device = currentInput.device
445
+
446
+ guard device.hasTorch, device.isTorchAvailable else {
447
+ call.resolve(["enabled": false]) // Report flash as disabled if not available/supported
448
+ return
449
+ }
450
+
451
+ do {
452
+ try device.lockForConfiguration()
453
+ let currentMode = device.torchMode
454
+ let newMode: AVCaptureDevice.TorchMode = (currentMode == .on) ? .off : .on
455
+ if device.isTorchModeSupported(newMode) {
456
+ device.torchMode = newMode
457
+ }
458
+ device.unlockForConfiguration()
459
+ call.resolve(["enabled": device.torchMode == .on])
460
+ } catch {
461
+ call.reject("Could not lock device for flash configuration: \(error.localizedDescription)")
462
+ }
463
+ }
379
464
  }
380
465
 
381
466
  extension CapacitorScannerPlugin: AVCaptureVideoDataOutputSampleBufferDelegate {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scr2em/capacitor-scanner",
3
- "version": "6.0.6",
3
+ "version": "6.0.7",
4
4
  "description": "scan codes",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",