@unif/react-native-hms-scan 0.6.2 → 2.0.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.
- package/README.md +3 -2
- package/android/src/main/AndroidManifest.xml +0 -9
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanImage.kt +135 -0
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanModule.kt +163 -147
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanResultMapper.kt +17 -9
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanView.kt +57 -19
- package/android/src/main/java/com/unif/reactnativehmsscan/HmsScanViewManager.kt +1 -1
- package/ios/HmsScanModule.mm +13 -37
- package/ios/HmsScanResultMapper.h +4 -3
- package/ios/HmsScanResultMapper.mm +31 -19
- package/ios/HmsScanView.h +1 -1
- package/ios/HmsScanView.mm +140 -25
- package/lib/module/HmsScanView/HmsScanView.js +66 -0
- package/lib/module/HmsScanView/HmsScanView.js.map +1 -0
- package/lib/module/HmsScanView/HmsScanView.web.js +26 -0
- package/lib/module/HmsScanView/HmsScanView.web.js.map +1 -0
- package/lib/module/HmsScanView/types.js +4 -0
- package/lib/module/HmsScanView/types.js.map +1 -0
- package/lib/module/HmsScanViewNativeComponent.ts +7 -5
- package/lib/module/NativeHmsScan.js +1 -1
- package/lib/module/NativeHmsScan.js.map +1 -1
- package/lib/module/ScanError.js +12 -0
- package/lib/module/ScanError.js.map +1 -0
- package/lib/module/Scanner/DeniedOverlay.js +1 -1
- package/lib/module/Scanner/DeniedOverlay.js.map +1 -1
- package/lib/module/Scanner/ResultFail.js +3 -2
- package/lib/module/Scanner/ResultFail.js.map +1 -1
- package/lib/module/Scanner/ResultFocus/ResultFocus.js +56 -0
- package/lib/module/Scanner/ResultFocus/ResultFocus.js.map +1 -0
- package/lib/module/Scanner/ResultFocus/styles.js +39 -0
- package/lib/module/Scanner/ResultFocus/styles.js.map +1 -0
- package/lib/module/Scanner/ResultFocus/types.js +4 -0
- package/lib/module/Scanner/ResultFocus/types.js.map +1 -0
- package/lib/module/Scanner/Scanner.js +11 -310
- package/lib/module/Scanner/Scanner.js.map +1 -1
- package/lib/module/Scanner/Scanner.web.js +29 -0
- package/lib/module/Scanner/Scanner.web.js.map +1 -0
- package/lib/module/Scanner/ScannerInner.js +310 -0
- package/lib/module/Scanner/ScannerInner.js.map +1 -0
- package/lib/module/Scanner/styles.js +11 -0
- package/lib/module/Scanner/styles.js.map +1 -0
- package/lib/module/Scanner/types.js +4 -0
- package/lib/module/Scanner/types.js.map +1 -0
- package/lib/module/constants.js +20 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/decodeImage.js +29 -24
- package/lib/module/decodeImage.js.map +1 -1
- package/lib/module/decodeImage.web.js +7 -0
- package/lib/module/decodeImage.web.js.map +1 -0
- package/lib/module/errors.js +24 -0
- package/lib/module/errors.js.map +1 -0
- package/lib/module/format.js +53 -47
- package/lib/module/format.js.map +1 -1
- package/lib/module/index.js +5 -19
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.web.js +8 -0
- package/lib/module/index.web.js.map +1 -0
- package/lib/module/mock.js +2 -23
- package/lib/module/mock.js.map +1 -1
- package/lib/module/permissions.js +24 -20
- package/lib/module/permissions.js.map +1 -1
- package/lib/module/permissions.web.js +10 -0
- package/lib/module/permissions.web.js.map +1 -0
- package/lib/module/types.js +1 -45
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/HmsScanView/HmsScanView.d.ts +3 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.web.d.ts +3 -0
- package/lib/typescript/src/HmsScanView/HmsScanView.web.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanView/types.d.ts +12 -0
- package/lib/typescript/src/HmsScanView/types.d.ts.map +1 -0
- package/lib/typescript/src/HmsScanViewNativeComponent.d.ts +6 -4
- package/lib/typescript/src/HmsScanViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/NativeHmsScan.d.ts +3 -5
- package/lib/typescript/src/NativeHmsScan.d.ts.map +1 -1
- package/lib/typescript/src/ScanError.d.ts +7 -0
- package/lib/typescript/src/ScanError.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/DeniedOverlay.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ResultFail.d.ts +2 -1
- package/lib/typescript/src/Scanner/ResultFail.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/ResultFocus/ResultFocus.d.ts +4 -0
- package/lib/typescript/src/Scanner/ResultFocus/ResultFocus.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ResultFocus/styles.d.ts +36 -0
- package/lib/typescript/src/Scanner/ResultFocus/styles.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ResultFocus/types.d.ts +8 -0
- package/lib/typescript/src/Scanner/ResultFocus/types.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/Scanner.d.ts +2 -45
- package/lib/typescript/src/Scanner/Scanner.d.ts.map +1 -1
- package/lib/typescript/src/Scanner/Scanner.web.d.ts +3 -0
- package/lib/typescript/src/Scanner/Scanner.web.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/ScannerInner.d.ts +3 -0
- package/lib/typescript/src/Scanner/ScannerInner.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/styles.d.ts +7 -0
- package/lib/typescript/src/Scanner/styles.d.ts.map +1 -0
- package/lib/typescript/src/Scanner/types.d.ts +20 -0
- package/lib/typescript/src/Scanner/types.d.ts.map +1 -0
- package/lib/typescript/src/constants.d.ts +8 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/decodeImage.d.ts +2 -12
- package/lib/typescript/src/decodeImage.d.ts.map +1 -1
- package/lib/typescript/src/decodeImage.web.d.ts +3 -0
- package/lib/typescript/src/decodeImage.web.d.ts.map +1 -0
- package/lib/typescript/src/errors.d.ts +6 -0
- package/lib/typescript/src/errors.d.ts.map +1 -0
- package/lib/typescript/src/format.d.ts +15 -12
- package/lib/typescript/src/format.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -7
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +9 -0
- package/lib/typescript/src/index.web.d.ts.map +1 -0
- package/lib/typescript/src/mock.d.ts +9 -8
- package/lib/typescript/src/mock.d.ts.map +1 -1
- package/lib/typescript/src/permissions.d.ts +4 -8
- package/lib/typescript/src/permissions.d.ts.map +1 -1
- package/lib/typescript/src/permissions.web.d.ts +4 -0
- package/lib/typescript/src/permissions.web.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +31 -56
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +16 -9
- package/src/HmsScanView/HmsScanView.tsx +67 -0
- package/src/HmsScanView/HmsScanView.web.tsx +22 -0
- package/src/HmsScanView/types.ts +17 -0
- package/src/HmsScanViewNativeComponent.ts +7 -5
- package/src/NativeHmsScan.ts +3 -5
- package/src/ScanError.ts +13 -0
- package/src/Scanner/DeniedOverlay.tsx +4 -2
- package/src/Scanner/ResultFail.tsx +23 -5
- package/src/Scanner/ResultFocus/ResultFocus.tsx +43 -0
- package/src/Scanner/ResultFocus/styles.ts +25 -0
- package/src/Scanner/ResultFocus/types.ts +8 -0
- package/src/Scanner/Scanner.tsx +14 -432
- package/src/Scanner/Scanner.web.tsx +26 -0
- package/src/Scanner/ScannerInner.tsx +391 -0
- package/src/Scanner/styles.ts +6 -0
- package/src/Scanner/types.ts +36 -0
- package/src/constants.ts +55 -0
- package/src/decodeImage.ts +35 -29
- package/src/decodeImage.web.ts +8 -0
- package/src/errors.ts +26 -0
- package/src/format.ts +77 -76
- package/src/index.tsx +17 -32
- package/src/index.web.tsx +22 -0
- package/src/mock.ts +21 -29
- package/src/permissions.ts +33 -20
- package/src/permissions.web.ts +9 -0
- package/src/types.ts +38 -126
- package/lib/module/HmsScanView.js +0 -43
- package/lib/module/HmsScanView.js.map +0 -1
- package/lib/module/Scanner/ResultFocus.js +0 -171
- package/lib/module/Scanner/ResultFocus.js.map +0 -1
- package/lib/typescript/src/HmsScanView.d.ts +0 -33
- package/lib/typescript/src/HmsScanView.d.ts.map +0 -1
- package/lib/typescript/src/Scanner/ResultFocus.d.ts +0 -14
- package/lib/typescript/src/Scanner/ResultFocus.d.ts.map +0 -1
- package/src/HmsScanView.tsx +0 -87
- package/src/Scanner/ResultFocus.tsx +0 -122
|
@@ -2,6 +2,7 @@ package com.unif.reactnativehmsscan
|
|
|
2
2
|
|
|
3
3
|
import android.app.Activity
|
|
4
4
|
import android.content.Context
|
|
5
|
+
import android.content.pm.PackageManager
|
|
5
6
|
import android.os.Bundle
|
|
6
7
|
import android.widget.FrameLayout
|
|
7
8
|
import com.facebook.react.bridge.Arguments
|
|
@@ -42,14 +43,12 @@ class HmsScanView(
|
|
|
42
43
|
private var paused: Boolean = false
|
|
43
44
|
private var torch: Boolean = false
|
|
44
45
|
private var actualTorchOn: Boolean = false
|
|
45
|
-
private var
|
|
46
|
+
private var lowLight: Boolean? = null
|
|
46
47
|
|
|
47
48
|
// True while this view is between onAttachedToWindow and onDetachedFromWindow.
|
|
48
49
|
private var attached: Boolean = false
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
themedReactContext.addLifecycleEventListener(this)
|
|
52
|
-
}
|
|
50
|
+
private var listening: Boolean = false
|
|
51
|
+
private var lastTorchState: Triple<Boolean, Boolean, Boolean?>? = null
|
|
53
52
|
|
|
54
53
|
// ── Props (called from the ViewManager) ───────────────────────────────────
|
|
55
54
|
|
|
@@ -81,6 +80,10 @@ class HmsScanView(
|
|
|
81
80
|
override fun onAttachedToWindow() {
|
|
82
81
|
super.onAttachedToWindow()
|
|
83
82
|
attached = true
|
|
83
|
+
if (!listening) {
|
|
84
|
+
listening = true
|
|
85
|
+
themedReactContext.addLifecycleEventListener(this)
|
|
86
|
+
}
|
|
84
87
|
if (remoteView == null) {
|
|
85
88
|
buildRemoteView()
|
|
86
89
|
}
|
|
@@ -91,6 +94,7 @@ class HmsScanView(
|
|
|
91
94
|
super.onDetachedFromWindow()
|
|
92
95
|
attached = false
|
|
93
96
|
teardownRemoteView()
|
|
97
|
+
removeLifecycleListener()
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
// ── Host (Activity) lifecycle ─────────────────────────────────────────────
|
|
@@ -109,7 +113,14 @@ class HmsScanView(
|
|
|
109
113
|
|
|
110
114
|
override fun onHostDestroy() {
|
|
111
115
|
teardownRemoteView()
|
|
112
|
-
|
|
116
|
+
removeLifecycleListener()
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private fun removeLifecycleListener() {
|
|
120
|
+
if (listening) {
|
|
121
|
+
listening = false
|
|
122
|
+
themedReactContext.removeLifecycleEventListener(this)
|
|
123
|
+
}
|
|
113
124
|
}
|
|
114
125
|
|
|
115
126
|
// ── RemoteView build / teardown ───────────────────────────────────────────
|
|
@@ -142,9 +153,15 @@ class HmsScanView(
|
|
|
142
153
|
|
|
143
154
|
val view = builder.build()
|
|
144
155
|
|
|
145
|
-
view.setOnResultCallback(
|
|
156
|
+
view.setOnResultCallback(
|
|
157
|
+
OnResultCallback { result ->
|
|
158
|
+
if (remoteView === view && !paused) onScanResult(result)
|
|
159
|
+
},
|
|
160
|
+
)
|
|
146
161
|
view.setOnLightVisibleCallback(
|
|
147
|
-
OnLightVisibleCallBack { visible ->
|
|
162
|
+
OnLightVisibleCallBack { visible ->
|
|
163
|
+
if (remoteView === view) onTorchVisible(visible)
|
|
164
|
+
},
|
|
148
165
|
)
|
|
149
166
|
|
|
150
167
|
// onCreate must run after build() and before addView (per HMS docs/demo).
|
|
@@ -172,6 +189,8 @@ class HmsScanView(
|
|
|
172
189
|
|
|
173
190
|
private fun teardownRemoteView() {
|
|
174
191
|
val view = remoteView ?: return
|
|
192
|
+
// Invalidate callbacks before the SDK starts its asynchronous teardown.
|
|
193
|
+
remoteView = null
|
|
175
194
|
try {
|
|
176
195
|
view.onPause()
|
|
177
196
|
view.onStop()
|
|
@@ -180,8 +199,9 @@ class HmsScanView(
|
|
|
180
199
|
// Defensive: never let teardown crash the host.
|
|
181
200
|
}
|
|
182
201
|
removeView(view)
|
|
183
|
-
remoteView = null
|
|
184
202
|
actualTorchOn = false
|
|
203
|
+
lowLight = null
|
|
204
|
+
lastTorchState = null
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
/** Rebuild the RemoteView in place to honour a changed build-time prop. */
|
|
@@ -207,15 +227,17 @@ class HmsScanView(
|
|
|
207
227
|
|
|
208
228
|
private fun applyTorch() {
|
|
209
229
|
val view = remoteView ?: return
|
|
230
|
+
var commandFailed = false
|
|
210
231
|
try {
|
|
211
232
|
// switchLight() toggles; only flip when the current state differs from target.
|
|
212
233
|
if (view.lightStatus != torch) {
|
|
213
|
-
view.switchLight()
|
|
234
|
+
commandFailed = !view.switchLight()
|
|
214
235
|
}
|
|
215
236
|
} catch (_: Throwable) {
|
|
216
|
-
|
|
237
|
+
commandFailed = true
|
|
217
238
|
}
|
|
218
|
-
|
|
239
|
+
// A rejected command must acknowledge even an unchanged state so JS can retry.
|
|
240
|
+
emitTorchState(view, force = commandFailed)
|
|
219
241
|
}
|
|
220
242
|
|
|
221
243
|
/** Resolve the hosting Activity required by RemoteView.Builder.setContext(). */
|
|
@@ -226,8 +248,14 @@ class HmsScanView(
|
|
|
226
248
|
private fun onScanResult(result: Array<HmsScan?>?) {
|
|
227
249
|
// Drop empty callbacks (continuous mode can fire with nothing useful).
|
|
228
250
|
if (result == null || result.isEmpty()) return
|
|
229
|
-
val json =
|
|
230
|
-
|
|
251
|
+
val json =
|
|
252
|
+
try {
|
|
253
|
+
HmsScanResultMapper.toJson(result)
|
|
254
|
+
} catch (error: InvalidScanResponse) {
|
|
255
|
+
emitError("E_INVALID_RESPONSE", error.message ?: "Invalid scan result")
|
|
256
|
+
return
|
|
257
|
+
}
|
|
258
|
+
// A batch containing only null vendor entries has no result to emit.
|
|
231
259
|
if (json == "[]") return
|
|
232
260
|
emitEvent(
|
|
233
261
|
"topScanResult",
|
|
@@ -238,12 +266,15 @@ class HmsScanView(
|
|
|
238
266
|
}
|
|
239
267
|
|
|
240
268
|
private fun onTorchVisible(visible: Boolean) {
|
|
241
|
-
|
|
269
|
+
lowLight = visible
|
|
242
270
|
val view = remoteView ?: return
|
|
243
|
-
|
|
271
|
+
emitTorchState(view)
|
|
244
272
|
}
|
|
245
273
|
|
|
246
|
-
private fun
|
|
274
|
+
private fun emitTorchState(
|
|
275
|
+
view: RemoteView,
|
|
276
|
+
force: Boolean = false,
|
|
277
|
+
) {
|
|
247
278
|
val on =
|
|
248
279
|
try {
|
|
249
280
|
view.lightStatus
|
|
@@ -251,10 +282,17 @@ class HmsScanView(
|
|
|
251
282
|
actualTorchOn
|
|
252
283
|
}
|
|
253
284
|
actualTorchOn = on
|
|
285
|
+
val available = context.packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH)
|
|
286
|
+
val state = Triple(on, available, lowLight)
|
|
287
|
+
if (!force && lastTorchState == state) return
|
|
288
|
+
lastTorchState = state
|
|
254
289
|
emitEvent(
|
|
255
|
-
"
|
|
290
|
+
"topTorchState",
|
|
256
291
|
Arguments.createMap().apply {
|
|
257
|
-
putBoolean("available",
|
|
292
|
+
putBoolean("available", available)
|
|
293
|
+
putBoolean("hasAvailable", true)
|
|
294
|
+
putBoolean("lowLight", lowLight ?: false)
|
|
295
|
+
putBoolean("hasLowLight", lowLight != null)
|
|
258
296
|
putBoolean("on", on)
|
|
259
297
|
},
|
|
260
298
|
)
|
|
@@ -66,7 +66,7 @@ class HmsScanViewManager :
|
|
|
66
66
|
mapOf(
|
|
67
67
|
"topScanResult" to mapOf("registrationName" to "onScanResult"),
|
|
68
68
|
"topScanError" to mapOf("registrationName" to "onScanError"),
|
|
69
|
-
"
|
|
69
|
+
"topTorchState" to mapOf("registrationName" to "onTorchState"),
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
companion object {
|
package/ios/HmsScanModule.mm
CHANGED
|
@@ -11,18 +11,18 @@
|
|
|
11
11
|
|
|
12
12
|
#import <ScanKitFrameWork/ScanKitFrameWork.h>
|
|
13
13
|
|
|
14
|
-
//
|
|
14
|
+
// Native codes are normalized to ScanFailure at the public capability boundary.
|
|
15
15
|
static NSString *const kErrImageLoadFailed = @"E_IMAGE_LOAD_FAILED";
|
|
16
16
|
static NSString *const kErrDecodeFailed = @"E_DECODE_FAILED";
|
|
17
17
|
|
|
18
18
|
@implementation HmsScanModule
|
|
19
19
|
|
|
20
|
-
// Export under the JS-visible name "HmsScan"
|
|
20
|
+
// Export under the JS-visible TurboModule name "HmsScan".
|
|
21
21
|
RCT_EXPORT_MODULE(HmsScan)
|
|
22
22
|
|
|
23
23
|
#pragma mark - Permission status mapping
|
|
24
24
|
|
|
25
|
-
// AVAuthorizationStatus -> our
|
|
25
|
+
// AVAuthorizationStatus -> our ScanCameraPermission string.
|
|
26
26
|
// authorized -> granted; notDetermined -> undetermined; denied/restricted -> blocked.
|
|
27
27
|
+ (NSString *)stringForAuthorizationStatus:(AVAuthorizationStatus)status {
|
|
28
28
|
switch (status) {
|
|
@@ -34,7 +34,7 @@ RCT_EXPORT_MODULE(HmsScan)
|
|
|
34
34
|
case AVAuthorizationStatusRestricted:
|
|
35
35
|
return @"blocked";
|
|
36
36
|
default:
|
|
37
|
-
return @"
|
|
37
|
+
return @"unknown";
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -66,45 +66,18 @@ RCT_EXPORT_MODULE(HmsScan)
|
|
|
66
66
|
|
|
67
67
|
#pragma mark - Image loading
|
|
68
68
|
|
|
69
|
-
//
|
|
70
|
-
// or a data: URI. Remote URLs and Photos (ph:// / assets-library://) are not
|
|
71
|
-
// supported here -> returns nil so the caller rejects E_IMAGE_LOAD_FAILED.
|
|
69
|
+
// Read only the local file prepared by the caller.
|
|
72
70
|
+ (nullable UIImage *)imageForURI:(NSString *)uri {
|
|
73
71
|
if (uri.length == 0) {
|
|
74
72
|
return nil;
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
NSURL *url = [NSURL URLWithString:uri];
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
// data: URI (base64 or otherwise) -> decode bytes directly.
|
|
81
|
-
if ([scheme isEqualToString:@"data"]) {
|
|
82
|
-
NSData *data = [NSData dataWithContentsOfURL:url];
|
|
83
|
-
return data ? [UIImage imageWithData:data] : nil;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// file:// URI -> use its filesystem path.
|
|
87
|
-
if ([scheme isEqualToString:@"file"]) {
|
|
76
|
+
if (url.isFileURL) {
|
|
88
77
|
NSString *path = url.path;
|
|
89
78
|
return path ? [UIImage imageWithContentsOfFile:path] : nil;
|
|
90
79
|
}
|
|
91
80
|
|
|
92
|
-
// No scheme (or a Windows-like drive letter is irrelevant on iOS): treat the
|
|
93
|
-
// whole string as a filesystem path. Also strip a leading file path that may
|
|
94
|
-
// have arrived percent-encoded.
|
|
95
|
-
if (scheme == nil || scheme.length == 0) {
|
|
96
|
-
UIImage *image = [UIImage imageWithContentsOfFile:uri];
|
|
97
|
-
if (image == nil) {
|
|
98
|
-
NSString *decoded = [uri stringByRemovingPercentEncoding];
|
|
99
|
-
if (decoded != nil && ![decoded isEqualToString:uri]) {
|
|
100
|
-
image = [UIImage imageWithContentsOfFile:decoded];
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return image;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// ph:// , assets-library:// , http(s):// , content:// (Android-only) etc. are
|
|
107
|
-
// intentionally unsupported on iOS in this bridge.
|
|
108
81
|
return nil;
|
|
109
82
|
}
|
|
110
83
|
|
|
@@ -114,11 +87,13 @@ RCT_EXPORT_MODULE(HmsScan)
|
|
|
114
87
|
formatsCsv:(NSString *)formatsCsv
|
|
115
88
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
116
89
|
reject:(RCTPromiseRejectBlock)reject {
|
|
90
|
+
if ([HmsScanResultMapper unsupportedFormatInCsv:formatsCsv] != nil) {
|
|
91
|
+
reject(@"E_UNSUPPORTED_FORMAT", @"iOS ScanKit 不支持请求的码制", nil);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
117
94
|
UIImage *image = [HmsScanModule imageForURI:uri];
|
|
118
95
|
if (image == nil) {
|
|
119
|
-
reject(kErrImageLoadFailed,
|
|
120
|
-
[NSString stringWithFormat:@"无法从 URI 加载图片(仅支持 file:// / 绝对路径 / data:):%@", uri ?: @"(nil)"],
|
|
121
|
-
nil);
|
|
96
|
+
reject(kErrImageLoadFailed, [NSString stringWithFormat:@"无法读取本地图片文件:%@", uri ?: @"(nil)"], nil);
|
|
122
97
|
return;
|
|
123
98
|
}
|
|
124
99
|
|
|
@@ -133,7 +108,8 @@ RCT_EXPORT_MODULE(HmsScan)
|
|
|
133
108
|
// JS parseResultsJson handles the empty case.
|
|
134
109
|
resolve(json);
|
|
135
110
|
} @catch (NSException *exception) {
|
|
136
|
-
reject(
|
|
111
|
+
reject([exception.name isEqualToString:@"HmsInvalidResponse"] ? @"E_INVALID_RESPONSE" : kErrDecodeFailed,
|
|
112
|
+
exception.reason ?: @"HUAWEI Scan Kit 解码图片时发生异常", nil);
|
|
137
113
|
}
|
|
138
114
|
}
|
|
139
115
|
|
|
@@ -18,8 +18,9 @@
|
|
|
18
18
|
NS_ASSUME_NONNULL_BEGIN
|
|
19
19
|
|
|
20
20
|
@interface HmsScanResultMapper : NSObject
|
|
21
|
+
+ (nullable NSString *)unsupportedFormatInCsv:(NSString *)csv;
|
|
21
22
|
|
|
22
|
-
/// Parse a comma-separated list of our
|
|
23
|
+
/// Parse a comma-separated list of our ScanFormat strings into the
|
|
23
24
|
/// `HmsScanOptions.scanFormatType` bitmask (HMSScanFormatTypeCode OR-ed).
|
|
24
25
|
/// An empty/whitespace-only string means "all formats".
|
|
25
26
|
+ (unsigned int)scanFormatTypeFromCsv:(nullable NSString *)csv;
|
|
@@ -30,11 +31,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
30
31
|
+ (nullable NSDictionary *)scanResultFromHuaweiDict:(nullable NSDictionary *)dict;
|
|
31
32
|
|
|
32
33
|
/// Map an array of HUAWEI result dictionaries into an array of ScanResult
|
|
33
|
-
/// dictionaries
|
|
34
|
+
/// dictionaries. Invalid required fields raise HmsInvalidResponse.
|
|
34
35
|
+ (NSArray<NSDictionary *> *)scanResultsFromHuaweiArray:(nullable NSArray *)array;
|
|
35
36
|
|
|
36
37
|
/// Serialize an array of ScanResult dictionaries to a JSON string.
|
|
37
|
-
///
|
|
38
|
+
/// Invalid input or serialization failures raise HmsInvalidResponse.
|
|
38
39
|
+ (NSString *)jsonStringFromScanResults:(nullable NSArray<NSDictionary *> *)results;
|
|
39
40
|
|
|
40
41
|
/// Convenience: HUAWEI multi-decode array -> ScanResult[] JSON string.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
#import "HmsScanResultMapper.h"
|
|
7
7
|
|
|
8
8
|
#import <ScanKitFrameWork/ScanKitFrameWork.h>
|
|
9
|
+
#include <cmath>
|
|
9
10
|
|
|
10
11
|
// NOTE on naming collisions: HMSScanFormatTypeCode (in HmsScanFormat.h) declares
|
|
11
12
|
// *global, unscoped* enum constants named QR_CODE, EAN_13, DATA_MATRIX, etc.
|
|
@@ -17,7 +18,7 @@
|
|
|
17
18
|
|
|
18
19
|
#pragma mark - CSV -> scanFormatType bitmask
|
|
19
20
|
|
|
20
|
-
// Maps one of our
|
|
21
|
+
// Maps one of our ScanFormat tokens to the matching HMSScanFormatTypeCode bit.
|
|
21
22
|
// Returns 0 for tokens HUAWEI iOS does not support (e.g. MULTI_FUNCTIONAL) or
|
|
22
23
|
// UNKNOWN, so they contribute nothing to the OR-ed mask.
|
|
23
24
|
+ (unsigned int)bitForFormatToken:(NSString *)token {
|
|
@@ -53,6 +54,16 @@
|
|
|
53
54
|
return bit ? (unsigned int)bit.unsignedIntValue : 0u;
|
|
54
55
|
}
|
|
55
56
|
|
|
57
|
+
+ (nullable NSString *)unsupportedFormatInCsv:(NSString *)csv {
|
|
58
|
+
if (csv.length == 0)
|
|
59
|
+
return nil;
|
|
60
|
+
for (NSString *token in [csv componentsSeparatedByString:@","]) {
|
|
61
|
+
if ([self bitForFormatToken:token] == 0u)
|
|
62
|
+
return token;
|
|
63
|
+
}
|
|
64
|
+
return nil;
|
|
65
|
+
}
|
|
66
|
+
|
|
56
67
|
+ (unsigned int)scanFormatTypeFromCsv:(NSString *)csv {
|
|
57
68
|
NSString *trimmed = [csv stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
58
69
|
if (trimmed.length == 0) {
|
|
@@ -63,12 +74,10 @@
|
|
|
63
74
|
for (NSString *token in [trimmed componentsSeparatedByString:@","]) {
|
|
64
75
|
mask |= [self bitForFormatToken:token];
|
|
65
76
|
}
|
|
66
|
-
//
|
|
67
|
-
// fall back to ALL so we still attempt a decode rather than scanning nothing.
|
|
68
|
-
return mask == 0u ? (unsigned int)ALL : mask;
|
|
77
|
+
return mask; // Unsupported tokens are rejected by the capability boundary.
|
|
69
78
|
}
|
|
70
79
|
|
|
71
|
-
#pragma mark - formatValue -> our
|
|
80
|
+
#pragma mark - formatValue -> our ScanFormat string
|
|
72
81
|
|
|
73
82
|
// HUAWEI's `formatValue` is documented as the HMSScanFormatTypeCode integer.
|
|
74
83
|
// We tolerate it being delivered as an NSNumber (bit value) OR an NSString
|
|
@@ -207,9 +216,6 @@
|
|
|
207
216
|
if ([v isKindOfClass:[NSNumber class]]) {
|
|
208
217
|
return (NSNumber *)v;
|
|
209
218
|
}
|
|
210
|
-
if ([v isKindOfClass:[NSString class]]) {
|
|
211
|
-
return @([(NSString *)v doubleValue]);
|
|
212
|
-
}
|
|
213
219
|
}
|
|
214
220
|
return nil;
|
|
215
221
|
}
|
|
@@ -228,7 +234,7 @@
|
|
|
228
234
|
NSDictionary *p = (NSDictionary *)element;
|
|
229
235
|
NSNumber *x = [self numberFromPointDict:p keys:@[ @"posX", @"x", @"X" ]];
|
|
230
236
|
NSNumber *y = [self numberFromPointDict:p keys:@[ @"posY", @"y", @"Y" ]];
|
|
231
|
-
if (x != nil && y != nil) {
|
|
237
|
+
if (x != nil && y != nil && std::isfinite(x.doubleValue) && std::isfinite(y.doubleValue)) {
|
|
232
238
|
[points addObject:@{@"x" : x, @"y" : y}];
|
|
233
239
|
}
|
|
234
240
|
}
|
|
@@ -241,7 +247,7 @@
|
|
|
241
247
|
// Primary key is `text`; tolerate a couple of alternates defensively.
|
|
242
248
|
for (NSString *key in @[ @"text", @"originalValue", @"showText", @"value" ]) {
|
|
243
249
|
id v = dict[key];
|
|
244
|
-
if ([v isKindOfClass:[NSString class]] &&
|
|
250
|
+
if ([v isKindOfClass:[NSString class]] && v != nil) {
|
|
245
251
|
return (NSString *)v;
|
|
246
252
|
}
|
|
247
253
|
}
|
|
@@ -255,7 +261,7 @@
|
|
|
255
261
|
|
|
256
262
|
NSString *value = [self valueFromHuaweiDict:dict];
|
|
257
263
|
if (value == nil) {
|
|
258
|
-
return nil;
|
|
264
|
+
return nil;
|
|
259
265
|
}
|
|
260
266
|
|
|
261
267
|
NSMutableDictionary *out = [NSMutableDictionary dictionary];
|
|
@@ -277,12 +283,16 @@
|
|
|
277
283
|
|
|
278
284
|
+ (NSArray<NSDictionary *> *)scanResultsFromHuaweiArray:(NSArray *)array {
|
|
279
285
|
NSMutableArray<NSDictionary *> *results = [NSMutableArray array];
|
|
286
|
+
if (array != nil && ![array isKindOfClass:[NSArray class]]) {
|
|
287
|
+
[NSException raise:@"HmsInvalidResponse" format:@"ScanKit returned a non-array result"];
|
|
288
|
+
}
|
|
280
289
|
if ([array isKindOfClass:[NSArray class]]) {
|
|
281
290
|
for (id element in array) {
|
|
282
291
|
NSDictionary *mapped = [self scanResultFromHuaweiDict:element];
|
|
283
|
-
if (mapped
|
|
284
|
-
[
|
|
292
|
+
if (mapped == nil) {
|
|
293
|
+
[NSException raise:@"HmsInvalidResponse" format:@"ScanKit returned an invalid result"];
|
|
285
294
|
}
|
|
295
|
+
[results addObject:mapped];
|
|
286
296
|
}
|
|
287
297
|
}
|
|
288
298
|
return [results copy];
|
|
@@ -291,17 +301,19 @@
|
|
|
291
301
|
#pragma mark - JSON
|
|
292
302
|
|
|
293
303
|
+ (NSString *)jsonStringFromScanResults:(NSArray<NSDictionary *> *)results {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
return @"[]";
|
|
304
|
+
if (![results isKindOfClass:[NSArray class]] || ![NSJSONSerialization isValidJSONObject:results]) {
|
|
305
|
+
[NSException raise:@"HmsInvalidResponse" format:@"ScanKit result cannot be serialized"];
|
|
297
306
|
}
|
|
298
307
|
NSError *error = nil;
|
|
299
|
-
NSData *data = [NSJSONSerialization dataWithJSONObject:
|
|
308
|
+
NSData *data = [NSJSONSerialization dataWithJSONObject:results options:0 error:&error];
|
|
300
309
|
if (data == nil || error != nil) {
|
|
301
|
-
|
|
310
|
+
[NSException raise:@"HmsInvalidResponse" format:@"ScanKit result cannot be serialized"];
|
|
302
311
|
}
|
|
303
312
|
NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
304
|
-
|
|
313
|
+
if (json == nil) {
|
|
314
|
+
[NSException raise:@"HmsInvalidResponse" format:@"ScanKit result cannot be encoded as UTF-8"];
|
|
315
|
+
}
|
|
316
|
+
return json;
|
|
305
317
|
}
|
|
306
318
|
|
|
307
319
|
+ (NSString *)jsonStringFromHuaweiArray:(NSArray *)array {
|
package/ios/HmsScanView.h
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// Fabric component registered under the name "HmsScanView". Hosts HUAWEI Scan
|
|
6
6
|
// Kit's HmsCustomScanViewController as an embedded child view controller and
|
|
7
7
|
// bridges its delegate callbacks to the codegen events
|
|
8
|
-
// (onScanResult / onScanError /
|
|
8
|
+
// (onScanResult / onScanError / onTorchState).
|
|
9
9
|
//
|
|
10
10
|
|
|
11
11
|
#import <React/RCTViewComponentView.h>
|