cordova-plugin-mlkit-suite 0.1.0 → 0.2.0

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 (38) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +526 -640
  3. package/docs/screenshots/ios-text-recognition.jpg +0 -0
  4. package/package.json +7 -3
  5. package/plugin.xml +149 -134
  6. package/src/android/java/BarcodeFrameAnalyzer.java +115 -111
  7. package/src/android/java/BarcodeOverlayView.java +83 -25
  8. package/src/android/java/BarcodeScanActivity.java +100 -12
  9. package/src/android/java/BarcodeScanConfig.java +6 -1
  10. package/src/android/java/BarcodeScanRegion.java +26 -8
  11. package/src/android/java/MlKitModelsModuleInstaller.java +35 -16
  12. package/src/android/java/MlKitSuitePlugin.java +315 -33
  13. package/src/android/java/SelfieSegmentationProcessor.java +215 -0
  14. package/src/android/java/StaticImageProcessor.java +208 -151
  15. package/src/android/java/SubjectSegmentationModuleInstaller.java +154 -0
  16. package/src/android/java/SubjectSegmentationProcessor.java +189 -0
  17. package/src/android/mlkit-suite.gradle +30 -0
  18. package/src/android/res/values/mlkit_suite_strings.xml +8 -0
  19. package/src/android/res/values-es/mlkit_suite_strings.xml +8 -0
  20. package/src/ios/BarcodeOverlayView.swift +57 -6
  21. package/src/ios/BarcodeScanTypes.swift +3 -1
  22. package/src/ios/BarcodeScanViewController.swift +120 -5
  23. package/src/ios/MlKitSuitePlugin.swift +222 -13
  24. package/src/ios/MlKitSuiteResources.bundle/en.lproj/Localizable.strings +4 -0
  25. package/src/ios/MlKitSuiteResources.bundle/es.lproj/Localizable.strings +4 -0
  26. package/src/ios/SelfieSegmentationProcessor.swift +105 -0
  27. package/src/ios/TextRecognitionProcessor.swift +99 -0
  28. package/tests/ios-image-labeling.test.js +17 -0
  29. package/tests/ios-live-barcode.test.js +5 -0
  30. package/tests/ios-selfie-segmentation.test.js +24 -0
  31. package/tests/ios-static-text.test.js +21 -0
  32. package/tests/java/BarcodeScanRegionTest.java +12 -0
  33. package/tests/java-confirmation.test.js +41 -41
  34. package/tests/js-api.test.js +42 -2
  35. package/www/MlKit.js +111 -7
  36. package/docs/screenshots/capabilities.jpg +0 -0
  37. package/docs/screenshots/demo-tour.gif +0 -0
  38. package/docs/screenshots/text-recognition.jpg +0 -0
