qrdnicapacitor 1.0.3

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 (31) hide show
  1. package/Package.swift +34 -0
  2. package/Qrdnicapacitor.podspec +18 -0
  3. package/README.md +106 -0
  4. package/android/build.gradle +64 -0
  5. package/android/src/main/AndroidManifest.xml +16 -0
  6. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/QrCodeScanner.java +84 -0
  7. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/jj2000/ImgStreamWriter.java +483 -0
  8. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/jj2000/J2kStreamDecoder.java +116 -0
  9. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/jj2000/MyFileFormatReader.java +204 -0
  10. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/qrdni.java +130 -0
  11. package/android/src/main/java/com/cqesolutions/qrdnicapacitor/qrdniPlugin.java +109 -0
  12. package/android/src/main/res/.gitkeep +0 -0
  13. package/dist/docs.json +255 -0
  14. package/dist/esm/definitions.d.ts +44 -0
  15. package/dist/esm/definitions.js +2 -0
  16. package/dist/esm/definitions.js.map +1 -0
  17. package/dist/esm/index.d.ts +4 -0
  18. package/dist/esm/index.js +7 -0
  19. package/dist/esm/index.js.map +1 -0
  20. package/dist/esm/web.d.ts +14 -0
  21. package/dist/esm/web.js +18 -0
  22. package/dist/esm/web.js.map +1 -0
  23. package/dist/plugin.cjs.js +32 -0
  24. package/dist/plugin.cjs.js.map +1 -0
  25. package/dist/plugin.js +35 -0
  26. package/dist/plugin.js.map +1 -0
  27. package/ios/Sources/qrdniPlugin/ScannerViewController.swift +170 -0
  28. package/ios/Sources/qrdniPlugin/qrdni.swift +111 -0
  29. package/ios/Sources/qrdniPlugin/qrdniPlugin.swift +105 -0
  30. package/ios/Tests/qrdniPluginTests/qrdniTests.swift +15 -0
  31. package/package.json +80 -0
