raxon-barcode-scanner 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # raxon-barcode-scanner
2
+
3
+ Zebra ve benzeri kurumsal Android cihazlarda fiziksel barkod okuyucudan gelen veriyi React Native / Expo uygulamanıza aktaran hook tabanlı paket.
4
+
5
+ ## Özellikler
6
+
7
+ - `useBarcodeScanner` hook ile basit entegrasyon
8
+ - Zebra DataWedge profilini otomatik oluşturma / güncelleme
9
+ - Klavye gibi davranan (HID / keyboard-wedge) okuyucu desteği: tuş vuruşları tamponlanır, Enter/Tab ile tek barkod olarak yayınlanır ve UI'a sızmaz
10
+ - Android 13+ broadcast receiver uyumluluğu
11
+ - Expo Modules API ile autolinking
12
+
13
+ ## Kurulum
14
+
15
+ ```bash
16
+ npm install raxon-barcode-scanner
17
+ ```
18
+
19
+ Expo projelerinde development build veya `expo prebuild` gerekir. Expo Go içinde native modül çalışmaz.
20
+
21
+ ```bash
22
+ npx expo prebuild
23
+ npx expo run:android
24
+ ```
25
+
26
+ ## Kullanım
27
+
28
+ ```tsx
29
+ import { useCallback, useState } from 'react';
30
+ import { useBarcodeScanner } from 'raxon-barcode-scanner';
31
+
32
+ function ScannerScreen() {
33
+ const [enabled, setEnabled] = useState(true);
34
+
35
+ const onReadBarcode = useCallback((payload) => {
36
+ console.log(payload.code, payload.symbology);
37
+ }, []);
38
+
39
+ const scanner = useBarcodeScanner(enabled, onReadBarcode);
40
+
41
+ return (
42
+ <>
43
+ <Switch value={enabled} onValueChange={setEnabled} />
44
+ <Text>{scanner.isListening ? 'Dinleniyor' : 'Kapalı'}</Text>
45
+ </>
46
+ );
47
+ }
48
+ ```
49
+
50
+ `Switch` ve `Text` için `react-native` importunu eklemeyi unutmayın.
51
+
52
+ `enabled` değeri `true` olduğu sürece native dinleyici açık kalır. `false` yapıldığında dinleyici kapanır.
53
+
54
+ ### Gelişmiş ayarlar
55
+
56
+ ```tsx
57
+ useBarcodeScanner(enabled, onReadBarcode, {
58
+ intentAction: 'com.myapp.barcode.ACTION',
59
+ profileName: 'MyAppScanner',
60
+ configureDataWedge: true,
61
+ captureKeyboard: true,
62
+ });
63
+ ```
64
+
65
+ | Seçenek | Varsayılan | Açıklama |
66
+ | --- | --- | --- |
67
+ | `intentAction` | `com.raxon.barcode.ACTION` | DataWedge broadcast action |
68
+ | `profileName` | `RaxonBarcodeScanner` | DataWedge profil adı |
69
+ | `configureDataWedge` | `true` | Profili otomatik yapılandır |
70
+ | `captureKeyboard` | `true` | Klavye modundaki (HID) okuyucuları yakala |
71
+
72
+ `configureDataWedge: false` kullanın eğer DataWedge profilini MDM veya manuel olarak yönetiyorsanız. Bu durumda `intentAction` değerinin profildeki Intent Output action ile eşleşmesi gerekir.
73
+
74
+ ## Test projesi
75
+
76
+ Depodaki `example` uygulaması modülü test etmek içindir.
77
+
78
+ ```bash
79
+ cd example
80
+ npm install
81
+ npm run android
82
+ ```
83
+
84
+ Kök dizinden:
85
+
86
+ ```bash
87
+ npm run open:android
88
+ ```
89
+
90
+ ## Desteklenen cihazlar
91
+
92
+ - Zebra TC serisi ve DataWedge yüklü cihazlar
93
+ - DataWedge Intent Output ile broadcast gönderen diğer kurumsal Android cihazlar
94
+ - Klavye (HID / keyboard-wedge) modunda çalışan okuyucular ve el terminalleri
95
+
96
+ ## npm yayını
97
+
98
+ ```bash
99
+ npm run build
100
+ npm publish --access public
101
+ ```
102
+
103
+ ## Lisans
104
+
105
+ MIT
@@ -0,0 +1,18 @@
1
+ plugins {
2
+ id 'com.android.library'
3
+ id 'expo-module-gradle-plugin'
4
+ }
5
+
6
+ group = 'expo.modules.raxonbarcodescanner'
7
+ version = '0.1.0'
8
+
9
+ android {
10
+ namespace "expo.modules.raxonbarcodescanner"
11
+ defaultConfig {
12
+ versionCode 1
13
+ versionName "0.1.0"
14
+ }
15
+ lintOptions {
16
+ abortOnError false
17
+ }
18
+ }
@@ -0,0 +1,2 @@
1
+ <manifest>
2
+ </manifest>
@@ -0,0 +1,330 @@
1
+ package expo.modules.raxonbarcodescanner
2
+
3
+ import android.app.Activity
4
+ import android.content.BroadcastReceiver
5
+ import android.content.Context
6
+ import android.content.Intent
7
+ import android.content.IntentFilter
8
+ import android.os.Build
9
+ import android.os.Bundle
10
+ import android.os.SystemClock
11
+ import android.view.KeyEvent
12
+ import android.view.Window
13
+ import expo.modules.kotlin.modules.Module
14
+ import expo.modules.kotlin.modules.ModuleDefinition
15
+
16
+ class RaxonBarcodeScannerModule : Module() {
17
+ companion object {
18
+ private const val DEFAULT_INTENT_ACTION = "com.raxon.barcode.ACTION"
19
+ private const val DEFAULT_PROFILE_NAME = "RaxonBarcodeScanner"
20
+ private const val DATAWEDGE_API_ACTION = "com.symbol.datawedge.api.ACTION"
21
+ private const val DATAWEDGE_RESULT_ACTION = "com.symbol.datawedge.api.RESULT_ACTION"
22
+ private const val DATAWEDGE_PACKAGE = "com.symbol.datawedge"
23
+ private const val SET_CONFIG_EXTRA = "com.symbol.datawedge.api.SET_CONFIG"
24
+
25
+ private val BARCODE_DATA_KEYS = listOf(
26
+ "com.symbol.datawedge.data_string",
27
+ "com.motorolasolutions.emdk.datawedge.data_string",
28
+ "data",
29
+ "barcode_string",
30
+ "SCAN_BARCODE1",
31
+ "barcode"
32
+ )
33
+
34
+ private val SYMBOLOGY_KEYS = listOf(
35
+ "com.symbol.datawedge.label_type",
36
+ "com.motorolasolutions.emdk.datawedge.label_type",
37
+ "label_type",
38
+ "aimId"
39
+ )
40
+
41
+ // Keyboard-wedge: art arda gelen tuşlar arasında bu süre aşılırsa tampon sıfırlanır.
42
+ private const val WEDGE_BUFFER_TIMEOUT_MS = 1000L
43
+
44
+ private val TERMINATOR_KEY_CODES = setOf(
45
+ KeyEvent.KEYCODE_ENTER,
46
+ KeyEvent.KEYCODE_NUMPAD_ENTER,
47
+ KeyEvent.KEYCODE_TAB
48
+ )
49
+ }
50
+
51
+ private var broadcastReceiver: BroadcastReceiver? = null
52
+ private var isListening = false
53
+ private var keyInterceptor: KeyInterceptorCallback? = null
54
+ private val wedgeBuffer = StringBuilder()
55
+ private var lastWedgeKeyTime = 0L
56
+ private val consumedKeyDowns = mutableSetOf<Int>()
57
+
58
+ private val context: Context
59
+ get() = requireNotNull(appContext.reactContext)
60
+
61
+ override fun definition() = ModuleDefinition {
62
+ Name("RaxonBarcodeScanner")
63
+
64
+ Events("onBarcodeScanned")
65
+
66
+ Function("startListening") { options: Map<String, Any?>? ->
67
+ startListening(options)
68
+ }
69
+
70
+ Function("stopListening") {
71
+ stopListening()
72
+ }
73
+
74
+ OnDestroy {
75
+ stopListening()
76
+ }
77
+ }
78
+
79
+ private fun startListening(options: Map<String, Any?>?) {
80
+ if (isListening) {
81
+ stopListening()
82
+ }
83
+
84
+ val intentAction = (options?.get("intentAction") as? String)?.takeIf { it.isNotBlank() }
85
+ ?: DEFAULT_INTENT_ACTION
86
+ val profileName = (options?.get("profileName") as? String)?.takeIf { it.isNotBlank() }
87
+ ?: DEFAULT_PROFILE_NAME
88
+ val configureDataWedge = options?.get("configureDataWedge") as? Boolean ?: true
89
+ val captureKeyboard = options?.get("captureKeyboard") as? Boolean ?: true
90
+
91
+ if (configureDataWedge) {
92
+ configureDataWedgeProfile(profileName, intentAction)
93
+ }
94
+
95
+ registerBarcodeReceiver(intentAction)
96
+
97
+ if (captureKeyboard) {
98
+ installKeyboardCapture()
99
+ }
100
+
101
+ isListening = true
102
+ }
103
+
104
+ private fun stopListening() {
105
+ broadcastReceiver?.let { receiver ->
106
+ try {
107
+ context.unregisterReceiver(receiver)
108
+ } catch (_: IllegalArgumentException) {
109
+ // Receiver was already unregistered.
110
+ }
111
+ }
112
+ broadcastReceiver = null
113
+ uninstallKeyboardCapture()
114
+ isListening = false
115
+ }
116
+
117
+ // region Keyboard wedge (HID) scanners
118
+
119
+ private fun installKeyboardCapture() {
120
+ val activity = appContext.activityProvider?.currentActivity ?: return
121
+ activity.runOnUiThread {
122
+ val window = activity.window ?: return@runOnUiThread
123
+ val current = window.callback
124
+ if (current is KeyInterceptorCallback) {
125
+ current.enabled = true
126
+ keyInterceptor = current
127
+ return@runOnUiThread
128
+ }
129
+ val interceptor = KeyInterceptorCallback(current, ::handleWedgeKeyEvent)
130
+ window.callback = interceptor
131
+ keyInterceptor = interceptor
132
+ }
133
+ }
134
+
135
+ private fun uninstallKeyboardCapture() {
136
+ val interceptor = keyInterceptor ?: return
137
+ keyInterceptor = null
138
+ wedgeBuffer.setLength(0)
139
+ consumedKeyDowns.clear()
140
+ val activity = appContext.activityProvider?.currentActivity
141
+ if (activity == null) {
142
+ interceptor.enabled = false
143
+ return
144
+ }
145
+ activity.runOnUiThread {
146
+ val window = activity.window
147
+ if (window?.callback === interceptor) {
148
+ // Başka bir kütüphane callback'i sarmadıysa orijinali geri koy.
149
+ window.callback = interceptor.wrapped
150
+ } else {
151
+ interceptor.enabled = false
152
+ }
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Klavye gibi davranan (HID/keyboard-wedge) okuyucuların tuş vuruşlarını tamponlar.
158
+ * Enter/Tab geldiğinde tamponu barkod olarak yayınlar ve tuşu yutar; böylece
159
+ * Enter odaktaki bileşeni (ör. Switch) tetiklemez.
160
+ */
161
+ private fun handleWedgeKeyEvent(event: KeyEvent): Boolean {
162
+ val isTerminator = event.keyCode in TERMINATOR_KEY_CODES
163
+ val unicode = event.unicodeChar
164
+
165
+ when (event.action) {
166
+ KeyEvent.ACTION_UP -> {
167
+ // DOWN'da yutulan tuşların UP'ı da yutulur ki view'lar click üretmesin.
168
+ return consumedKeyDowns.remove(event.keyCode)
169
+ }
170
+ KeyEvent.ACTION_MULTIPLE -> {
171
+ val characters = event.characters
172
+ if (!characters.isNullOrEmpty()) {
173
+ touchWedgeBuffer()
174
+ wedgeBuffer.append(characters)
175
+ return true
176
+ }
177
+ return false
178
+ }
179
+ KeyEvent.ACTION_DOWN -> {
180
+ if (isTerminator) {
181
+ if (!wedgeBufferActive()) {
182
+ // Tampon boşken Enter/Tab normal davranışına bırakılır.
183
+ return false
184
+ }
185
+ val code = wedgeBuffer.toString()
186
+ wedgeBuffer.setLength(0)
187
+ consumedKeyDowns.add(event.keyCode)
188
+ if (code.isNotEmpty()) {
189
+ sendEvent("onBarcodeScanned", mapOf("code" to code))
190
+ }
191
+ return true
192
+ }
193
+ if (unicode != 0) {
194
+ touchWedgeBuffer()
195
+ wedgeBuffer.append(unicode.toChar())
196
+ consumedKeyDowns.add(event.keyCode)
197
+ return true
198
+ }
199
+ return false
200
+ }
201
+ else -> return false
202
+ }
203
+ }
204
+
205
+ private fun wedgeBufferActive(): Boolean {
206
+ return wedgeBuffer.isNotEmpty() &&
207
+ SystemClock.elapsedRealtime() - lastWedgeKeyTime <= WEDGE_BUFFER_TIMEOUT_MS
208
+ }
209
+
210
+ private fun touchWedgeBuffer() {
211
+ val now = SystemClock.elapsedRealtime()
212
+ if (now - lastWedgeKeyTime > WEDGE_BUFFER_TIMEOUT_MS) {
213
+ wedgeBuffer.setLength(0)
214
+ }
215
+ lastWedgeKeyTime = now
216
+ }
217
+
218
+ private class KeyInterceptorCallback(
219
+ val wrapped: Window.Callback,
220
+ private val onKeyEvent: (KeyEvent) -> Boolean,
221
+ ) : Window.Callback by wrapped {
222
+ var enabled = true
223
+
224
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
225
+ if (enabled && onKeyEvent(event)) {
226
+ return true
227
+ }
228
+ return wrapped.dispatchKeyEvent(event)
229
+ }
230
+ }
231
+
232
+ // endregion
233
+
234
+ private fun registerBarcodeReceiver(intentAction: String) {
235
+ val filter = IntentFilter().apply {
236
+ addAction(intentAction)
237
+ addAction(DATAWEDGE_RESULT_ACTION)
238
+ addCategory(Intent.CATEGORY_DEFAULT)
239
+ }
240
+
241
+ val receiver = object : BroadcastReceiver() {
242
+ override fun onReceive(ctx: Context?, intent: Intent?) {
243
+ intent ?: return
244
+ handleIntent(intent)
245
+ }
246
+ }
247
+
248
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
249
+ context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
250
+ } else {
251
+ @Suppress("UnspecifiedRegisterReceiverFlag")
252
+ context.registerReceiver(receiver, filter)
253
+ }
254
+
255
+ broadcastReceiver = receiver
256
+ }
257
+
258
+ private fun handleIntent(intent: Intent) {
259
+ val action = intent.action ?: return
260
+
261
+ if (action == DATAWEDGE_RESULT_ACTION) {
262
+ return
263
+ }
264
+
265
+ val code = BARCODE_DATA_KEYS.firstNotNullOfOrNull { key ->
266
+ intent.getStringExtra(key)?.takeIf { it.isNotBlank() }
267
+ } ?: return
268
+
269
+ val symbology = SYMBOLOGY_KEYS.firstNotNullOfOrNull { key ->
270
+ intent.getStringExtra(key)?.takeIf { it.isNotBlank() }
271
+ }
272
+
273
+ val payload = mutableMapOf<String, Any>("code" to code)
274
+ symbology?.let { payload["symbology"] = it }
275
+
276
+ sendEvent("onBarcodeScanned", payload)
277
+ }
278
+
279
+ private fun configureDataWedgeProfile(profileName: String, intentAction: String) {
280
+ val packageName = context.packageName
281
+
282
+ val barcodePlugin = Bundle().apply {
283
+ putString("PLUGIN_NAME", "BARCODE")
284
+ putString("RESET_CONFIG", "true")
285
+ putString("scanner_selection", "auto")
286
+ putString("scanner_input_enabled", "true")
287
+ }
288
+
289
+ val appList = Bundle().apply {
290
+ putString("PACKAGE_NAME", packageName)
291
+ putStringArray("ACTIVITY_LIST", arrayOf("*"))
292
+ }
293
+
294
+ val profileConfig = Bundle().apply {
295
+ putString("PROFILE_NAME", profileName)
296
+ putString("PROFILE_ENABLED", "true")
297
+ putString("CONFIG_MODE", "CREATE_IF_NOT_EXIST")
298
+ putParcelableArray("PLUGIN_CONFIG", arrayOf(barcodePlugin))
299
+ putParcelableArray("APP_LIST", arrayOf(appList))
300
+ }
301
+
302
+ sendDataWedgeIntent(profileConfig)
303
+
304
+ val intentPlugin = Bundle().apply {
305
+ putString("PLUGIN_NAME", "INTENT")
306
+ putString("RESET_CONFIG", "true")
307
+ putString("intent_output_enabled", "true")
308
+ putString("intent_action", intentAction)
309
+ putString("intent_delivery", "2")
310
+ putString("intent_category", "android.intent.category.DEFAULT")
311
+ }
312
+
313
+ val intentConfig = Bundle().apply {
314
+ putString("PROFILE_NAME", profileName)
315
+ putString("CONFIG_MODE", "UPDATE")
316
+ putParcelableArray("PLUGIN_CONFIG", arrayOf(intentPlugin))
317
+ }
318
+
319
+ sendDataWedgeIntent(intentConfig)
320
+ }
321
+
322
+ private fun sendDataWedgeIntent(config: Bundle) {
323
+ val intent = Intent().apply {
324
+ action = DATAWEDGE_API_ACTION
325
+ setPackage(DATAWEDGE_PACKAGE)
326
+ putExtra(SET_CONFIG_EXTRA, config)
327
+ }
328
+ context.sendBroadcast(intent)
329
+ }
330
+ }
@@ -0,0 +1,36 @@
1
+ export type BarcodeScanPayload = {
2
+ code: string;
3
+ symbology?: string;
4
+ };
5
+ export type BarcodeScannerOptions = {
6
+ /**
7
+ * DataWedge intent action used for barcode broadcasts.
8
+ * @default "com.raxon.barcode.ACTION"
9
+ */
10
+ intentAction?: string;
11
+ /**
12
+ * DataWedge profile name created or updated for this app.
13
+ * @default "RaxonBarcodeScanner"
14
+ */
15
+ profileName?: string;
16
+ /**
17
+ * Automatically configure a DataWedge profile on Zebra devices.
18
+ * Set to false if you manage DataWedge profiles manually.
19
+ * @default true
20
+ */
21
+ configureDataWedge?: boolean;
22
+ /**
23
+ * Capture input from scanners that act as a keyboard (HID / keyboard-wedge).
24
+ * Keystrokes are buffered and emitted as a single scan when Enter/Tab is
25
+ * received; the keys are swallowed so they don't interact with the UI.
26
+ * @default true
27
+ */
28
+ captureKeyboard?: boolean;
29
+ };
30
+ export type RaxonBarcodeScannerModuleEvents = {
31
+ onBarcodeScanned: (params: BarcodeScanPayload) => void;
32
+ };
33
+ export type UseBarcodeScannerResult = {
34
+ isListening: boolean;
35
+ };
36
+ //# sourceMappingURL=RaxonBarcodeScanner.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScanner.types.d.ts","sourceRoot":"","sources":["../src/RaxonBarcodeScanner.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,gBAAgB,EAAE,CAAC,MAAM,EAAE,kBAAkB,KAAK,IAAI,CAAC;CACxD,CAAC;AAEF,MAAM,MAAM,uBAAuB,GAAG;IACpC,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RaxonBarcodeScanner.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScanner.types.js","sourceRoot":"","sources":["../src/RaxonBarcodeScanner.types.ts"],"names":[],"mappings":"","sourcesContent":["export type BarcodeScanPayload = {\n code: string;\n symbology?: string;\n};\n\nexport type BarcodeScannerOptions = {\n /**\n * DataWedge intent action used for barcode broadcasts.\n * @default \"com.raxon.barcode.ACTION\"\n */\n intentAction?: string;\n /**\n * DataWedge profile name created or updated for this app.\n * @default \"RaxonBarcodeScanner\"\n */\n profileName?: string;\n /**\n * Automatically configure a DataWedge profile on Zebra devices.\n * Set to false if you manage DataWedge profiles manually.\n * @default true\n */\n configureDataWedge?: boolean;\n /**\n * Capture input from scanners that act as a keyboard (HID / keyboard-wedge).\n * Keystrokes are buffered and emitted as a single scan when Enter/Tab is\n * received; the keys are swallowed so they don't interact with the UI.\n * @default true\n */\n captureKeyboard?: boolean;\n};\n\nexport type RaxonBarcodeScannerModuleEvents = {\n onBarcodeScanned: (params: BarcodeScanPayload) => void;\n};\n\nexport type UseBarcodeScannerResult = {\n isListening: boolean;\n};\n"]}
@@ -0,0 +1,9 @@
1
+ import { NativeModule } from 'expo';
2
+ import { BarcodeScannerOptions, RaxonBarcodeScannerModuleEvents } from './RaxonBarcodeScanner.types';
3
+ declare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
4
+ startListening(options?: BarcodeScannerOptions): void;
5
+ stopListening(): void;
6
+ }
7
+ declare const _default: RaxonBarcodeScannerModule;
8
+ export default _default;
9
+ //# sourceMappingURL=RaxonBarcodeScannerModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScannerModule.d.ts","sourceRoot":"","sources":["../src/RaxonBarcodeScannerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,EAChC,MAAM,6BAA6B,CAAC;AAErC,OAAO,OAAO,yBAA0B,SAAQ,YAAY,CAAC,+BAA+B,CAAC;IAC3F,cAAc,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI;IACrD,aAAa,IAAI,IAAI;CACtB;;AAED,wBAAqF"}
@@ -0,0 +1,3 @@
1
+ import { requireNativeModule } from 'expo';
2
+ export default requireNativeModule('RaxonBarcodeScanner');
3
+ //# sourceMappingURL=RaxonBarcodeScannerModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScannerModule.js","sourceRoot":"","sources":["../src/RaxonBarcodeScannerModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAYzD,eAAe,mBAAmB,CAA4B,qBAAqB,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\nimport {\n BarcodeScannerOptions,\n RaxonBarcodeScannerModuleEvents,\n} from './RaxonBarcodeScanner.types';\n\ndeclare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {\n startListening(options?: BarcodeScannerOptions): void;\n stopListening(): void;\n}\n\nexport default requireNativeModule<RaxonBarcodeScannerModule>('RaxonBarcodeScanner');\n"]}
@@ -0,0 +1,9 @@
1
+ import { NativeModule } from 'expo';
2
+ import { BarcodeScannerOptions, RaxonBarcodeScannerModuleEvents } from './RaxonBarcodeScanner.types';
3
+ declare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
4
+ startListening(_options?: BarcodeScannerOptions): void;
5
+ stopListening(): void;
6
+ }
7
+ declare const _default: typeof RaxonBarcodeScannerModule;
8
+ export default _default;
9
+ //# sourceMappingURL=RaxonBarcodeScannerModule.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScannerModule.web.d.ts","sourceRoot":"","sources":["../src/RaxonBarcodeScannerModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,YAAY,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,EAChC,MAAM,6BAA6B,CAAC;AAErC,cAAM,yBAA0B,SAAQ,YAAY,CAAC,+BAA+B,CAAC;IACnF,cAAc,CAAC,QAAQ,CAAC,EAAE,qBAAqB,GAAG,IAAI;IAItD,aAAa,IAAI,IAAI;CAGtB;;AAED,wBAAmF"}
@@ -0,0 +1,11 @@
1
+ import { registerWebModule, NativeModule } from 'expo';
2
+ class RaxonBarcodeScannerModule extends NativeModule {
3
+ startListening(_options) {
4
+ console.warn('raxon-barcode-scanner is only supported on Android.');
5
+ }
6
+ stopListening() {
7
+ // no-op
8
+ }
9
+ }
10
+ export default registerWebModule(RaxonBarcodeScannerModule, 'RaxonBarcodeScanner');
11
+ //# sourceMappingURL=RaxonBarcodeScannerModule.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RaxonBarcodeScannerModule.web.js","sourceRoot":"","sources":["../src/RaxonBarcodeScannerModule.web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AAOvD,MAAM,yBAA0B,SAAQ,YAA6C;IACnF,cAAc,CAAC,QAAgC;QAC7C,OAAO,CAAC,IAAI,CAAC,qDAAqD,CAAC,CAAC;IACtE,CAAC;IAED,aAAa;QACX,QAAQ;IACV,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC,yBAAyB,EAAE,qBAAqB,CAAC,CAAC","sourcesContent":["import { registerWebModule, NativeModule } from 'expo';\n\nimport {\n BarcodeScannerOptions,\n RaxonBarcodeScannerModuleEvents,\n} from './RaxonBarcodeScanner.types';\n\nclass RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {\n startListening(_options?: BarcodeScannerOptions): void {\n console.warn('raxon-barcode-scanner is only supported on Android.');\n }\n\n stopListening(): void {\n // no-op\n }\n}\n\nexport default registerWebModule(RaxonBarcodeScannerModule, 'RaxonBarcodeScanner');\n"]}
@@ -0,0 +1,4 @@
1
+ export { default as RaxonBarcodeScanner } from './RaxonBarcodeScannerModule';
2
+ export { useBarcodeScanner } from './useBarcodeScanner';
3
+ export * from './RaxonBarcodeScanner.types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,cAAc,6BAA6B,CAAC"}
package/build/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { default as RaxonBarcodeScanner } from './RaxonBarcodeScannerModule';
2
+ export { useBarcodeScanner } from './useBarcodeScanner';
3
+ export * from './RaxonBarcodeScanner.types';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,cAAc,6BAA6B,CAAC","sourcesContent":["export { default as RaxonBarcodeScanner } from './RaxonBarcodeScannerModule';\nexport { useBarcodeScanner } from './useBarcodeScanner';\nexport * from './RaxonBarcodeScanner.types';\n"]}
@@ -0,0 +1,3 @@
1
+ import { BarcodeScanPayload, BarcodeScannerOptions, UseBarcodeScannerResult } from './RaxonBarcodeScanner.types';
2
+ export declare function useBarcodeScanner(enabled: boolean, onReadBarcode: (payload: BarcodeScanPayload) => void, options?: BarcodeScannerOptions): UseBarcodeScannerResult;
3
+ //# sourceMappingURL=useBarcodeScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBarcodeScanner.d.ts","sourceRoot":"","sources":["../src/useBarcodeScanner.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACxB,MAAM,6BAA6B,CAAC;AAGrC,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,EACpD,OAAO,CAAC,EAAE,qBAAqB,GAC9B,uBAAuB,CA+BzB"}
@@ -0,0 +1,29 @@
1
+ import { useEffect, useRef } from 'react';
2
+ import RaxonBarcodeScanner from './RaxonBarcodeScannerModule';
3
+ export function useBarcodeScanner(enabled, onReadBarcode, options) {
4
+ const onReadBarcodeRef = useRef(onReadBarcode);
5
+ useEffect(() => {
6
+ onReadBarcodeRef.current = onReadBarcode;
7
+ });
8
+ useEffect(() => {
9
+ if (!enabled) {
10
+ return;
11
+ }
12
+ RaxonBarcodeScanner.startListening(options);
13
+ const subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
14
+ onReadBarcodeRef.current(event);
15
+ });
16
+ return () => {
17
+ subscription.remove();
18
+ RaxonBarcodeScanner.stopListening();
19
+ };
20
+ }, [
21
+ enabled,
22
+ options?.intentAction,
23
+ options?.profileName,
24
+ options?.configureDataWedge,
25
+ options?.captureKeyboard,
26
+ ]);
27
+ return { isListening: enabled };
28
+ }
29
+ //# sourceMappingURL=useBarcodeScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBarcodeScanner.js","sourceRoot":"","sources":["../src/useBarcodeScanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAO1C,OAAO,mBAAmB,MAAM,6BAA6B,CAAC;AAE9D,MAAM,UAAU,iBAAiB,CAC/B,OAAgB,EAChB,aAAoD,EACpD,OAA+B;IAE/B,MAAM,gBAAgB,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;IAE/C,SAAS,CAAC,GAAG,EAAE;QACb,gBAAgB,CAAC,OAAO,GAAG,aAAa,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO;QACT,CAAC;QAED,mBAAmB,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;QAE5C,MAAM,YAAY,GAAG,mBAAmB,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE;YACjF,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAClC,CAAC,CAAC,CAAC;QAEH,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;YACtB,mBAAmB,CAAC,aAAa,EAAE,CAAC;QACtC,CAAC,CAAC;IACJ,CAAC,EAAE;QACD,OAAO;QACP,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,WAAW;QACpB,OAAO,EAAE,kBAAkB;QAC3B,OAAO,EAAE,eAAe;KACzB,CAAC,CAAC;IAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC;AAClC,CAAC","sourcesContent":["import { useEffect, useRef } from 'react';\n\nimport {\n BarcodeScanPayload,\n BarcodeScannerOptions,\n UseBarcodeScannerResult,\n} from './RaxonBarcodeScanner.types';\nimport RaxonBarcodeScanner from './RaxonBarcodeScannerModule';\n\nexport function useBarcodeScanner(\n enabled: boolean,\n onReadBarcode: (payload: BarcodeScanPayload) => void,\n options?: BarcodeScannerOptions\n): UseBarcodeScannerResult {\n const onReadBarcodeRef = useRef(onReadBarcode);\n\n useEffect(() => {\n onReadBarcodeRef.current = onReadBarcode;\n });\n\n useEffect(() => {\n if (!enabled) {\n return;\n }\n\n RaxonBarcodeScanner.startListening(options);\n\n const subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {\n onReadBarcodeRef.current(event);\n });\n\n return () => {\n subscription.remove();\n RaxonBarcodeScanner.stopListening();\n };\n }, [\n enabled,\n options?.intentAction,\n options?.profileName,\n options?.configureDataWedge,\n options?.captureKeyboard,\n ]);\n\n return { isListening: enabled };\n}\n"]}
@@ -0,0 +1,6 @@
1
+ {
2
+ "platforms": ["android"],
3
+ "android": {
4
+ "modules": ["expo.modules.raxonbarcodescanner.RaxonBarcodeScannerModule"]
5
+ }
6
+ }
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "raxon-barcode-scanner",
3
+ "version": "0.1.0",
4
+ "description": "Zebra and enterprise barcode scanner hook for React Native Expo apps",
5
+ "main": "build/index.js",
6
+ "types": "build/index.d.ts",
7
+ "scripts": {
8
+ "build": "node internal/module_scripts/build.js",
9
+ "clean": "node internal/module_scripts/clean.js",
10
+ "lint": "eslint src/",
11
+ "test": "node internal/module_scripts/test.js",
12
+ "prepare": "node internal/module_scripts/prepare.js",
13
+ "open:ios": "node internal/module_scripts/open-ios.js",
14
+ "open:android": "node internal/module_scripts/open-android.js"
15
+ },
16
+ "keywords": [
17
+ "react-native",
18
+ "expo",
19
+ "expo-module",
20
+ "barcode",
21
+ "barcode-scanner",
22
+ "zebra",
23
+ "datawedge",
24
+ "enterprise-scanner",
25
+ "raxon-barcode-scanner"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+ssh://git@github.com/raxonltd/raxon-barcode-scanner.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/raxonltd/raxon-barcode-scanner/issues"
33
+ },
34
+ "author": "Raxon Ltd <furkanbahadirozdemir@nexine.com.tr>",
35
+ "license": "MIT",
36
+ "homepage": "https://github.com/raxonltd/raxon-barcode-scanner#readme",
37
+ "files": [
38
+ "android/build.gradle",
39
+ "android/src",
40
+ "build",
41
+ "expo-module.config.json",
42
+ "src"
43
+ ],
44
+ "dependencies": {},
45
+ "devDependencies": {
46
+ "@babel/core": "^7.26.0",
47
+ "@types/jest": "^29.2.1",
48
+ "@types/react": "~19.1.1",
49
+ "babel-preset-expo": "~56.0.14",
50
+ "eslint": "~9.39.4",
51
+ "eslint-config-universe": "^15.0.3",
52
+ "expo": "^56.0.9",
53
+ "jest": "^29.7.0",
54
+ "jest-expo": "~56.0.4",
55
+ "prettier": "^3.0.0",
56
+ "react-native": "0.85.3",
57
+ "typescript": "^5.9.2"
58
+ },
59
+ "jest": {
60
+ "preset": "jest-expo",
61
+ "roots": [
62
+ "<rootDir>/src"
63
+ ]
64
+ },
65
+ "peerDependencies": {
66
+ "expo": "*",
67
+ "react": "*",
68
+ "react-native": "*"
69
+ }
70
+ }
@@ -0,0 +1,38 @@
1
+ export type BarcodeScanPayload = {
2
+ code: string;
3
+ symbology?: string;
4
+ };
5
+
6
+ export type BarcodeScannerOptions = {
7
+ /**
8
+ * DataWedge intent action used for barcode broadcasts.
9
+ * @default "com.raxon.barcode.ACTION"
10
+ */
11
+ intentAction?: string;
12
+ /**
13
+ * DataWedge profile name created or updated for this app.
14
+ * @default "RaxonBarcodeScanner"
15
+ */
16
+ profileName?: string;
17
+ /**
18
+ * Automatically configure a DataWedge profile on Zebra devices.
19
+ * Set to false if you manage DataWedge profiles manually.
20
+ * @default true
21
+ */
22
+ configureDataWedge?: boolean;
23
+ /**
24
+ * Capture input from scanners that act as a keyboard (HID / keyboard-wedge).
25
+ * Keystrokes are buffered and emitted as a single scan when Enter/Tab is
26
+ * received; the keys are swallowed so they don't interact with the UI.
27
+ * @default true
28
+ */
29
+ captureKeyboard?: boolean;
30
+ };
31
+
32
+ export type RaxonBarcodeScannerModuleEvents = {
33
+ onBarcodeScanned: (params: BarcodeScanPayload) => void;
34
+ };
35
+
36
+ export type UseBarcodeScannerResult = {
37
+ isListening: boolean;
38
+ };
@@ -0,0 +1,13 @@
1
+ import { NativeModule, requireNativeModule } from 'expo';
2
+
3
+ import {
4
+ BarcodeScannerOptions,
5
+ RaxonBarcodeScannerModuleEvents,
6
+ } from './RaxonBarcodeScanner.types';
7
+
8
+ declare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
9
+ startListening(options?: BarcodeScannerOptions): void;
10
+ stopListening(): void;
11
+ }
12
+
13
+ export default requireNativeModule<RaxonBarcodeScannerModule>('RaxonBarcodeScanner');
@@ -0,0 +1,18 @@
1
+ import { registerWebModule, NativeModule } from 'expo';
2
+
3
+ import {
4
+ BarcodeScannerOptions,
5
+ RaxonBarcodeScannerModuleEvents,
6
+ } from './RaxonBarcodeScanner.types';
7
+
8
+ class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
9
+ startListening(_options?: BarcodeScannerOptions): void {
10
+ console.warn('raxon-barcode-scanner is only supported on Android.');
11
+ }
12
+
13
+ stopListening(): void {
14
+ // no-op
15
+ }
16
+ }
17
+
18
+ export default registerWebModule(RaxonBarcodeScannerModule, 'RaxonBarcodeScanner');
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export { default as RaxonBarcodeScanner } from './RaxonBarcodeScannerModule';
2
+ export { useBarcodeScanner } from './useBarcodeScanner';
3
+ export * from './RaxonBarcodeScanner.types';
@@ -0,0 +1,45 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import {
4
+ BarcodeScanPayload,
5
+ BarcodeScannerOptions,
6
+ UseBarcodeScannerResult,
7
+ } from './RaxonBarcodeScanner.types';
8
+ import RaxonBarcodeScanner from './RaxonBarcodeScannerModule';
9
+
10
+ export function useBarcodeScanner(
11
+ enabled: boolean,
12
+ onReadBarcode: (payload: BarcodeScanPayload) => void,
13
+ options?: BarcodeScannerOptions
14
+ ): UseBarcodeScannerResult {
15
+ const onReadBarcodeRef = useRef(onReadBarcode);
16
+
17
+ useEffect(() => {
18
+ onReadBarcodeRef.current = onReadBarcode;
19
+ });
20
+
21
+ useEffect(() => {
22
+ if (!enabled) {
23
+ return;
24
+ }
25
+
26
+ RaxonBarcodeScanner.startListening(options);
27
+
28
+ const subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
29
+ onReadBarcodeRef.current(event);
30
+ });
31
+
32
+ return () => {
33
+ subscription.remove();
34
+ RaxonBarcodeScanner.stopListening();
35
+ };
36
+ }, [
37
+ enabled,
38
+ options?.intentAction,
39
+ options?.profileName,
40
+ options?.configureDataWedge,
41
+ options?.captureKeyboard,
42
+ ]);
43
+
44
+ return { isListening: enabled };
45
+ }