package/README.md CHANGED
@@ -1,640 +1,526 @@
1
- ![npm downloads](https://img.shields.io/npm/dt/cordova-plugin-mlkit-suite) ![npm version](https://img.shields.io/npm/v/cordova-plugin-mlkit-suite) ![GitHub package.json version](https://img.shields.io/github/package-json/v/andreszs/cordova-plugin-mlkit-suite?color=FF6D00&label=main&logo=github) [![Android and iOS demo](https://img.shields.io/badge/demo-Android%20%7C%20iOS-2ea44f?logo=apachecordova&logoColor=white)](https://github.com/andreszs/cordova-plugin-demos/tree/main/com.andreszs.mlkit.suite.demo) ![GitHub license](https://img.shields.io/github/license/andreszs/cordova-plugin-mlkit-suite) ![GitHub last commit](https://img.shields.io/github/last-commit/andreszs/cordova-plugin-mlkit-suite)
2
-
3
- <img src="docs/mlkit-logo.svg" alt="ML Kit logo" width="64" align="right">
4
-
5
- # cordova-plugin-mlkit-suite
6
-
7
- Extensible [Google ML Kit](https://developers.google.com/ml-kit) integration for Cordova on Android and iOS. Version `0.1.0` includes guided face capture, camera and still-image barcode scanning, Latin text recognition, and the native Android document scanner.
8
-
9
- > [!WARNING]
10
- > This plugin is in active development. Its API, defaults, quality thresholds, and native behavior may change before a stable release. Validate every required workflow on representative physical devices before using it in production.
11
-
12
- ## Table of contents
13
-
14
- - [Features](#features)
15
- - [Screenshots](#screenshots)
16
- - [Compatibility](#compatibility)
17
- - [Installation](#installation)
18
- - [iOS setup](#ios-setup)
19
- - [Android model delivery](#android-model-delivery)
20
- - [Bundled vs unbundled](#bundled-vs-unbundled)
21
- - [Localization](#localization)
22
- - [API overview](#api-overview)
23
- - [Capabilities](#capabilities)
24
- - [Document scanning](#document-scanning)
25
- - [Document options](#document-options)
26
- - [Document result](#document-result)
27
- - [Text recognition](#text-recognition)
28
- - [Barcode detection in an image](#barcode-detection-in-an-image)
29
- - [Camera barcode scanning](#camera-barcode-scanning)
30
- - [Barcode formats](#barcode-formats)
31
- - [Camera barcode options](#camera-barcode-options)
32
- - [Face capture](#face-capture)
33
- - [Face options](#face-options)
34
- - [Optional face checks](#optional-face-checks)
35
- - [Face result](#face-result)
36
- - [Cancellation and errors](#cancellation-and-errors)
37
- - [Dependencies and licenses](#dependencies-and-licenses)
38
- - [Privacy](#privacy)
39
- - [Known limitations](#known-limitations)
40
- - [Tests](#tests)
41
-
42
- ## Features
43
-
44
- - **Guided face capture** on Android and iOS, with a front-camera overlay, configurable quality checks, automatic capture, and a local JPEG result.
45
- - **Barcode scanning** from the live camera or an existing image on Android and iOS, supporting all available barcode formats, configurable format filters, scan-region enforcement, and consecutive-frame confirmation.
46
- - **Text recognition v2** for extracting Latin-script text from local images on Android and iOS.
47
- - **Document scanning** on Android, with page limits, scanner modes, gallery import, JPEG pages, and optional PDF output.
48
- - **Per-module Android delivery**, allowing Face Detection, Barcode Scanning, and Text Recognition to use `bundled`, `install-time`, or `first-use` independently.
49
- - **Localized native UI and capability reporting**, with automatic, English, and Spanish UI selection, customizable loading messages, and readiness checks that do not trigger a download.
50
-
51
- ## Screenshots
52
-
53
- <table>
54
- <tr>
55
- <th>Demo tour</th>
56
- <th>Face capture</th>
57
- <th>Barcode scanning</th>
58
- </tr>
59
- <tr>
60
- <td><img src="docs/screenshots/demo-tour.gif" alt="ML Kit Suite demo tour" width="260"></td>
61
- <td><img src="docs/screenshots/face-capture.png" alt="Guided face capture" width="260"></td>
62
- <td><img src="docs/screenshots/barcode-scanning.png" alt="Live barcode scanning" width="260"></td>
63
- </tr>
64
- <tr>
65
- <th>Document scanning</th>
66
- <th>Text recognition</th>
67
- <th>Capabilities</th>
68
- </tr>
69
- <tr>
70
- <td><img src="docs/screenshots/document-scanning.jpg" alt="Native document scanning result" width="260"></td>
71
- <td><img src="docs/screenshots/text-recognition.jpg" alt="Text recognition result" width="260"></td>
72
- <td><img src="docs/screenshots/capabilities.jpg" alt="Module capabilities and readiness" width="260"></td>
73
- </tr>
74
- </table>
75
-
76
- ## Compatibility
77
-
78
- The plugin requires Cordova 11 or newer. The Android implementation supports `cordova-android` 12 through 15 and therefore requires Android 7.0 / API 24 or newer. It selects a tested CameraX version from the installed `cordova-android` major version.
79
-
80
- | cordova-android | CameraX | Bundled Face | Bundled Barcode | Bundled OCR | Document Scanner | Reference toolchain |
81
- | --- | --- | --- | --- | --- | --- | --- |
82
- | 12.x | 1.2.3 | 16.1.7 | 17.2.0 | 16.0.1 | 16.0.0 | compileSdk 33 / AGP 7.4.2 / JDK 11 |
83
- | 13.x | 1.4.2 | 16.1.7 | 17.2.0 | 16.0.1 | 16.0.0 | compileSdk 34 / AGP 8.3.0 / JDK 17 |
84
- | 14.x | 1.5.2 | 16.1.7 | 17.2.0 | 16.0.1 | 16.0.0 | compileSdk 35 / AGP 8.7.3 / JDK 17 |
85
- | 15.x | 1.6.0 | 16.1.7 | 17.2.0 | 16.0.1 | 16.0.0 | compileSdk 36 / AGP 8.10.1 / JDK 17 |
86
-
87
- All Android delivery modes share the API 24 floor. Face, Barcode, and OCR can select their delivery modes independently; all default to `install-time`. The Android document-scanner UI and model are always delivered dynamically by Google Play services, require at least 1.7 GB of total RAM, and can require a first-use download. On an unsupported device, `MlKit.scanDocument()` rejects with `UNSUPPORTED_DEVICE`; the other modules remain available. See [Android model delivery](#android-model-delivery) for the delivery strategies. Unknown Cordova Android toolchains fail at build time instead of silently selecting untested versions.
88
-
89
- The current Android Google Play services clients are Face Detection `17.1.0`, Barcode Scanning `18.3.1`, and Text Recognition `19.0.1`. The iOS implementation supports `cordova-ios` 8.x and requires iOS 15.5 or newer. Face capture, live and still-image barcode detection, and Latin text recognition use Google ML Kit CocoaPods `8.0.0`. Google does not provide the ML Kit Document Scanner API for iOS; document-scanner methods reject with `UNSUPPORTED_PLATFORM` there.
90
-
91
- ## Installation
92
-
93
- > [!IMPORTANT]
94
- > Choose the Android modes in the install command. By default all three modules use the smaller Google Play services `install-time` mode. No plugin, Gradle, XML, manifest, or generated Android file needs to be edited.
95
-
96
- Run the selected command from the Cordova application root. Cordova resolves `cordova-plugin-mlkit-suite` from the public npm registry.
97
-
98
- Default — all modules use `install-time`:
99
-
100
- ```bash
101
- cordova plugin add cordova-plugin-mlkit-suite
102
- ```
103
-
104
- Apply one mode to Face, Barcode, and OCR with `ANDROID_MLKIT_MODEL_DELIVERY`:
105
-
106
- ```bash
107
- # Package every model in the application.
108
- cordova plugin add cordova-plugin-mlkit-suite \
109
- --variable ANDROID_MLKIT_MODEL_DELIVERY=bundled
110
-
111
- # Let each model download when its API is first used.
112
- cordova plugin add cordova-plugin-mlkit-suite \
113
- --variable ANDROID_MLKIT_MODEL_DELIVERY=first-use
114
- ```
115
-
116
- Override individual modules when a mixed configuration is preferable. A module-specific value has priority over the global value; `inherit` or an omitted module variable uses the global value:
117
-
118
- ```bash
119
- cordova plugin add cordova-plugin-mlkit-suite \
120
- --variable ANDROID_MLKIT_MODEL_DELIVERY=install-time \
121
- --variable ANDROID_MLKIT_FACE_MODE=bundled \
122
- --variable ANDROID_MLKIT_BARCODE_MODE=first-use
123
- ```
124
-
125
- The accepted mode values are `bundled`, `install-time`, and `first-use`. The legacy value `play-services` is also accepted as an alias for `install-time`. Available variables are:
126
-
127
- | Variable | Default | Scope |
128
- | --- | --- | --- |
129
- | `ANDROID_MLKIT_MODEL_DELIVERY` | `install-time` | Global fallback for all three modules |
130
- | `ANDROID_MLKIT_FACE_MODE` | `inherit` | Face Detection override |
131
- | `ANDROID_MLKIT_BARCODE_MODE` | `inherit` | Barcode Scanning override |
132
- | `ANDROID_MLKIT_OCR_MODE` | `inherit` | Latin Text Recognition override |
133
-
134
- For a local checkout, replace the package name with `/absolute/path/to/cordova-plugin-mlkit-suite`. Cordova persists these variables under `cordova.plugins["cordova-plugin-mlkit-suite"].variables` in the **host application's** `package.json`; the plugin reads them while Gradle configures the Android build. Do not manually edit plugin source, `plugins/`, `platforms/android/`, or the generated manifest.
135
-
136
- Delivery modes are build-time settings. To change them, remove and re-add this plugin with the new variables, then rebuild; editing `package.json` alone does not update an installed native platform.
137
-
138
- ### iOS setup
139
-
140
- On iOS, configure the deployment target and a camera-purpose string in the host application's `config.xml`:
141
-
142
- ```xml
143
- <platform name="ios">
144
- <preference name="deployment-target" value="15.5" />
145
- <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
146
- <string>Use the camera to capture a photo.</string>
147
- </edit-config>
148
- </platform>
149
- ```
150
-
151
- The plugin requests camera access. Generated images and PDFs are written to the application's private cache, so no storage or Photo Library permission is required.
152
-
153
- ## Android model delivery
154
-
155
- Face Detection, Barcode Scanning, and Latin Text Recognition independently support the two official [ML Kit model installation paths](https://developers.google.com/ml-kit/tips/installation-paths): bundled in the application or unbundled through Google Play services. The plugin exposes `install-time` and `first-use` as two download timings for the same unbundled implementation.
156
-
157
- ### Bundled vs unbundled
158
-
159
- | Feature | Unbundled — Google Play services | Bundled — application package |
160
- | --- | --- | --- |
161
- | Plugin values | `install-time` or `first-use` | `bundled` |
162
- | Implementation | The model is downloaded and managed by Google Play services. | The model is statically linked into the app at build time. |
163
- | App download | Smaller APK/AAB; only the thin client is packaged. | Larger APK/AAB because the model is packaged with the app. |
164
- | First availability | The module must finish downloading before inference can run. | Available immediately after installation. |
165
- | Offline first use | Not guaranteed. | Yes. |
166
- | Model updates | Managed independently by Google Play services. | Require updating and rebuilding the app. |
167
-
168
- Google's current size guidance for the exact Android dependency families used by this plugin is:
169
-
170
- | Module | Unbundled app-size increase | Bundled app-size increase | Official guide |
171
- | --- | ---: | ---: | --- |
172
- | Face Detection | about 0.8 MB | about 6.9 MB | [Face Detection for Android](https://developers.google.com/ml-kit/vision/face-detection/android) |
173
- | Barcode Scanning | about 0.2 MB | about 2.4 MB | [Barcode Scanning for Android](https://developers.google.com/ml-kit/vision/barcode-scanning/android) |
174
- | Latin Text Recognition | about 0.26 MB per architecture | about 4 MB per script and architecture | [Text Recognition for Android](https://developers.google.com/ml-kit/vision/text-recognition/v2/android) |
175
- | Document Scanner | about 0.3 MB; always delivered through Play services | not available | [Document Scanner for Android](https://developers.google.com/ml-kit/vision/doc-scanner/android) |
176
-
177
- These are Google's incremental estimates, not final APK sizes. Build type, compression, transitive libraries, and included CPU architectures affect the result. An AAB lets Google Play deliver only the device's required architecture, but it does not change a bundled module into an unbundled one.
178
-
179
- The plugin modes choose when each implementation becomes available:
180
-
181
- | Mode | Implementation | Download timing | Best fit |
182
- | --- | --- | --- | --- |
183
- | `install-time` (**default**) | Unbundled | Requested after installation from Google Play; completion is best effort. | Small app with proactive delivery for normal Play Store installs. |
184
- | `first-use` | Unbundled | Downloaded when the app first calls that module, unless prepared earlier. | Small app where unused modules should never download. |
185
- | `bundled` | Bundled | Included in the APK/AAB; no model download. | Immediate offline availability is more important than package size. |
186
-
187
- For `install-time`, the plugin adds the selected modules to the generated Android manifest so Google Play can request them after installation. This is best effort and may still be incomplete at first use, especially for a sideloaded APK. `first-use` skips that request and downloads a module when its API is first called. Google Play services can update managed models independently, but not the Java client libraries selected at build time.
188
-
189
- When an unbundled module is missing, the plugin shows a native spinner, waits for the download, and then continues the requested operation. Its default text follows the resolved plugin locale; set `moduleLoadingMessage` to custom text, or to an empty string for a spinner without a label:
190
-
191
- ```javascript
192
- await MlKit.recognizeText(imagePath, {
193
- moduleLoadingMessage: "Descargando reconocimiento de texto…"
194
- });
195
- ```
196
-
197
- Use `MlKit.prepareModels()` to download all unbundled modules proactively, or pass selected module names:
198
-
199
- ```javascript
200
- await MlKit.prepareModels(["barcode", "ocr"], {
201
- moduleLoadingMessage: "Preparando funciones de cámara…"
202
- });
203
- ```
204
-
205
- `prepareModels()` and `prepareDocumentScanner()` resolve with the same result shape:
206
-
207
- ```json
208
- { "status": "ready", "alreadyAvailable": true }
209
- ```
210
-
211
- `alreadyAvailable` is `true` when no download was needed and `false` when the call completed a download.
212
-
213
- `MlKit.getCapabilities()` reports each module's configured mode and readiness without starting a download. Document Scanner is separate: it is always delivered through Google Play services and can be prepared with `MlKit.prepareDocumentScanner()`.
214
-
215
- ## Localization
216
-
217
- Plugin-owned camera instructions, accessibility labels, loading text, and user-facing native errors are available in English and Spanish. Every options object accepts an optional `locale`:
218
-
219
- ```javascript
220
- await MlKit.face.capture({ locale: "es-AR" });
221
- await MlKit.barcode.scan({ locale: "auto" });
222
- ```
223
-
224
- `auto` is the default and uses the application/device language resolved natively at runtime. `en`, `es`, and BCP-47 tags such as `en-US` or `es-AR` are accepted; unsupported languages safely fall back to English. Explicit `instructionText` and `moduleLoadingMessage` values always take precedence, including an empty string.
225
-
226
- Android translations live in `src/android/res/values*/mlkit_suite_strings.xml`; iOS translations live in `src/ios/MlKitSuiteResources.bundle/*.lproj/Localizable.strings`. Add the same keys in a new platform locale directory to extend the plugin. Stable result values and error codes are never translated.
227
-
228
- The Google Document Scanner UI is owned by Google Play services and follows the locale selected by that component. Its public API has no locale override, so `locale` controls the plugin's loading and error messages around the flow but cannot force the language of Google's scanner screens.
229
-
230
- ## API overview
231
-
232
- | Method | Android | iOS | Purpose |
233
- | --- | --- | --- | --- |
234
- | `MlKit.getCapabilities()` | yes | yes | Describes modules available on the current platform and device. |
235
- | `MlKit.prepareModels(modules?, options?)` | yes | yes | Ensures all or selected Face, Barcode, and Latin OCR modules are ready; bundled modules resolve immediately. |
236
- | `MlKit.prepareDocumentScanner(options?)` | Android 7+ and 1.7 GB RAM | no | Checks and installs the on-demand Google Play services scanner component. |
237
- | `MlKit.scanDocument(options?)` | Android 7+ and 1.7 GB RAM | no | Opens Google's native document capture, crop, filter, and cleanup UI. |
238
- | `MlKit.recognizeText(imagePath, options?)` | yes | yes | Returns plain Latin-script text from a local image. |
239
- | `MlKit.scanBarcodes(imagePath, options?)` | yes | yes | Returns every matching barcode detected in a local image. |
240
- | `MlKit.barcode.scan(options?)` | yes | yes | Opens a high-resolution rear-camera barcode scanner. |
241
- | `MlKit.face.capture(options?)` | yes | yes | Opens guided front-camera face capture with automatic capture. |
242
-
243
- All methods return Promises. The plugin processes content locally and does not interpret, validate, upload, or transform decoded payloads for a particular country or document type.
244
-
245
- ## Capabilities
246
-
247
- Use `getCapabilities()` to discover platform and device support:
248
-
249
- ```javascript
250
- const capabilities = await MlKit.getCapabilities();
251
-
252
- if (capabilities.document.scan && capabilities.document.deviceSupported) {
253
- // The native document flow can be requested on this device.
254
- }
255
- ```
256
-
257
- The exact shape on a supported Android device using the default `install-time` mode is:
258
-
259
- ```json
260
- {
261
- "platform": "android",
262
- "offline": false,
263
- "modelDelivery": "play-services",
264
- "modelDownloadStrategy": "install-time",
265
- "localization": {
266
- "default": "auto",
267
- "supportedLocales": ["en", "es"],
268
- "deviceLocale": "es-AR",
269
- "resolvedLocale": "es"
270
- },
271
- "face": {
272
- "capture": true, "frontCamera": true, "autoCapture": true,
273
- "mode": "install-time", "bundled": false,
274
- "ready": true, "modelStatus": "downloaded"
275
- },
276
- "barcode": {
277
- "scan": true, "scanImage": true,
278
- "mode": "install-time", "bundled": false,
279
- "ready": true, "modelStatus": "downloaded",
280
- "resolution": "1920x1080", "confirmationFrames": 2,
281
- "formats": ["AZTEC", "CODABAR", "CODE_39", "CODE_93", "CODE_128", "DATA_MATRIX", "EAN_8", "EAN_13", "ITF", "PDF_417", "QR_CODE", "UPC_A", "UPC_E"]
282
- },
283
- "document": {
284
- "scan": true, "prepare": true, "deviceSupported": true,
285
- "minimumAndroidApi": 24, "dynamicallyDelivered": true,
286
- "mode": "play-services", "minimumTotalRamMb": 1700,
287
- "resultFormats": ["JPEG", "PDF"],
288
- "ready": true, "modelStatus": "downloaded"
289
- },
290
- "text": {
291
- "recognize": true, "mode": "install-time", "bundled": false,
292
- "ready": true, "modelStatus": "downloaded", "scripts": ["LATIN"]
293
- }
294
- }
295
- ```
296
-
297
- Locale and readiness values vary at runtime. A read-only Google Play services check supplies `ready` (`true`, `false`, or `null`) and `modelStatus` (`bundled`, `downloaded`, `not-ready`, `unsupported`, or `unknown`) without starting a download. Root delivery fields report `mixed` when module modes differ. Android reports `offline: false` because Document Scanner is dynamically delivered. iOS reports bundled Face, Barcode, and Text modules with `offline: true`; its `document` object reports `scan: false`, `prepare: false`, `mode: "unsupported"`, `ready: false`, and `modelStatus: "unsupported"`.
298
-
299
- ## Document scanning
300
-
301
- `MlKit.scanDocument(options?)` opens the [Google ML Kit Document Scanner](https://developers.google.com/ml-kit/vision/doc-scanner) on Android. The native flow handles capture, automatic edge detection, crop adjustment, rotation, cleanup, filters, and optional gallery import.
302
-
303
- The scanner UI, logic, and models are an on-demand Google Play services component. `scanDocument()` now prepares that component automatically and shows the native loading indicator only when a download is required. Call `prepareDocumentScanner()` earlier only when proactive preparation better suits the application flow:
304
-
305
- ```javascript
306
- await MlKit.prepareDocumentScanner({
307
- moduleLoadingMessage: "Preparando el escáner…"
308
- });
309
- ```
310
-
311
- ```javascript
312
- const result = await MlKit.scanDocument({
313
- pageLimit: 4,
314
- galleryImportAllowed: true,
315
- resultFormats: ["JPEG", "PDF"],
316
- scannerMode: "FULL",
317
- moduleLoadingMessage: "Preparando el escáner…"
318
- });
319
-
320
- if (result.status === "success") {
321
- document.querySelector("img").src = result.pages[0].imageUri;
322
- console.log(result.pdf && result.pdf.path);
323
- }
324
- ```
325
-
326
- The method performs device checks before requesting the Google scanner. Android versions below API 24 and devices with less than 1.7 GB of total RAM reject with `UNSUPPORTED_DEVICE` instead of opening native scanner code.
327
-
328
- ### Document options
329
-
330
- | Option | Type | Default | Description |
331
- | --- | --- | --- | --- |
332
- | `pageLimit` | integer `1–100` | `1` | Maximum number of pages in one scan. |
333
- | `galleryImportAllowed` | `boolean` | `true` | Allows importing document images from the gallery. |
334
- | `resultFormats` | `string[] \| string \| null` | `["JPEG", "PDF"]` | Requests JPEG pages, a PDF, or both. A comma-separated string is accepted. `null` uses the default; `JPG` aliases `JPEG`. |
335
- | `scannerMode` | `"BASE" \| "BASE_WITH_FILTER" \| "FULL" \| null` | `"FULL"` | Selects the native editing feature set. `null` uses the default. `BASE` still includes crop, rotation, page removal, and page reordering. |
336
- | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading the scanner component. Empty string shows only the spinner. |
337
- | `locale` | `string` | `"auto"` | Plugin loading/error locale. Google's scanner UI resolves its own locale. |
338
-
339
- Unknown options, invalid types, empty result formats, and out-of-range values reject with `INVALID_ARGUMENT`.
340
-
341
- ### Document result
342
-
343
- ```json
344
- {
345
- "status": "success",
346
- "pages": [
347
- {
348
- "imagePath": "/data/user/0/<app-id>/cache/mlkit-suite/document-...-page-1.jpg",
349
- "imageUri": "https://localhost/_mlkit_suite_image_/document-...-page-1.jpg",
350
- "mimeType": "image/jpeg",
351
- "bytes": 182734,
352
- "width": 1440,
353
- "height": 1920
354
- }
355
- ],
356
- "pdf": {
357
- "path": "/data/user/0/<app-id>/cache/mlkit-suite/document-....pdf",
358
- "uri": "https://localhost/_mlkit_suite_document_/document-....pdf",
359
- "mimeType": "application/pdf",
360
- "bytes": 251809,
361
- "pageCount": 1
362
- }
363
- }
364
- ```
365
-
366
- `pages` is empty when JPEG was not requested. `pdf` is `null` when PDF was not requested. Use `imageUri` for an `<img>` inside the Cordova WebView; its scheme and hostname follow the host application's Cordova configuration. Native paths are intended for APIs such as `recognizeText()` and `scanBarcodes()`.
367
-
368
- ## Text recognition
369
-
370
- `MlKit.recognizeText(imagePath, options?)` runs [ML Kit Text Recognition v2](https://developers.google.com/ml-kit/vision/text-recognition/v2) for the Latin script against a local image and resolves to one plain string. Android uses the selected delivery mode; iOS always bundles the recognizer. An image containing no recognized text resolves to `""`.
371
-
372
- ```javascript
373
- const text = await MlKit.recognizeText(result.pages[0].imagePath, {
374
- moduleLoadingMessage: "Cargando reconocimiento de texto…"
375
- });
376
- console.log(text);
377
- ```
378
-
379
- `imagePath` can be an absolute native file path, a `file:` URI, an Android `content:` URI, or an `imageUri` previously returned by this plugin. Remote HTTP images are not fetched.
380
-
381
- ## Barcode detection in an image
382
-
383
- `MlKit.scanBarcodes(imagePath, options?)` uses [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning) to analyze one existing local image and resolves to an array. It does not require the camera UI or two-frame confirmation.
384
-
385
- ```javascript
386
- const barcodes = await MlKit.scanBarcodes(imagePath, {
387
- formats: ["PDF_417", "QR_CODE"],
388
- moduleLoadingMessage: "Cargando detector de códigos…"
389
- });
390
-
391
- for (const barcode of barcodes) {
392
- console.log(barcode.text, barcode.format);
393
- }
394
- ```
395
-
396
- Omit `formats` to enable every supported format. `moduleLoadingMessage` follows the common native-indicator rules above. Each detected value includes `text`, `format`, numeric `valueType`, `boundingBox`, and `cornerPoints`. An image containing no matching barcode resolves to `[]`.
397
-
398
- ```json
399
- [
400
- {
401
- "text": "https://example.com",
402
- "format": "QR_CODE",
403
- "valueType": 8,
404
- "boundingBox": { "x": 120, "y": 84, "width": 420, "height": 418 },
405
- "cornerPoints": [
406
- { "x": 120, "y": 84 },
407
- { "x": 540, "y": 84 },
408
- { "x": 540, "y": 502 },
409
- { "x": 120, "y": 502 }
410
- ]
411
- }
412
- ]
413
- ```
414
-
415
- `boundingBox` can be `null`; `cornerPoints` can be empty. Coordinates use the source image's pixel coordinate system. `valueType` is the platform ML Kit enum value, and `format` is one of the names below or `UNKNOWN` if the native SDK reports an unrecognized value.
416
-
417
- ## Camera barcode scanning
418
-
419
- `MlKit.barcode.scan(options?)` combines [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning) with the plugin's own full-screen rear-camera UI on Android and iOS:
420
-
421
- ```javascript
422
- const result = await MlKit.barcode.scan({
423
- formats: ["PDF_417", "QR_CODE"],
424
- showCloseButton: true,
425
- showTorchButton: true,
426
- instructionText: "Place a barcode inside the frame",
427
- guideColor: null,
428
- moduleLoadingMessage: "Cargando detector de códigos…",
429
- timeoutMs: 45000
430
- });
431
-
432
- if (result.status === "success") {
433
- console.log(result.text, result.format);
434
- }
435
- ```
436
-
437
- ```json
438
- { "status": "success", "text": "https://example.com", "format": "QR_CODE" }
439
- ```
440
-
441
- Android requests `1920×1080` analysis frames from CameraX; iOS requests the equivalent AVFoundation `hd1920x1080` preset. Each platform falls back to the closest suitable high-resolution stream supported by the camera. The four-color corner guide is the active scan region: codes outside it or crossing its edges are ignored. A result is returned only after two consecutive analyzed frames contain exactly the same non-empty text and format while fully inside the guide. A missing, outside, or different value resets confirmation.
442
-
443
- ### Barcode formats
444
-
445
- Both barcode methods support all 13 formats exposed by ML Kit Barcode Scanning:
446
-
447
- - `AZTEC`
448
- - `CODABAR`
449
- - `CODE_39`
450
- - `CODE_93`
451
- - `CODE_128`
452
- - `DATA_MATRIX`
453
- - `EAN_8`
454
- - `EAN_13`
455
- - `ITF`
456
- - `PDF_417`
457
- - `QR_CODE`
458
- - `UPC_A`
459
- - `UPC_E`
460
-
461
- `PDF417` is accepted as an alias of `PDF_417`. A comma-separated string is accepted in addition to an array.
462
-
463
- ### Camera barcode options
464
-
465
- | Option | Type | Default | Description |
466
- | --- | --- | --- | --- |
467
- | `formats` | `string[] \| string \| null` | all formats | Formats to detect. Restricting formats generally improves speed. |
468
- | `showCloseButton` | `boolean` | `true` | Shows the native close button. Android Back still cancels when hidden. |
469
- | `showTorchButton` | `boolean` | `true` | Shows a torch toggle when the rear camera has flash hardware. |
470
- | `instructionText` | `string \| null` | `"Place a barcode inside the frame"` | Text shown above the guide. An empty string hides it. |
471
- | `guideColor` | `string \| null` | `null` | Optional `#RRGGBB` color for all four guide corners. When omitted, the default blue, yellow, green, and red corners are used. A detected in-guide barcode always changes them to green. |
472
- | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading Barcode Scanning. Empty string shows only the spinner. |
473
- | `locale` | `string` | `"auto"` | Plugin UI/error locale (`en`, `es`, or BCP-47). |
474
- | `timeoutMs` | integer `0–600000` | `0` | Session timeout in milliseconds. `0` disables it. |
475
-
476
- ## Face capture
477
-
478
- `MlKit.face.capture(options?)` combines [ML Kit Face Detection](https://developers.google.com/ml-kit/vision/face-detection) with a full-screen native front-camera guide and resolves after a valid JPEG is captured:
479
-
480
- ```javascript
481
- const result = await MlKit.face.capture({
482
- outputWidth: 600,
483
- outputHeight: 720,
484
- jpegQuality: 95,
485
- showCloseButton: true,
486
- debugMode: false,
487
- instructionText: "Place your face inside the oval",
488
- moduleLoadingMessage: "Cargando detector facial…",
489
- checks: {
490
- singleFace: true,
491
- faceSize: true,
492
- centering: true,
493
- pose: true,
494
- eyesOpen: true,
495
- neutralExpression: true,
496
- stability: true,
497
- sharpness: true,
498
- brightness: true
499
- },
500
- timeoutMs: 45000
501
- });
502
- ```
503
-
504
- Capture starts automatically only after all enabled checks remain acceptable for a short stability window.
505
-
506
- ### Face options
507
-
508
- | Option | Type | Default | Description |
509
- | --- | --- | --- | --- |
510
- | `outputWidth` | integer `320–2048` | `600` | Exact JPEG width in pixels. |
511
- | `outputHeight` | integer `320–2048` | `720` | Exact JPEG height in pixels and crop aspect ratio. |
512
- | `jpegQuality` | integer `50–100` | `95` | JPEG encoding quality. |
513
- | `showCloseButton` | `boolean` | `true` | Shows the native close button. |
514
- | `debugMode` | `boolean` | `false` | Shows face bounds and live quality values without weakening validation. |
515
- | `instructionText` | `string \| null` | `"Place your face inside the oval"` | Text shown above the guide. An empty string hides it. |
516
- | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading Face Detection. Empty string shows only the spinner. |
517
- | `locale` | `string` | `"auto"` | Plugin UI/error locale (`en`, `es`, or BCP-47). |
518
- | `checks` | `object \| null` | all enabled | Enables or disables individual capture gates. |
519
- | `timeoutMs` | integer `0–600000` | `0` | Session timeout in milliseconds. `0` disables it. |
520
-
521
- ### Optional face checks
522
-
523
- | Check | Default | Purpose |
524
- | --- | --- | --- |
525
- | `singleFace` | `true` | Requires exactly one detected face. |
526
- | `faceSize` | `true` | Rejects faces that are too small or too close. |
527
- | `centering` | `true` | Requires the face near the guide center. |
528
- | `pose` | `true` | Limits head yaw, pitch, and roll. |
529
- | `eyesOpen` | `true` | Requires both eye-open probabilities to pass the internal threshold. |
530
- | `neutralExpression` | `true` | Rejects a high smiling probability. |
531
- | `stability` | `true` | Requires a stable face position before capture. |
532
- | `sharpness` | `true` | Rejects low-detail frames and final images. |
533
- | `brightness` | `true` | Rejects images that are too dark or too bright. |
534
-
535
- ### Face result
536
-
537
- ```json
538
- {
539
- "status": "success",
540
- "imageUri": "<cordova-scheme>://<hostname>/_mlkit_suite_image_/face-...jpg",
541
- "imagePath": "<app-cache>/mlkit-suite/face-...jpg",
542
- "width": 600,
543
- "height": 720,
544
- "mimeType": "image/jpeg",
545
- "bytes": 78421,
546
- "capturedAt": "2026-10-15T12:34:56.789Z",
547
- "quality": { "brightness": 0.57, "sharpness": 0.41 },
548
- "face": {
549
- "yaw": 1.2,
550
- "pitch": -0.8,
551
- "roll": 0.5,
552
- "leftEyeOpenProbability": 0.98,
553
- "rightEyeOpenProbability": 0.97,
554
- "smilingProbability": 0.04
555
- },
556
- "appliedChecks": {
557
- "singleFace": true,
558
- "faceSize": true,
559
- "centering": true,
560
- "pose": true,
561
- "eyesOpen": true,
562
- "neutralExpression": true,
563
- "stability": true,
564
- "sharpness": true,
565
- "brightness": true
566
- }
567
- }
568
- ```
569
-
570
- `imageUri` uses the host application's configured Cordova scheme and hostname. Cache files are temporary and may be removed by the operating system.
571
-
572
- Eye-open and smiling probabilities can be `null` when ML Kit does not provide them for the captured face.
573
-
574
- ## Cancellation and errors
575
-
576
- User cancellation of an interactive native UI resolves rather than rejects:
577
-
578
- ```javascript
579
- { "status": "cancelled", "reason": "user" }
580
- ```
581
-
582
- Other failures reject with a stable shape:
583
-
584
- ```json
585
- {
586
- "code": "UNSUPPORTED_DEVICE",
587
- "message": "ML Kit Document Scanner requires Android 7.0 or newer and at least 1.7 GB of total RAM.",
588
- "recoverable": false
589
- }
590
- ```
591
-
592
- | Code | Meaning |
593
- | --- | --- |
594
- | `INVALID_ARGUMENT` | An argument, option, or check is invalid. |
595
- | `INVALID_IMAGE` | The supplied local image cannot be opened or decoded. |
596
- | `BUSY` | Another interactive native session is active. |
597
- | `CAMERA_PERMISSION_DENIED` | Camera permission is unavailable. |
598
- | `CAMERA_UNAVAILABLE` | The requested camera or pipeline could not be opened. |
599
- | `CAPTURE_FAILED` | No acceptable final face photo was obtained. |
600
- | `PROCESSING_FAILED` | ML Kit or output persistence could not complete. |
601
- | `SCANNER_UNAVAILABLE` | The document scanner could not be downloaded, initialized, or opened. |
602
- | `MODULE_DOWNLOAD_CANCELLED` | Google Play services cancelled a requested model or scanner-component installation. |
603
- | `MODULE_DOWNLOAD_FAILED` | Google Play services could not check or install the requested modules. |
604
- | `UNSUPPORTED_DEVICE` | The current Android version, memory, or Google implementation does not support the document scanner. |
605
- | `UNSUPPORTED_PLATFORM` | The requested module is not implemented on the current platform. |
606
- | `TIMEOUT` | The configured interactive-session timeout elapsed. |
607
- | `UNKNOWN` | The JavaScript bridge received an unstructured or otherwise unknown native error. |
608
-
609
- ## Dependencies and licenses
610
-
611
- The plugin uses Google ML Kit, [AndroidX CameraX](https://developer.android.com/media/camera/camerax), and Apple system frameworks including AVFoundation. Plugin source is licensed under the MIT License; platform dependencies retain their own licenses and terms.
612
-
613
- ## Privacy
614
-
615
- The plugin requests camera permission and processes camera frames and selected local images. It does not include analytics, advertising, network upload code, face recognition, or persistent biometric storage. The document scanner is provided dynamically through Google Play services. The host application controls returned files and is responsible for privacy disclosures, consent, retention, transport security, and regulatory compliance.
616
-
617
- The included Apple privacy manifest declares no tracking, collected data, or required-reason API use by the plugin itself. Host applications and transitive SDKs must provide their own accurate manifests.
618
-
619
- ## Known limitations
620
-
621
- - The document scanner is Android-only, requires API 24 and at least 1.7 GB RAM, depends on Google Play services, and can require a first-use component download.
622
- - The Google Document Scanner always allows basic page editing, including crop and rotation; its native UI cannot be configured as an immutable front/back capture flow.
623
- - Text recognition currently includes only the Latin script recognizer.
624
- - Face detection is not face recognition, identity verification, liveness detection, or presentation-attack detection.
625
- - Static-image APIs accept local resources only and do not download remote URLs.
626
- - Face quality thresholds are heuristics and require calibration across devices, skin tones, lighting, optics, and accessibility needs.
627
- - The face oval is a visual guide; the returned JPEG is rectangular.
628
- - Cache URLs are session-local and are not durable storage.
629
- - A physical device is required to validate camera and document-scanner behavior.
630
- - The iOS implementation must be compiled and device-tested on macOS with a compatible Xcode and CocoaPods toolchain.
631
-
632
- ## Tests
633
-
634
- Run the JavaScript, Java confirmation, iOS source-contract, and localization-resource tests:
635
-
636
- ```bash
637
- npm test
638
- ```
639
-
640
- Before release, validate installation and a debug build with each supported Cordova Android major. Build iOS with `cordova-ios` 8.x on macOS, then test the native flows on physical devices.
1
+ ![npm downloads](https://img.shields.io/npm/dt/cordova-plugin-mlkit-suite) ![npm version](https://img.shields.io/npm/v/cordova-plugin-mlkit-suite) ![GitHub package.json version](https://img.shields.io/github/package-json/v/andreszs/cordova-plugin-mlkit-suite?color=FF6D00&label=main&logo=github) [![Android and iOS demo](https://img.shields.io/badge/demo-Android%20%7C%20iOS-2ea44f?logo=apachecordova&logoColor=white)](https://github.com/andreszs/cordova-plugin-demos/tree/main/com.andreszs.mlkit.suite.demo) ![GitHub license](https://img.shields.io/github/license/andreszs/cordova-plugin-mlkit-suite)
2
+
3
+ <img src="docs/mlkit-logo.svg" alt="ML Kit logo" width="64" align="right">
4
+
5
+ # cordova-plugin-mlkit-suite
6
+
7
+ Extensible [Google ML Kit](https://developers.google.com/ml-kit) integration for Cordova on Android and iOS. It provides guided face capture, live and still-image barcode scanning, image labeling, selfie and subject segmentation, Latin text recognition, and the native Android document scanner.
8
+
9
+ > [!WARNING]
10
+ > This plugin is in active development. Its API, defaults, quality thresholds, and native behavior may change before a stable release. Validate every required workflow on representative physical devices before using it in production.
11
+
12
+ ## Table of contents
13
+
14
+ - [Overview](#overview)
15
+ - [Screenshots](#screenshots)
16
+ - [Compatibility](#compatibility)
17
+ - [Quick start](#quick-start)
18
+ - [Android model delivery](#android-model-delivery)
19
+ - [Localization](#localization)
20
+ - [API](#api)
21
+ - [Cancellation and errors](#cancellation-and-errors)
22
+ - [Privacy and limitations](#privacy-and-limitations)
23
+ - [Tests](#tests)
24
+
25
+ ## Overview
26
+
27
+ - **Guided face capture** on Android and iOS, with a front-camera overlay, configurable quality checks, automatic capture, and a local JPEG result.
28
+ - **Barcode scanning** from the live camera or an existing image on Android and iOS, supporting all available barcode formats, configurable format filters, scan-region enforcement, consecutive-frame confirmation, animated success feedback, and an optional confirmation beep.
29
+ - **Text recognition v2** for extracting Latin-script text from local images on Android and iOS.
30
+ - **Document scanning** on Android, with page limits, scanner modes, gallery import, JPEG pages, and optional PDF output.
31
+ - **Image labeling** for classifying local images with ML Kit's general-purpose default model on Android and iOS.
32
+ - **Selfie segmentation** for separating people from the background and producing a transparent PNG on Android and iOS.
33
+ - **Subject segmentation** for separating foreground people, pets, and objects from the background on Android.
34
+
35
+ | Module | Android | iOS |
36
+ | --- | --- | --- |
37
+ | [Barcode scanning](#barcode-scanning) | ✅ Live camera and still images | ✅ Live camera and still images |
38
+ | [Document scanner](#document-scanning) | ✅ Available | ❌ Not available |
39
+ | [Face detection](#face-capture) | ✅ Guided face capture | ✅ Guided face capture |
40
+ | [Image labeling](#image-labeling) | ✅ Default model | ✅ Default model |
41
+ | [Selfie segmentation](#selfie-segmentation) | ✅ Transparent PNG | ✅ Transparent PNG |
42
+ | [Subject segmentation](#subject-segmentation) | ✅ Transparent PNG | ❌ Not available |
43
+ | [Text recognition v2](#text-recognition-v2) | ✅ Latin script | ✅ Latin script |
44
+
45
+ Android model delivery is configurable per module. Native plugin UI and errors support automatic, English, and Spanish localization.
46
+
47
+ ## Screenshots
48
+
49
+ <table>
50
+ <tr>
51
+ <th>Face capture<br><sub>Android shown</sub></th>
52
+ <th>Live barcode<br><sub>Android shown</sub></th>
53
+ <th>Document scanner<br><sub>Android only</sub></th>
54
+ <th>Text recognition<br><sub>iOS shown</sub></th>
55
+ </tr>
56
+ <tr>
57
+ <td><img src="docs/screenshots/face-capture.png" alt="Guided face capture on Android" width="205"></td>
58
+ <td><img src="docs/screenshots/barcode-scanning.png" alt="Live barcode scanning on Android" width="205"></td>
59
+ <td><img src="docs/screenshots/document-scanning.jpg" alt="Native document scanner result on Android" width="205"></td>
60
+ <td><img src="docs/screenshots/ios-text-recognition.jpg" alt="Text recognition from a rotated gallery image on iOS" width="205"></td>
61
+ </tr>
62
+ </table>
63
+
64
+ ## Compatibility
65
+
66
+ The plugin requires Cordova 11 or newer.
67
+
68
+ | Platform | Supported Cordova platform | Minimum OS |
69
+ | --- | --- | --- |
70
+ | Android | `cordova-android` 12.x–15.x | Android 7.0 / API 24 |
71
+ | iOS | `cordova-ios` 8.x | iOS 15.5 |
72
+
73
+ ### Android compatibility notes
74
+
75
+ Document Scanner on Android additionally requires Google Play services and at least 1.7 GB of total RAM.
76
+
77
+ The Android implementation selects a tested CameraX version for each supported `cordova-android` major:
78
+
79
+ | cordova-android | CameraX | Reference toolchain |
80
+ | --- | --- | --- |
81
+ | 12.x | 1.2.3 | compileSdk 33 / AGP 7.4.2 / JDK 11 |
82
+ | 13.x | 1.4.2 | compileSdk 34 / AGP 8.3.0 / JDK 17 |
83
+ | 14.x | 1.5.2 | compileSdk 35 / AGP 8.7.3 / JDK 17 |
84
+ | 15.x | 1.6.0 | compileSdk 36 / AGP 8.10.1 / JDK 17 |
85
+
86
+ Unknown `cordova-android` majors fail at build time instead of silently selecting untested dependencies. On an unsupported Android device, `MlKit.scanDocument()` rejects with `UNSUPPORTED_DEVICE` without affecting the other modules.
87
+
88
+ Model packaging and download behavior are documented separately under [Android model delivery](#android-model-delivery).
89
+
90
+ ## Quick start
91
+
92
+ Install the plugin from the Cordova application root:
93
+
94
+ ```bash
95
+ cordova plugin add cordova-plugin-mlkit-suite
96
+ ```
97
+
98
+ All methods return Promises. For example, open the native barcode scanner and inspect the result:
99
+
100
+ ```javascript
101
+ const result = await MlKit.barcode.scan({
102
+ formats: ["QR_CODE", "PDF_417"]
103
+ });
104
+
105
+ if (result.status === "success") {
106
+ console.log(result.text, result.format);
107
+ }
108
+ ```
109
+
110
+ On Android, Face Detection, Barcode Scanning, Image Labeling, OCR, and Subject Segmentation default to the smaller Google Play services `install-time` strategy. Selfie Segmentation is always bundled by ML Kit. See [Android model delivery](#android-model-delivery) to bundle configurable models or defer supported downloads.
111
+
112
+ ### iOS setup
113
+
114
+ On iOS, configure the deployment target and a camera-purpose string in the host application's `config.xml`:
115
+
116
+ ```xml
117
+ <platform name="ios">
118
+ <preference name="deployment-target" value="15.5" />
119
+ <edit-config file="*-Info.plist" mode="merge" target="NSCameraUsageDescription">
120
+ <string>Use the camera to capture a photo.</string>
121
+ </edit-config>
122
+ </platform>
123
+ ```
124
+
125
+ The plugin requests camera access. Generated images are written to the application's private cache, so no storage or Photo Library permission is required.
126
+
127
+ ## Android model delivery
128
+
129
+ Face Detection, Barcode Scanning, Image Labeling, and Latin Text Recognition can independently use the official ML Kit [bundled or unbundled installation paths](https://developers.google.com/ml-kit/tips/installation-paths). Subject Segmentation is always unbundled through Google Play services but can use `install-time` or `first-use`. Document Scanner is always delivered dynamically by Google Play services. Selfie Segmentation only supports ML Kit's bundled installation path.
130
+
131
+ | Mode | Implementation | Download timing | Best fit |
132
+ | --- | --- | --- | --- |
133
+ | `install-time` (**default**) | Unbundled | Requested after installation from Google Play; completion is best effort. | Small app with proactive delivery for normal Play Store installs. |
134
+ | `first-use` | Unbundled | Downloaded when the app first calls that module, unless prepared earlier. | Small app where unused modules should never download. |
135
+ | `bundled` | Bundled | Included in the APK/AAB; no model download. | Immediate offline availability is more important than package size. |
136
+
137
+ Apply one mode as the global fallback during installation:
138
+
139
+ ```bash
140
+ cordova plugin add cordova-plugin-mlkit-suite \
141
+ --variable ANDROID_MLKIT_MODEL_DELIVERY=bundled
142
+ ```
143
+
144
+ Module-specific variables override the global value; `inherit` uses the global setting:
145
+
146
+ | Module | Installation variable | Valid values |
147
+ | --- | --- | --- |
148
+ | Global fallback | `ANDROID_MLKIT_MODEL_DELIVERY` | `bundled`, `install-time`, `first-use`, `play-services` |
149
+ | Face Detection | `ANDROID_MLKIT_FACE_MODE` | `inherit`, `bundled`, `install-time`, `first-use`, `play-services` |
150
+ | Barcode Scanning | `ANDROID_MLKIT_BARCODE_MODE` | `inherit`, `bundled`, `install-time`, `first-use`, `play-services` |
151
+ | Image Labeling | `ANDROID_MLKIT_IMAGE_LABELING_MODE` | `inherit`, `bundled`, `install-time`, `first-use`, `play-services` |
152
+ | Latin Text Recognition | `ANDROID_MLKIT_OCR_MODE` | `inherit`, `bundled`, `install-time`, `first-use`, `play-services` |
153
+ | Selfie Segmentation | None | `bundled` only |
154
+ | Subject Segmentation | `ANDROID_MLKIT_SUBJECT_SEGMENTATION_MODE` | `inherit`, `install-time`, `first-use`, `play-services` |
155
+ | Document Scanner | None | Google Play services only |
156
+
157
+ The global fallback defaults to `install-time`; module variables default to `inherit`. `play-services` aliases `install-time`. Subject Segmentation resolves inherited `bundled` to `install-time`.
158
+
159
+ These are build-time settings, so remove and re-add the plugin to change them. Do not edit generated Gradle, manifest, `plugins/`, or `platforms/android/` files.
160
+
161
+ When an unbundled module is missing, the plugin shows a native spinner, waits for the download, and then continues the requested operation. Its default text follows the resolved plugin locale; set `moduleLoadingMessage` to custom text, or to an empty string for a spinner without a label:
162
+
163
+ ```javascript
164
+ await MlKit.recognizeText(imagePath, {
165
+ moduleLoadingMessage: "Downloading text recognition…"
166
+ });
167
+ ```
168
+
169
+ Use `prepareModels()` to download selected modules proactively:
170
+
171
+ ```javascript
172
+ await MlKit.prepareModels(["barcode", "image-labeling", "ocr"], {
173
+ moduleLoadingMessage: "Preparing ML Kit models…"
174
+ });
175
+ ```
176
+
177
+ Preparation resolves with `{ "status": "ready", "alreadyAvailable": true }`; `alreadyAvailable` is `false` when the call completed a download. `getCapabilities()` reports configured modes and readiness without starting one.
178
+
179
+ ## Localization
180
+
181
+ Plugin-owned camera instructions, accessibility labels, loading text, and user-facing native errors are available in English and Spanish. Every options object accepts an optional `locale`:
182
+
183
+ ```javascript
184
+ await MlKit.face.capture({ locale: "es-AR" });
185
+ await MlKit.barcode.scan({ locale: "auto" });
186
+ ```
187
+
188
+ `auto` is the default and uses the application/device language resolved natively at runtime. `en`, `es`, and BCP-47 tags such as `en-US` or `es-AR` are accepted; unsupported languages safely fall back to English. Explicit `instructionText` and `moduleLoadingMessage` values always take precedence, including an empty string.
189
+
190
+ Android translations live in `src/android/res/values*/mlkit_suite_strings.xml`; iOS translations live in `src/ios/MlKitSuiteResources.bundle/*.lproj/Localizable.strings`. Add the same keys in a new platform locale directory to extend the plugin. Stable result values and error codes are never translated.
191
+
192
+ The Google Document Scanner UI is owned by Google Play services and follows the locale selected by that component. Its public API has no locale override, so `locale` controls the plugin's loading and error messages around the flow but cannot force the language of Google's scanner screens.
193
+
194
+ ## API
195
+
196
+ | Method | Android | iOS | Purpose |
197
+ | --- | --- | --- | --- |
198
+ | `MlKit.getCapabilities()` | ✅ | ✅ | Describes modules available on the current platform and device. |
199
+ | `MlKit.prepareModels(modules?, options?)` | ✅ | ✅ | Ensures all or selected Face, Barcode, Image Labeling, Latin OCR, and Subject Segmentation modules are ready; bundled modules resolve immediately. |
200
+ | `MlKit.prepareDocumentScanner(options?)` | ✅ | ❌ | Checks and installs the on-demand Google Play services scanner component. |
201
+ | `MlKit.scanDocument(options?)` | ✅ | ❌ | Opens Google's native document capture, crop, filter, and cleanup UI. |
202
+ | `MlKit.labelImage(imagePath, options?)` | ✅ | ✅ | Returns labels from ML Kit's default image-labeling model. |
203
+ | `MlKit.segmentSelfie(imagePath, options?)` | ✅ | ✅ | Creates a transparent PNG containing the people found in a local image. |
204
+ | `MlKit.segmentSubjects(imagePath, options?)` | ✅ | ❌ | Creates a transparent PNG containing foreground subjects from a local image. |
205
+ | `MlKit.recognizeText(imagePath, options?)` | ✅ | ✅ | Returns plain Latin-script text from a local image. |
206
+ | `MlKit.scanBarcodes(imagePath, options?)` | ✅ | ✅ | Returns every matching barcode detected in a local image. |
207
+ | `MlKit.barcode.scan(options?)` | ✅ | ✅ | Opens a high-resolution rear-camera barcode scanner. |
208
+ | `MlKit.face.capture(options?)` | ✅ | ✅ | Opens guided front-camera face capture with automatic capture. |
209
+
210
+ Document Scanner requires at least 1.7 GB of total device RAM.
211
+
212
+ All methods return Promises. The plugin processes content locally and does not interpret, validate, upload, or transform decoded payloads for a particular country or document type.
213
+
214
+ ### Capabilities
215
+
216
+ Use `getCapabilities()` to discover platform and device support:
217
+
218
+ ```javascript
219
+ const capabilities = await MlKit.getCapabilities();
220
+
221
+ if (capabilities.document.scan && capabilities.document.deviceSupported) {
222
+ // The native document flow can be requested on this device.
223
+ }
224
+ ```
225
+
226
+ The result contains `platform`, localization details, and `face`, `barcode`, `document`, `imageLabeling`, `selfieSegmentation`, `subjectSegmentation`, and `text` capability objects. Each module reports its configured `mode`, `ready` state, and `modelStatus` without starting a download. Android can report `mixed` when configurable module modes differ. Selfie Segmentation is bundled on both platforms; Subject Segmentation is an unbundled Android module. On iOS, Subject Segmentation and Document Scanner report `unsupported`.
227
+
228
+ ### Document scanning
229
+
230
+ `MlKit.scanDocument(options?)` opens the [Google ML Kit Document Scanner](https://developers.google.com/ml-kit/vision/doc-scanner) on Android. The native flow handles capture, automatic edge detection, crop adjustment, rotation, cleanup, filters, and optional gallery import.
231
+
232
+ The scanner UI, logic, and models are an on-demand Google Play services component. `scanDocument()` now prepares that component automatically and shows the native loading indicator only when a download is required. Call `prepareDocumentScanner()` earlier only when proactive preparation better suits the application flow:
233
+
234
+ ```javascript
235
+ await MlKit.prepareDocumentScanner({
236
+ moduleLoadingMessage: "Preparing document scanner…"
237
+ });
238
+ ```
239
+
240
+ ```javascript
241
+ const result = await MlKit.scanDocument({
242
+ pageLimit: 4,
243
+ galleryImportAllowed: true,
244
+ resultFormats: ["JPEG", "PDF"],
245
+ scannerMode: "FULL",
246
+ moduleLoadingMessage: "Preparing document scanner…"
247
+ });
248
+
249
+ if (result.status === "success") {
250
+ document.querySelector("img").src = result.pages[0].imageUri;
251
+ console.log(result.pdf && result.pdf.path);
252
+ }
253
+ ```
254
+
255
+ The method performs device checks before requesting the Google scanner. Android versions below API 24 and devices with less than 1.7 GB of total RAM reject with `UNSUPPORTED_DEVICE` instead of opening native scanner code.
256
+
257
+ #### Document options
258
+
259
+ | Option | Type | Default | Description |
260
+ | --- | --- | --- | --- |
261
+ | `pageLimit` | integer `1–100` | `1` | Maximum number of pages in one scan. |
262
+ | `galleryImportAllowed` | `boolean` | `true` | Allows importing document images from the gallery. |
263
+ | `resultFormats` | `string[] \| string \| null` | `["JPEG", "PDF"]` | Requests JPEG pages, a PDF, or both. A comma-separated string is accepted. `null` uses the default; `JPG` aliases `JPEG`. |
264
+ | `scannerMode` | `"BASE" \| "BASE_WITH_FILTER" \| "FULL" \| null` | `"FULL"` | Selects the native editing feature set. `null` uses the default. `BASE` still includes crop, rotation, page removal, and page reordering. |
265
+ | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading the scanner component. Empty string shows only the spinner. |
266
+ | `locale` | `string` | `"auto"` | Plugin loading/error locale. Google's scanner UI resolves its own locale. |
267
+
268
+ Unknown options, invalid types, empty result formats, and out-of-range values reject with `INVALID_ARGUMENT`.
269
+
270
+ #### Document result
271
+
272
+ Successful results contain `status`, a `pages` array, and an optional `pdf` object. Each page reports `imagePath`, WebView-safe `imageUri`, MIME type, byte size, width, and height. The PDF reports its native `path`, WebView-safe `uri`, MIME type, byte size, and page count.
273
+
274
+ `pages` is empty when JPEG was not requested and `pdf` is `null` when PDF was not requested. Use `imageUri` inside the Cordova WebView and native paths with APIs such as `recognizeText()`, `scanBarcodes()`, `labelImage()`, `segmentSelfie()`, and `segmentSubjects()`.
275
+
276
+ ### Image labeling
277
+
278
+ `MlKit.labelImage(imagePath, options?)` uses [ML Kit Image Labeling](https://developers.google.com/ml-kit/vision/image-labeling) to classify one local image with the general-purpose default model. It resolves to an array of labels ordered by the native SDK; an image with no matching labels resolves to `[]`.
279
+
280
+ ```javascript
281
+ const labels = await MlKit.labelImage(imagePath, {
282
+ confidenceThreshold: 0.6,
283
+ moduleLoadingMessage: "Loading image labeling…"
284
+ });
285
+
286
+ for (const label of labels) {
287
+ console.log(label.text, label.confidence, label.index);
288
+ }
289
+ ```
290
+
291
+ ```json
292
+ [
293
+ { "text": "Food", "confidence": 0.93, "index": 12 }
294
+ ]
295
+ ```
296
+
297
+ `confidenceThreshold` is an optional number from `0` to `1`; ML Kit's native default is used when omitted. Each result contains `text`, `confidence`, and the default model's `index`. Android follows the configured delivery mode; iOS bundles the model.
298
+
299
+ ### Selfie segmentation
300
+
301
+ `MlKit.segmentSelfie(imagePath, options?)` uses [ML Kit Selfie Segmentation](https://developers.google.com/ml-kit/vision/selfie-segmentation) in single-image mode. It separates every person from the background and writes a temporary PNG whose alpha channel contains ML Kit's foreground-confidence mask.
302
+
303
+ ```javascript
304
+ const result = await MlKit.segmentSelfie(imagePath, {
305
+ maxDimension: 2048
306
+ });
307
+
308
+ document.querySelector("img").src = result.imageUri;
309
+ console.log(result.imagePath, result.width, result.height, result.bytes);
310
+ ```
311
+
312
+ The successful result contains `status`, WebView-safe `imageUri`, native `imagePath`, `width`, `height`, `mimeType: "image/png"`, and `bytes`. `maxDimension` accepts an integer from `256` to `4096` and defaults to `2048`; larger inputs are proportionally reduced before processing. The model is bundled on Android and iOS, so this method never triggers a Play services download.
313
+
314
+ ### Subject segmentation
315
+
316
+ `MlKit.segmentSubjects(imagePath, options?)` uses [ML Kit Subject Segmentation](https://developers.google.com/ml-kit/vision/subject-segmentation) on Android to isolate the combined foreground from people, animals, or objects and save it as a transparent PNG.
317
+
318
+ ```javascript
319
+ const result = await MlKit.segmentSubjects(imagePath, {
320
+ maxDimension: 2048,
321
+ moduleLoadingMessage: "Loading subject segmentation…"
322
+ });
323
+
324
+ document.querySelector("img").src = result.imageUri;
325
+ ```
326
+
327
+ The result shape and `maxDimension` rules match `segmentSelfie()`. The API is Android-only and unbundled: the plugin checks the Google Play services module, shows its localized loading spinner only when required, waits for installation, and then performs inference. Use `prepareModels("subject-segmentation")` to prepare it earlier without processing an image.
328
+
329
+ ### Text recognition v2
330
+
331
+ `MlKit.recognizeText(imagePath, options?)` runs [ML Kit Text Recognition v2](https://developers.google.com/ml-kit/vision/text-recognition/v2) for the Latin script against a local image and resolves to one plain string. Android uses the selected delivery mode; iOS always bundles the recognizer. An image containing no recognized text resolves to `""`.
332
+
333
+ ```javascript
334
+ const text = await MlKit.recognizeText(result.pages[0].imagePath, {
335
+ moduleLoadingMessage: "Loading text recognition…"
336
+ });
337
+ console.log(text);
338
+ ```
339
+
340
+ `imagePath` can be an absolute native file path, a `file:` URI, an Android `content:` URI, or an `imageUri` previously returned by this plugin. Remote HTTP images are not fetched.
341
+
342
+ ### Barcode scanning
343
+
344
+ Both barcode APIs use [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning). Use still-image detection for existing local files or the native camera flow for interactive scanning.
345
+
346
+ #### Barcode detection in an image
347
+
348
+ `MlKit.scanBarcodes(imagePath, options?)` uses [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning) to analyze one existing local image and resolves to an array. It does not require the camera UI or two-frame confirmation.
349
+
350
+ ```javascript
351
+ const barcodes = await MlKit.scanBarcodes(imagePath, {
352
+ formats: ["PDF_417", "QR_CODE"],
353
+ moduleLoadingMessage: "Loading barcode detector…"
354
+ });
355
+
356
+ for (const barcode of barcodes) {
357
+ console.log(barcode.text, barcode.format);
358
+ }
359
+ ```
360
+
361
+ Omit `formats` to enable every supported format. `moduleLoadingMessage` follows the common native-indicator rules above. Each detected value includes `text`, `format`, numeric `valueType`, `boundingBox`, and `cornerPoints`. An image containing no matching barcode resolves to `[]`.
362
+
363
+ ```json
364
+ { "text": "https://example.com", "format": "QR_CODE", "valueType": 8 }
365
+ ```
366
+
367
+ `boundingBox` can be `null`; `cornerPoints` can be empty. Coordinates use the source image's pixel coordinate system. `valueType` is the platform ML Kit enum value, and `format` is one of the names below or `UNKNOWN` if the native SDK reports an unrecognized value.
368
+
369
+ #### Camera barcode scanning
370
+
371
+ `MlKit.barcode.scan(options?)` combines [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning) with the plugin's own full-screen rear-camera UI on Android and iOS:
372
+
373
+ ```javascript
374
+ const result = await MlKit.barcode.scan({
375
+ formats: ["PDF_417", "QR_CODE"],
376
+ timeoutMs: 45000
377
+ });
378
+
379
+ if (result.status === "success") {
380
+ console.log(result.text, result.format);
381
+ }
382
+ ```
383
+
384
+ ```json
385
+ { "status": "success", "text": "https://example.com", "format": "QR_CODE" }
386
+ ```
387
+
388
+ Android requests `1920×1080` analysis frames from CameraX; iOS requests the equivalent AVFoundation `hd1920x1080` preset. Each platform falls back to the closest suitable high-resolution stream supported by the camera. The four-color corner guide is the active scan region: codes outside it or crossing its edges are ignored. A result is returned only after two consecutive analyzed frames contain exactly the same non-empty text and format while fully inside the guide. A missing, outside, or different value resets confirmation. On success, the scanner freezes the confirmed camera frame and animates the guide onto the detected code before returning the result.
389
+
390
+ ##### Barcode formats
391
+
392
+ Both barcode methods support `AZTEC`, `CODABAR`, `CODE_39`, `CODE_93`, `CODE_128`, `DATA_MATRIX`, `EAN_8`, `EAN_13`, `ITF`, `PDF_417`, `QR_CODE`, `UPC_A`, and `UPC_E`. `PDF417` aliases `PDF_417`; comma-separated strings are accepted in addition to arrays.
393
+
394
+ ##### Camera barcode options
395
+
396
+ | Option | Type | Default | Description |
397
+ | --- | --- | --- | --- |
398
+ | `formats` | `string[] \| string \| null` | all formats | Formats to detect. Restricting formats generally improves speed. |
399
+ | `showCloseButton` | `boolean` | `true` | Shows the native close button. Android Back still cancels when hidden. |
400
+ | `showTorchButton` | `boolean` | `true` | Shows a torch toggle when the rear camera has flash hardware. |
401
+ | `beepEnabled` | `boolean` | `true` | Plays a short synthesized confirmation beep after a successful scan. Device silent mode is respected. |
402
+ | `instructionText` | `string \| null` | `"Place a barcode inside the frame"` | Text shown above the guide. An empty string hides it. |
403
+ | `guideColor` | `string \| null` | `null` | Optional `#RRGGBB` color for all four guide corners. When omitted, the default blue, yellow, green, and red corners are used. A detected in-guide barcode always changes them to green. |
404
+ | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading Barcode Scanning. Empty string shows only the spinner. |
405
+ | `locale` | `string` | `"auto"` | Plugin UI/error locale (`en`, `es`, or BCP-47). |
406
+ | `timeoutMs` | integer `0–600000` | `0` | Session timeout in milliseconds. `0` disables it. |
407
+
408
+ ### Face capture
409
+
410
+ `MlKit.face.capture(options?)` combines [ML Kit Face Detection](https://developers.google.com/ml-kit/vision/face-detection) with a full-screen native front-camera guide and resolves after a valid JPEG is captured:
411
+
412
+ ```javascript
413
+ const result = await MlKit.face.capture({
414
+ outputWidth: 600,
415
+ outputHeight: 720,
416
+ debugMode: false,
417
+ timeoutMs: 45000
418
+ });
419
+ ```
420
+
421
+ All checks are enabled by default. Capture starts automatically only after they remain acceptable for a short stability window.
422
+
423
+ #### Face options
424
+
425
+ | Option | Type | Default | Description |
426
+ | --- | --- | --- | --- |
427
+ | `outputWidth` | integer `320–2048` | `600` | Exact JPEG width in pixels. |
428
+ | `outputHeight` | integer `320–2048` | `720` | Exact JPEG height in pixels and crop aspect ratio. |
429
+ | `jpegQuality` | integer `50–100` | `95` | JPEG encoding quality. |
430
+ | `showCloseButton` | `boolean` | `true` | Shows the native close button. |
431
+ | `debugMode` | `boolean` | `false` | Shows face bounds and live quality values without weakening validation. |
432
+ | `instructionText` | `string \| null` | `"Place your face inside the oval"` | Text shown above the guide. An empty string hides it. |
433
+ | `moduleLoadingMessage` | `string \| null` | localized | Android text shown only while downloading Face Detection. Empty string shows only the spinner. |
434
+ | `locale` | `string` | `"auto"` | Plugin UI/error locale (`en`, `es`, or BCP-47). |
435
+ | `checks` | `object \| null` | all enabled | Enables or disables individual capture gates. |
436
+ | `timeoutMs` | integer `0–600000` | `0` | Session timeout in milliseconds. `0` disables it. |
437
+
438
+ #### Optional face checks
439
+
440
+ | Check | Default | Purpose |
441
+ | --- | --- | --- |
442
+ | `singleFace` | `true` | Requires exactly one detected face. |
443
+ | `faceSize` | `true` | Rejects faces that are too small or too close. |
444
+ | `centering` | `true` | Requires the face near the guide center. |
445
+ | `pose` | `true` | Limits head yaw, pitch, and roll. |
446
+ | `eyesOpen` | `true` | Requires both eye-open probabilities to pass the internal threshold. |
447
+ | `neutralExpression` | `true` | Rejects a high smiling probability. |
448
+ | `stability` | `true` | Requires a stable face position before capture. |
449
+ | `sharpness` | `true` | Rejects low-detail frames and final images. |
450
+ | `brightness` | `true` | Rejects images that are too dark or too bright. |
451
+
452
+ #### Face result
453
+
454
+ Successful results include the JPEG's `imagePath`, WebView-safe `imageUri`, dimensions, MIME type, byte size, and capture time. The `quality` object contains brightness and sharpness; `face` contains yaw, pitch, roll, eye-open probabilities, and smiling probability; `appliedChecks` records the checks used for capture.
455
+
456
+ Cache files are temporary. Eye-open and smiling probabilities can be `null` when ML Kit does not provide them.
457
+
458
+ ## Cancellation and errors
459
+
460
+ User cancellation of an interactive native UI resolves rather than rejects:
461
+
462
+ ```javascript
463
+ { "status": "cancelled", "reason": "user" }
464
+ ```
465
+
466
+ Other failures reject with a stable shape:
467
+
468
+ ```json
469
+ {
470
+ "code": "UNSUPPORTED_DEVICE",
471
+ "message": "ML Kit Document Scanner requires Android 7.0 or newer and at least 1.7 GB of total RAM.",
472
+ "recoverable": false
473
+ }
474
+ ```
475
+
476
+ | Code | Meaning |
477
+ | --- | --- |
478
+ | `INVALID_ARGUMENT` | An argument, option, or check is invalid. |
479
+ | `INVALID_IMAGE` | The supplied local image cannot be opened or decoded. |
480
+ | `BUSY` | Another interactive native session is active. |
481
+ | `CAMERA_PERMISSION_DENIED` | Camera permission is unavailable. |
482
+ | `CAMERA_UNAVAILABLE` | The requested camera or pipeline could not be opened. |
483
+ | `CAPTURE_FAILED` | No acceptable final face photo was obtained. |
484
+ | `PROCESSING_FAILED` | ML Kit or output persistence could not complete. |
485
+ | `SCANNER_UNAVAILABLE` | The document scanner could not be downloaded, initialized, or opened. |
486
+ | `MODULE_DOWNLOAD_CANCELLED` | Google Play services cancelled a requested model or scanner-component installation. |
487
+ | `MODULE_DOWNLOAD_FAILED` | Google Play services could not check or install the requested modules. |
488
+ | `UNSUPPORTED_DEVICE` | The current Android version, memory, or Google implementation does not support the document scanner. |
489
+ | `UNSUPPORTED_PLATFORM` | The requested module is not implemented on the current platform. |
490
+ | `TIMEOUT` | The configured interactive-session timeout elapsed. |
491
+ | `UNKNOWN` | The JavaScript bridge received an unstructured or otherwise unknown native error. |
492
+
493
+ ## Privacy and limitations
494
+
495
+ ### Dependencies and licenses
496
+
497
+ The plugin uses Google ML Kit, [AndroidX CameraX](https://developer.android.com/media/camera/camerax), and Apple system frameworks including AVFoundation. Plugin source is licensed under the MIT License; platform dependencies retain their own licenses and terms.
498
+
499
+ ### Privacy
500
+
501
+ The plugin requests camera permission and processes camera frames and selected local images. It does not include analytics, advertising, network upload code, face recognition, or persistent biometric storage. Document Scanner and Subject Segmentation are provided through Google Play services; Selfie Segmentation is statically bundled. The host application controls returned files and is responsible for privacy disclosures, consent, retention, transport security, and regulatory compliance.
502
+
503
+ The included Apple privacy manifest declares no tracking, collected data, or required-reason API use by the plugin itself. Host applications and transitive SDKs must provide their own accurate manifests.
504
+
505
+ ### Known limitations
506
+
507
+ - The Google Document Scanner always allows basic page editing, including crop and rotation; its native UI cannot be configured as an immutable front/back capture flow.
508
+ - Image labeling currently uses ML Kit's general-purpose default model; custom models are not yet supported.
509
+ - Selfie Segmentation is a beta ML Kit API. Hair, motion blur, occlusion, and low foreground/background contrast can produce imperfect edges.
510
+ - Subject Segmentation is a beta, Android-only ML Kit API and requires its Google Play services module before first inference.
511
+ - Text recognition currently includes only the Latin script recognizer.
512
+ - Face detection is not face recognition, identity verification, liveness detection, or presentation-attack detection.
513
+ - Face quality thresholds are heuristics and require calibration across devices, skin tones, lighting, optics, and accessibility needs.
514
+ - The face oval is a visual guide; the returned JPEG is rectangular.
515
+ - Cache URLs are session-local and are not durable storage.
516
+ - Camera and document-scanner behavior must be validated on representative physical devices.
517
+
518
+ ## Tests
519
+
520
+ Run the JavaScript, Java confirmation, iOS source-contract, and localization-resource tests:
521
+
522
+ ```bash
523
+ npm test
524
+ ```
525
+
526
+ Before release, validate installation and a debug build with each supported Cordova Android major. Build iOS with `cordova-ios` 8.x on macOS, then test the native flows on physical devices.