@@ -0,0 +1,44 @@
1
+ export interface qrdniPlugin {
2
+ configure(options: {
3
+ license: string;
4
+ certs?: {
5
+ [key: string]: string;
6
+ };
7
+ }): Promise<EstadoLicencia>;
8
+ validaMiDNIQR(options: {
9
+ data: string;
10
+ }): Promise<MiDNIData>;
11
+ abrirEscaner(): Promise<any>;
12
+ }
13
+ export interface EstadoLicencia {
14
+ descripcion: string;
15
+ APIKeyValida: boolean;
16
+ lecturaQRHabilitada: boolean;
17
+ }
18
+ export interface MiDNIData {
19
+ dni: string;
20
+ name: string;
21
+ surnames: string;
22
+ birthDate: string;
23
+ expiryDate: string;
24
+ gender: string;
25
+ address: string;
26
+ nationality: string;
27
+ parents: string;
28
+ supportNumber: string;
29
+ birthPlace1: string;
30
+ birthPlace2: string;
31
+ birthPlace3: string;
32
+ photoData: string;
33
+ isAdult?: boolean;
34
+ rawSignature: string;
35
+ signedData: string;
36
+ certificateRef: string;
37
+ type?: number;
38
+ verificationResult: {
39
+ status: 'VALID' | 'INVALID' | 'NO_CERTIFICATES' | 'INVALID_QR' | 'EXPIRED_QR' | 'UNKNOWN';
40
+ certificate?: string;
41
+ };
42
+ qrDataExpiry: string;
43
+ fullBirthPlace: string;
44
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=definitions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["export interface qrdniPlugin {\n configure(options: { license: string, certs?: { [key: string]: string } }): Promise<EstadoLicencia>;\n validaMiDNIQR(options: { data: string }): Promise<MiDNIData>;\n abrirEscaner(): Promise<any>;\n}\n\nexport interface EstadoLicencia {\n descripcion: string;\n APIKeyValida: boolean;\n lecturaQRHabilitada: boolean;\n}\n\nexport interface MiDNIData {\n dni: string;\n name: string;\n surnames: string;\n birthDate: string;\n expiryDate: string;\n gender: string;\n\n address: string;\n nationality: string;\n parents: string;\n supportNumber: string;\n \n birthPlace1: string;\n birthPlace2: string;\n birthPlace3: string;\n \n photoData: string; // Base64\n isAdult?: boolean;\n \n rawSignature: string;\n signedData: string;\n certificateRef: string;\n type?: number;\n verificationResult: {\n status: 'VALID' | 'INVALID' | 'NO_CERTIFICATES' | 'INVALID_QR' | 'EXPIRED_QR' | 'UNKNOWN';\n certificate?: string; // Solo presente si el status es VALID\n }; \n qrDataExpiry: string;\n\n fullBirthPlace: string;\n}"]}
@@ -0,0 +1,4 @@
1
+ import type { qrdniPlugin } from './definitions';
2
+ declare const qrdni: qrdniPlugin;
3
+ export * from './definitions';
4
+ export { qrdni };
@@ -0,0 +1,7 @@
1
+ import { registerPlugin } from '@capacitor/core';
2
+ const qrdni = registerPlugin('qrdni', {
3
+ web: () => import('./web').then((m) => new m.qrdniWeb()),
4
+ });
5
+ export * from './definitions';
6
+ export { qrdni };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,KAAK,GAAG,cAAc,CAAc,OAAO,EAAE;IACjD,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;CACzD,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,KAAK,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { qrdniPlugin } from './definitions';\n\nconst qrdni = registerPlugin<qrdniPlugin>('qrdni', {\n web: () => import('./web').then((m) => new m.qrdniWeb()),\n});\n\nexport * from './definitions';\nexport { qrdni };\n"]}
@@ -0,0 +1,14 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ import type { qrdniPlugin, EstadoLicencia, MiDNIData } from './definitions';
3
+ export declare class qrdniWeb extends WebPlugin implements qrdniPlugin {
4
+ abrirEscaner(): Promise<any>;
5
+ configure(options: {
6
+ license: string;
7
+ certs?: {
8
+ [key: string]: string;
9
+ };
10
+ }): Promise<EstadoLicencia>;
11
+ validaMiDNIQR(options: {
12
+ data: string;
13
+ }): Promise<MiDNIData>;
14
+ }
@@ -0,0 +1,18 @@
1
+ import { WebPlugin } from '@capacitor/core';
2
+ export class qrdniWeb extends WebPlugin {
3
+ abrirEscaner() {
4
+ console.log("NOT IMPLEMENTED");
5
+ throw new Error('Method not implemented.');
6
+ }
7
+ configure(options) {
8
+ console.log("NOT IMPLEMENTED");
9
+ console.log(options);
10
+ throw new Error('Method not implemented.');
11
+ }
12
+ validaMiDNIQR(options) {
13
+ console.log("NOT IMPLEMENTED");
14
+ console.log(options);
15
+ throw new Error('Method not implemented.');
16
+ }
17
+ }
18
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAI5C,MAAM,OAAO,QAAS,SAAQ,SAAS;IACrC,YAAY;QACV,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,SAAS,CAAC,OAAiE;QACzE,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;IACD,aAAa,CAAC,OAA0B;QACtC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\n\nimport type { qrdniPlugin, EstadoLicencia, MiDNIData } from './definitions';\n\nexport class qrdniWeb extends WebPlugin implements qrdniPlugin {\n abrirEscaner(): Promise<any> {\n console.log(\"NOT IMPLEMENTED\");\n throw new Error('Method not implemented.');\n }\n configure(options: { license: string; certs?: { [key: string]: string; }; }): Promise<EstadoLicencia> {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n validaMiDNIQR(options: { data: string; }): Promise<MiDNIData> {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n}\n"]}
@@ -0,0 +1,32 @@
1
+ 'use strict';
2
+
3
+ var core = require('@capacitor/core');
4
+
5
+ const qrdni = core.registerPlugin('qrdni', {
6
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.qrdniWeb()),
7
+ });
8
+
9
+ class qrdniWeb extends core.WebPlugin {
10
+ abrirEscaner() {
11
+ console.log("NOT IMPLEMENTED");
12
+ throw new Error('Method not implemented.');
13
+ }
14
+ configure(options) {
15
+ console.log("NOT IMPLEMENTED");
16
+ console.log(options);
17
+ throw new Error('Method not implemented.');
18
+ }
19
+ validaMiDNIQR(options) {
20
+ console.log("NOT IMPLEMENTED");
21
+ console.log(options);
22
+ throw new Error('Method not implemented.');
23
+ }
24
+ }
25
+
26
+ var web = /*#__PURE__*/Object.freeze({
27
+ __proto__: null,
28
+ qrdniWeb: qrdniWeb
29
+ });
30
+
31
+ exports.qrdni = qrdni;
32
+ //# sourceMappingURL=plugin.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst qrdni = registerPlugin('qrdni', {\n web: () => import('./web').then((m) => new m.qrdniWeb()),\n});\nexport * from './definitions';\nexport { qrdni };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class qrdniWeb extends WebPlugin {\n abrirEscaner() {\n console.log(\"NOT IMPLEMENTED\");\n throw new Error('Method not implemented.');\n }\n configure(options) {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n validaMiDNIQR(options) {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;;AACK,MAAC,KAAK,GAAGA,mBAAc,CAAC,OAAO,EAAE;AACtC,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC;;ACFM,MAAM,QAAQ,SAASC,cAAS,CAAC;AACxC,IAAI,YAAY,GAAG;AACnB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtC,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;AAClD,IAAI;AACJ,IAAI,SAAS,CAAC,OAAO,EAAE;AACvB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;AAClD,IAAI;AACJ,IAAI,aAAa,CAAC,OAAO,EAAE;AAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;AACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;AAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;AAClD,IAAI;AACJ;;;;;;;;;"}
package/dist/plugin.js ADDED
@@ -0,0 +1,35 @@
1
+ var capacitorqrdni = (function (exports, core) {
2
+ 'use strict';
3
+
4
+ const qrdni = core.registerPlugin('qrdni', {
5
+ web: () => Promise.resolve().then(function () { return web; }).then((m) => new m.qrdniWeb()),
6
+ });
7
+
8
+ class qrdniWeb extends core.WebPlugin {
9
+ abrirEscaner() {
10
+ console.log("NOT IMPLEMENTED");
11
+ throw new Error('Method not implemented.');
12
+ }
13
+ configure(options) {
14
+ console.log("NOT IMPLEMENTED");
15
+ console.log(options);
16
+ throw new Error('Method not implemented.');
17
+ }
18
+ validaMiDNIQR(options) {
19
+ console.log("NOT IMPLEMENTED");
20
+ console.log(options);
21
+ throw new Error('Method not implemented.');
22
+ }
23
+ }
24
+
25
+ var web = /*#__PURE__*/Object.freeze({
26
+ __proto__: null,
27
+ qrdniWeb: qrdniWeb
28
+ });
29
+
30
+ exports.qrdni = qrdni;
31
+
32
+ return exports;
33
+
34
+ })({}, capacitorExports);
35
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst qrdni = registerPlugin('qrdni', {\n web: () => import('./web').then((m) => new m.qrdniWeb()),\n});\nexport * from './definitions';\nexport { qrdni };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nexport class qrdniWeb extends WebPlugin {\n abrirEscaner() {\n console.log(\"NOT IMPLEMENTED\");\n throw new Error('Method not implemented.');\n }\n configure(options) {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n validaMiDNIQR(options) {\n console.log(\"NOT IMPLEMENTED\");\n console.log(options);\n throw new Error('Method not implemented.');\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":";;;AACK,UAAC,KAAK,GAAGA,mBAAc,CAAC,OAAO,EAAE;IACtC,IAAI,GAAG,EAAE,MAAM,mDAAe,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5D,CAAC;;ICFM,MAAM,QAAQ,SAASC,cAAS,CAAC;IACxC,IAAI,YAAY,GAAG;IACnB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtC,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAClD,IAAI;IACJ,IAAI,SAAS,CAAC,OAAO,EAAE;IACvB,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAClD,IAAI;IACJ,IAAI,aAAa,CAAC,OAAO,EAAE;IAC3B,QAAQ,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACtC,QAAQ,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;IAC5B,QAAQ,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC;IAClD,IAAI;IACJ;;;;;;;;;;;;;;;"}
@@ -0,0 +1,170 @@
1
+ //
2
+ // ScannerViewController.swift
3
+ // ValidAge
4
+ //
5
+ // Created by Diego Cid Merino on 8/1/24.
6
+ //
7
+
8
+ import AVFoundation
9
+ import UIKit
10
+
11
+ class ScannerViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
12
+ var captureSession: AVCaptureSession!
13
+ var previewLayer: AVCaptureVideoPreviewLayer!
14
+ var returnString: Bool = true
15
+
16
+
17
+ public func inicializa(returnString: Bool) {
18
+ self.returnString = returnString
19
+ }
20
+
21
+ override func viewDidLoad() {
22
+ super.viewDidLoad()
23
+ view.backgroundColor = .black
24
+
25
+ // Creamos la sesión
26
+ captureSession = AVCaptureSession()
27
+
28
+ // Ejecutamos la configuración pesada en segundo plano
29
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
30
+ guard let self = self else { return }
31
+
32
+ guard let videoCaptureDevice = AVCaptureDevice.default(for: .video) else { return }
33
+ let videoInput: AVCaptureDeviceInput
34
+
35
+ do {
36
+ videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
37
+ } catch {
38
+ return
39
+ }
40
+
41
+ if (self.captureSession.canAddInput(videoInput)) {
42
+ self.captureSession.addInput(videoInput)
43
+ }
44
+
45
+ let metadataOutput = AVCaptureMetadataOutput()
46
+
47
+ if (self.captureSession.canAddOutput(metadataOutput)) {
48
+ self.captureSession.addOutput(metadataOutput)
49
+ metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
50
+ metadataOutput.metadataObjectTypes = [.qr]
51
+ }
52
+
53
+ // La UI sí se actualiza en el Main Thread
54
+ DispatchQueue.main.async {
55
+ self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
56
+ self.previewLayer.frame = self.view.layer.bounds
57
+ self.previewLayer.videoGravity = .resizeAspectFill
58
+ self.view.layer.addSublayer(self.previewLayer)
59
+
60
+ // Iniciamos la sesión fuera del hilo principal de nuevo
61
+ DispatchQueue.global(qos: .userInitiated).async {
62
+ self.captureSession.startRunning()
63
+ }
64
+ }
65
+ }
66
+ }
67
+
68
+ func failed() {
69
+ let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
70
+ ac.addAction(UIAlertAction(title: "OK", style: .default))
71
+ present(ac, animated: true)
72
+ captureSession = nil
73
+ }
74
+
75
+ override func viewWillAppear(_ animated: Bool) {
76
+ super.viewWillAppear(animated)
77
+
78
+ if (captureSession?.isRunning == false) {
79
+ captureSession.startRunning()
80
+ }
81
+ }
82
+
83
+ override func viewWillDisappear(_ animated: Bool) {
84
+ super.viewWillDisappear(animated)
85
+
86
+ if (captureSession?.isRunning == true) {
87
+ captureSession.stopRunning()
88
+ }
89
+ }
90
+ /*
91
+ func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
92
+ captureSession.stopRunning()
93
+
94
+ var parametros: [String: Any] = [String: Any] ()
95
+ if(self.returnString)
96
+ {
97
+ if let metadataObject = metadataObjects.first {
98
+ guard let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject else { return }
99
+ guard let stringValue = readableObject.stringValue else { return }
100
+ AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
101
+ parametros["qrcode"] = stringValue
102
+ }
103
+ }
104
+ else {
105
+ if let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject {
106
+
107
+ // IMPORTANTE: Accedemos al descriptor, no al stringValue.
108
+ // El descriptor contiene los bytes crudos (RAW) del QR.
109
+ if let descriptor = metadataObject.descriptor as? CIQRCodeDescriptor {
110
+
111
+ let rawPayload = descriptor.errorCorrectedPayload
112
+ //print("Datos brutos: \(rawPayload.toPrettyHexString())")
113
+ parametros["qrcodeData"] = rawPayload
114
+ }
115
+ }
116
+ }
117
+
118
+ NotificationCenter.default.post(name: NSNotification.Name(rawValue: "lecturaQR"), object: nil, userInfo: parametros)
119
+ }
120
+ */
121
+
122
+ func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
123
+
124
+ // 1. Detenemos la sesión inmediatamente para evitar múltiples lecturas
125
+ // startRunning/stopRunning siempre fuera del hilo principal si es posible
126
+ DispatchQueue.global(qos: .userInitiated).async {
127
+ if self.captureSession.isRunning {
128
+ self.captureSession.stopRunning()
129
+ }
130
+ }
131
+
132
+ // 2. Procesamos los datos
133
+ guard let metadataObject = metadataObjects.first as? AVMetadataMachineReadableCodeObject,
134
+ let descriptor = metadataObject.descriptor as? CIQRCodeDescriptor else {
135
+ // Si falla, reiniciamos sesión
136
+ DispatchQueue.global(qos: .userInitiated).async { self.captureSession.startRunning() }
137
+ return
138
+ }
139
+
140
+ let rawPayload = descriptor.errorCorrectedPayload
141
+ let base64String = rawPayload.base64EncodedString()
142
+
143
+ // Feedback táctico
144
+ AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
145
+
146
+ // 3. TODO el proceso pesado de validación y cierre debe ir fuera del hilo UI
147
+ // pero el 'dismiss' debe volver al Main para no congelar la pantalla
148
+ DispatchQueue.main.async {
149
+ let parametros: [String: Any] = ["qrcode": base64String]
150
+
151
+ // Notificamos al Plugin
152
+ NotificationCenter.default.post(
153
+ name: NSNotification.Name(rawValue: "lecturaQR"),
154
+ object: nil,
155
+ userInfo: parametros
156
+ )
157
+
158
+ // Cerramos la cámara
159
+ self.dismiss(animated: true, completion: nil)
160
+ }
161
+ }
162
+
163
+ override var prefersStatusBarHidden: Bool {
164
+ return true
165
+ }
166
+
167
+ override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
168
+ return .portrait
169
+ }
170
+ }
@@ -0,0 +1,111 @@
1
+ import Foundation
2
+ import iQRDNI
3
+ import UIKit
4
+
5
+ @objc public class qrdni: NSObject {
6
+
7
+ public func configure(_ license: String, _ certConfig: [String: String]? = nil) -> EstadoLicencia {
8
+ return iQRDNI.configure(apiKey: license, certConfig: certConfig)
9
+ }
10
+
11
+ public func validaMiDNIQR(datosQR: Data) -> [String: Any]? {
12
+ guard let miDNIData = iQRDNI.validaMiDNIQR(qrRawData: datosQR) else {
13
+ return nil
14
+ }
15
+
16
+ var json: [String: Any] = [:]
17
+ json["dni"] = miDNIData.dni ?? ""
18
+ json["name"] = miDNIData.name ?? ""
19
+ json["surnames"] = miDNIData.surnames ?? ""
20
+ json["birthDate"] = miDNIData.birthDate ?? ""
21
+ json["expiryDate"] = miDNIData.expiryDate ?? ""
22
+ json["gender"] = miDNIData.gender ?? ""
23
+ json["address"] = miDNIData.address ?? ""
24
+ json["nationality"] = miDNIData.nationality ?? ""
25
+ json["parents"] = miDNIData.parents ?? ""
26
+ json["supportNumber"] = miDNIData.supportNumber ?? ""
27
+ json["birthPlace1"] = miDNIData.birthPlace1 ?? ""
28
+ json["birthPlace2"] = miDNIData.birthPlace2 ?? ""
29
+ json["birthPlace3"] = miDNIData.birthPlace3 ?? ""
30
+
31
+
32
+
33
+ if(miDNIData.photoData != nil)
34
+ {
35
+ if let photoImage = UIImage(data:miDNIData.photoData!)
36
+ {
37
+ let base64String = convertImageToBase64(image: photoImage)
38
+ json["photoData"] = base64String
39
+ }
40
+ else
41
+ {
42
+ json["photoData"] = ""
43
+ }
44
+ }
45
+ else
46
+ {
47
+ json["photoData"] = ""
48
+ }
49
+
50
+ if(miDNIData.isAdult != nil)
51
+ {
52
+ json["isAdult"] = miDNIData.isAdult
53
+ }
54
+ else
55
+ {
56
+ json["isAdult"] = false
57
+ }
58
+
59
+ json["rawSignature"] = miDNIData.rawSignature?.base64EncodedString() ?? ""
60
+ json["signedData"] = miDNIData.signedData?.base64EncodedString() ?? ""
61
+ json["certificateRef"] = miDNIData.certificateRef ?? ""
62
+
63
+ if(miDNIData.type != nil)
64
+ {
65
+ switch miDNIData.type!.rawValue as Int{
66
+ case 0:
67
+ json["type"] = "EDAD"
68
+ case 1:
69
+ json["type"] = "SIMPLE"
70
+ case 2:
71
+ json["type"] = "COMPLETO"
72
+ default:
73
+ json["type"] = ""
74
+ }
75
+ }
76
+
77
+ if let result = miDNIData.verificationResult {
78
+ var verificationJson: [String: Any] = [:]
79
+
80
+ switch result {
81
+ case .valid(let certificateName):
82
+ verificationJson["status"] = "VALID"
83
+ verificationJson["certificate"] = certificateName
84
+ case .invalid:
85
+ verificationJson["status"] = "INVALID"
86
+ case .noCertificates:
87
+ verificationJson["status"] = "NO_CERTIFICATES"
88
+ case .invalidQR:
89
+ verificationJson["status"] = "INVALID_QR"
90
+ case .expiratedQR:
91
+ verificationJson["status"] = "EXPIRED_QR"
92
+ }
93
+
94
+ json["verificationResult"] = verificationJson
95
+ } else {
96
+ json["verificationResult"] = ["status": "UNKNOWN"]
97
+ }
98
+
99
+ json["qrDataExpiry"] = miDNIData.qrDataExpiry ?? ""
100
+
101
+ json["fullBirthPlace"] = miDNIData.fullBirthPlace ?? ""
102
+
103
+ return json
104
+ }
105
+
106
+ public func convertImageToBase64(image: UIImage) -> String {
107
+ let imageData = image.pngData()!
108
+ return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
109
+ }
110
+
111
+ }
@@ -0,0 +1,105 @@
1
+ import Foundation
2
+ import Capacitor
3
+
4
+ @objc(qrdniPlugin)
5
+ public class qrdniPlugin: CAPPlugin, CAPBridgedPlugin {
6
+ public let identifier = "qrdniPlugin"
7
+ public let jsName = "qrdni"
8
+ public let pluginMethods: [CAPPluginMethod] = [
9
+ CAPPluginMethod(name: "configure", returnType: CAPPluginReturnPromise),
10
+ CAPPluginMethod(name: "validaMiDNIQR", returnType: CAPPluginReturnPromise),
11
+ CAPPluginMethod(name: "abrirEscaner", returnType: CAPPluginReturnPromise),
12
+ ]
13
+ private let implementation = qrdni()
14
+
15
+ // PROPIEDAD PARA GUARDAR LA LLAMADA (Plan B)
16
+ private var scanCall: CAPPluginCall?
17
+
18
+ @objc func configure(_ call: CAPPluginCall) {
19
+ let license = call.getString("license") ?? ""
20
+ let certs = call.getObject("certs") as? [String: String]
21
+ let resultado = implementation.configure(license, certs)
22
+
23
+ var json: [String: Any] = [:]
24
+ json["descripcion"] = resultado.descripcion
25
+ json["APIKeyValida"] = resultado.APIKeyValida
26
+ json["lecturaQRHabilitada"] = resultado.lecturaQRHabilitada
27
+
28
+ call.resolve(json)
29
+ }
30
+
31
+ @objc func validaMiDNIQR(_ call: CAPPluginCall) {
32
+ guard let qrBase64 = call.getString("data") else {
33
+ call.reject("No se proporcionó el QR")
34
+ return
35
+ }
36
+
37
+ if let qrData = Data(base64Encoded: qrBase64) {
38
+ if let resultadoJson = implementation.validaMiDNIQR(datosQR: qrData) {
39
+ call.resolve(resultadoJson)
40
+ } else {
41
+ call.reject("La validación del DNI falló o el QR es inválido")
42
+ }
43
+ } else {
44
+ call.reject("Formato Base64 inválido")
45
+ }
46
+ }
47
+
48
+ @objc func abrirEscaner(_ call: CAPPluginCall) {
49
+ // 1. Guardamos la llamada en nuestra variable local
50
+ self.scanCall = call
51
+
52
+ // 2. También la guardamos en el bridge por seguridad
53
+ self.bridge?.saveCall(call)
54
+
55
+ DispatchQueue.main.async {
56
+ let scannerVC = ScannerViewController()
57
+ scannerVC.modalPresentationStyle = .fullScreen
58
+ scannerVC.inicializa(returnString: false)
59
+
60
+ NotificationCenter.default.addObserver(self, selector: #selector(self.handleScannerResult(_:)), name: NSNotification.Name("lecturaQR"), object: nil)
61
+
62
+ self.bridge?.viewController?.present(scannerVC, animated: true)
63
+ }
64
+ }
65
+
66
+ @objc func handleScannerResult(_ notification: Notification) {
67
+ NotificationCenter.default.removeObserver(self, name: NSNotification.Name("lecturaQR"), object: nil)
68
+
69
+ // 3. RECUPERAMOS LA LLAMADA DESDE NUESTRA VARIABLE
70
+ guard let call = self.scanCall else {
71
+ print("ERROR: La referencia local a la llamada es nil")
72
+ return
73
+ }
74
+
75
+ guard let userInfo = notification.userInfo,
76
+ let qrBase64 = userInfo["qrcode"] as? String else {
77
+ call.reject("Error al obtener datos")
78
+ self.scanCall = nil // Limpiamos
79
+ return
80
+ }
81
+
82
+ DispatchQueue.global(qos: .userInitiated).async { [weak self] in
83
+ guard let self = self else { return }
84
+
85
+ if let qrData = Data(base64Encoded: qrBase64) {
86
+ if let resultadoJson = self.implementation.validaMiDNIQR(datosQR: qrData) {
87
+ DispatchQueue.main.async {
88
+ call.resolve(resultadoJson)
89
+ self.scanCall = nil // Limpieza final
90
+ }
91
+ } else {
92
+ DispatchQueue.main.async {
93
+ call.reject("Fallo en validación")
94
+ self.scanCall = nil
95
+ }
96
+ }
97
+ } else {
98
+ DispatchQueue.main.async {
99
+ call.reject("Base64 corrupto")
100
+ self.scanCall = nil
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,15 @@
1
+ import XCTest
2
+ @testable import qrdniPlugin
3
+
4
+ class qrdniTests: XCTestCase {
5
+ func testEcho() {
6
+ // This is an example of a functional test case for a plugin.
7
+ // Use XCTAssert and related functions to verify your tests produce the correct results.
8
+
9
+ let implementation = qrdni()
10
+ let value = "Hello, World!"
11
+ let result = implementation.echo(value)
12
+
13
+ XCTAssertEqual(value, result)
14
+ }
15
+ }