raxon-barcode-scanner 0.1.3 → 0.1.5

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.
@@ -1,8 +1,8 @@
1
1
  import { NativeModule } from 'expo';
2
2
  import { BarcodeScannerOptions, RaxonBarcodeScannerModuleEvents } from './RaxonBarcodeScanner.types';
3
3
  declare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
4
- startListening(options?: BarcodeScannerOptions): void;
5
- stopListening(): void;
4
+ startListening(options: BarcodeScannerOptions): Promise<void>;
5
+ stopListening(): Promise<void>;
6
6
  }
7
7
  declare const _default: RaxonBarcodeScannerModule;
8
8
  export default _default;
@@ -1 +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"}
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,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAC7D,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;CAC/B;;AAED,wBAAqF"}
@@ -1 +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"]}
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): Promise<void>;\n stopListening(): Promise<void>;\n}\n\nexport default requireNativeModule<RaxonBarcodeScannerModule>('RaxonBarcodeScanner');\n"]}
@@ -1 +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"}
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,CAmCzB"}
@@ -9,12 +9,15 @@ export function useBarcodeScanner(enabled, onReadBarcode, options) {
9
9
  if (!enabled) {
10
10
  return;
11
11
  }
12
- RaxonBarcodeScanner.startListening(options);
13
- const subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
14
- onReadBarcodeRef.current(event);
15
- });
12
+ let subscription = null;
13
+ (async () => {
14
+ await RaxonBarcodeScanner.startListening(options ?? {});
15
+ subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
16
+ onReadBarcodeRef.current(event);
17
+ });
18
+ })();
16
19
  return () => {
17
- subscription.remove();
20
+ subscription?.remove();
18
21
  RaxonBarcodeScanner.stopListening();
19
22
  };
20
23
  }, [
@@ -1 +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"]}
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,IAAI,YAAY,GAAkC,IAAI,CAAC;QAEvD,CAAC,KAAK,IAAI,EAAE;YACV,MAAM,mBAAmB,CAAC,cAAc,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;YAExD,YAAY,GAAG,mBAAmB,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC,KAAK,EAAE,EAAE;gBAC3E,gBAAgB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,EAAE,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,YAAY,EAAE,MAAM,EAAE,CAAC;YACvB,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 let subscription: { remove: () => void } | null = null;\n\n (async () => {\n await RaxonBarcodeScanner.startListening(options ?? {});\n\n subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {\n onReadBarcodeRef.current(event);\n });\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"]}
@@ -10,10 +10,11 @@ Pod::Spec.new do |s|
10
10
  s.license = package['license']
11
11
  s.author = package['author']
12
12
  s.homepage = package['homepage']
13
- s.platform = :ios, '13.4'
13
+ s.platform = :ios, '15.1'
14
14
  s.swift_version = '5.4'
15
15
  s.source = { git: 'https://github.com/raxonltd/raxon-barcode-scanner.git' }
16
16
  s.static_framework = true
17
+ s.frameworks = 'GameController'
17
18
 
18
19
  s.dependency 'ExpoModulesCore'
19
20
 
@@ -1,14 +1,15 @@
1
1
  import ExpoModulesCore
2
+ import GameController
2
3
  import UIKit
3
4
 
4
5
  // MARK: - RaxonBarcodeScannerModule
5
6
 
6
7
  /// iOS için Bluetooth ve HID barkod okuyucu desteği sağlayan Expo modülü.
7
- /// Harici klavye olarak davranan Bluetooth barkod okuyucuların tuş vuruşlarını yakalar.
8
+ /// GameController framework'ün GCKeyboard API'si ile donanım klavyesi tuşlarını
9
+ /// responder chain'den bağımsız olarak, sistem seviyesinde yakalar.
8
10
  public final class RaxonBarcodeScannerModule: Module {
9
11
  private var isListening = false
10
- private var captureKeyboard = true
11
- private var keyboardHandler: ExternalKeyboardHandler?
12
+ private var keyboardMonitor: HardwareKeyboardMonitor?
12
13
 
13
14
  public required init(appContext: AppContext) {
14
15
  super.init(appContext: appContext)
@@ -19,12 +20,16 @@ public final class RaxonBarcodeScannerModule: Module {
19
20
 
20
21
  Events("onBarcodeScanned")
21
22
 
22
- Function("startListening") { (options: [String: Any]?) in
23
- self.startListening(options: options)
23
+ AsyncFunction("startListening") { (options: [String: Any]?) in
24
+ await MainActor.run {
25
+ self.startListening(options: options)
26
+ }
24
27
  }
25
28
 
26
- Function("stopListening") {
27
- self.stopListening()
29
+ AsyncFunction("stopListening") {
30
+ await MainActor.run {
31
+ self.stopListening()
32
+ }
28
33
  }
29
34
 
30
35
  OnDestroy {
@@ -33,308 +38,159 @@ public final class RaxonBarcodeScannerModule: Module {
33
38
  }
34
39
 
35
40
  private func startListening(options: [String: Any]?) {
36
- if isListening {
37
- stopListening()
38
- }
41
+ guard !isListening else { return }
39
42
 
40
- captureKeyboard = options?["captureKeyboard"] as? Bool ?? true
43
+ let captureKeyboard = options?["captureKeyboard"] as? Bool ?? true
41
44
 
42
45
  if captureKeyboard {
43
- setupKeyboardCapture()
46
+ let monitor = HardwareKeyboardMonitor()
47
+ monitor.onBarcodeScanned = { [weak self] code in
48
+ self?.sendEvent("onBarcodeScanned", ["code": code])
49
+ }
50
+ monitor.start()
51
+ keyboardMonitor = monitor
44
52
  }
45
53
 
46
54
  isListening = true
47
55
  }
48
56
 
49
57
  private func stopListening() {
50
- teardownKeyboardCapture()
58
+ keyboardMonitor?.stop()
59
+ keyboardMonitor = nil
51
60
  isListening = false
52
61
  }
53
-
54
- private func setupKeyboardCapture() {
55
- // UIApplication'dan aktif view controller'ı al
56
- guard let viewController = findActiveViewController() else {
57
- return
58
- }
59
-
60
- if #available(iOS 13.4, *) {
61
- keyboardHandler = ExternalKeyboardHandler(
62
- viewController: viewController,
63
- onBarcodeScanned: { [weak self] code in
64
- self?.sendEvent("onBarcodeScanned", ["code": code])
65
- }
66
- )
67
- keyboardHandler?.startListening()
68
- }
69
- }
70
-
71
- private func findActiveViewController() -> UIViewController? {
72
- // UIApplication üzerinden root view controller'ı bul
73
- guard let windowScene = UIApplication.shared.connectedScenes
74
- .first(where: { $0.activationState == .foregroundActive }) as? UIWindowScene,
75
- let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow }) else {
76
- return nil
77
- }
78
-
79
- var topController = keyWindow.rootViewController
80
- while let presentedController = topController?.presentedViewController {
81
- topController = presentedController
82
- }
83
-
84
- return topController
85
- }
86
-
87
- private func teardownKeyboardCapture() {
88
- keyboardHandler?.stopListening()
89
- keyboardHandler = nil
90
- }
91
62
  }
92
63
 
93
- // MARK: - ExternalKeyboardHandler
64
+ // MARK: - HardwareKeyboardMonitor
94
65
 
95
- /// iOS 13.4+ için harici klavye (Bluetooth barkod okuyucu) girişini yakalayan sınıf.
96
- /// pressesBegan/pressesEnded API'sini kullanarak fiziksel klavye tuşlarını yakalar.
97
- @available(iOS 13.4, *)
98
- private final class ExternalKeyboardHandler {
99
- private weak var viewController: UIViewController?
100
- private let onBarcodeScanned: (String) -> Void
101
- private var keyBuffer: String = ""
102
- private var lastKeyTime: TimeInterval = 0
103
- private let bufferTimeout: TimeInterval = 1.0
104
-
105
- // Terminatör tuş kodları
106
- private let terminatorKeyCodes: Set<Int> = [
107
- 36, // Return/Enter
108
- 76, // Numpad Enter
109
- 48, // Tab
110
- 52, // Numpad Equal (bazı okuyucularda)
111
- 67, // Numpad Asterisk (bazı okuyucularda)
112
- ]
66
+ /// GCKeyboard ile donanım klavyesi (Bluetooth barkod okuyucu) girişini izler.
67
+ /// Responder chain'e bağımlı olmadığı için tuş kaybı yaşanmaz ve
68
+ /// ekrandaki input'lar ile etkileşime girmez.
69
+ private final class HardwareKeyboardMonitor {
70
+ var onBarcodeScanned: ((String) -> Void)?
113
71
 
114
- init(viewController: UIViewController, onBarcodeScanned: @escaping (String) -> Void) {
115
- self.viewController = viewController
116
- self.onBarcodeScanned = onBarcodeScanned
117
- }
72
+ private var keyBuffer = ""
73
+ private var lastKeyTime: TimeInterval = 0
74
+ // Tuşlar arası bu süre aşılırsa tampon sıfırlanır (insan yazısını barkoddan ayırır)
75
+ private let bufferTimeout: TimeInterval = 0.5
76
+ private var connectObserver: NSObjectProtocol?
77
+ private var attachedInputs: [ObjectIdentifier: GCKeyboardInput] = [:]
118
78
 
119
- func startListening() {
120
- // pressesBegan/pressesEnded metodlarını sarmalamak için swizzling kullanacağız
121
- swizzlePressesMethods()
79
+ func start() {
80
+ // Halihazırda bağlı klavye varsa hemen dinlemeye başla
81
+ attach(to: GCKeyboard.coalesced)
122
82
 
123
- // Ayrıca UIResponder chain'den gelen tuş olaylarını da dinle
124
- setupKeyCommandHandling()
83
+ // Sonradan bağlanan klavyeler (örn. Bluetooth okuyucu) için bildirim dinle
84
+ connectObserver = NotificationCenter.default.addObserver(
85
+ forName: .GCKeyboardDidConnect,
86
+ object: nil,
87
+ queue: .main
88
+ ) { [weak self] notification in
89
+ self?.attach(to: notification.object as? GCKeyboard)
90
+ }
125
91
  }
126
92
 
127
- func stopListening() {
128
- unswizzlePressesMethods()
93
+ func stop() {
94
+ if let connectObserver {
95
+ NotificationCenter.default.removeObserver(connectObserver)
96
+ }
97
+ connectObserver = nil
98
+
99
+ for (_, input) in attachedInputs {
100
+ input.keyChangedHandler = nil
101
+ }
102
+ attachedInputs.removeAll()
129
103
  keyBuffer = ""
130
104
  }
131
105
 
132
- private func setupKeyCommandHandling() {
133
- // UIKeyCommand kullanarak bazı özel tuşları yakalayabiliriz
134
- // Ancak tüm tuşları yakalamak için pressesBegan/pressesEnded daha iyi
135
- }
106
+ private func attach(to keyboard: GCKeyboard?) {
107
+ guard let input = keyboard?.keyboardInput else { return }
136
108
 
137
- // MARK: - Tuş İşleme
109
+ let id = ObjectIdentifier(input)
110
+ guard attachedInputs[id] == nil else { return }
111
+ attachedInputs[id] = input
138
112
 
139
- func handlePress(_ press: UIPress) -> Bool {
140
- guard let key = press.key else { return false }
113
+ input.keyChangedHandler = { [weak self] keyboardInput, _, keyCode, pressed in
114
+ guard pressed else { return }
115
+ self?.handleKey(keyCode, input: keyboardInput)
116
+ }
117
+ }
141
118
 
142
- let keyCode = key.keyCode.rawValue
119
+ private func handleKey(_ keyCode: GCKeyCode, input: GCKeyboardInput) {
143
120
  let now = Date().timeIntervalSince1970
144
-
145
- // Zaman aşımı kontrolü - tuşlar arası 1 saniyeden fazla varsa tamponu sıfırla
146
121
  if now - lastKeyTime > bufferTimeout {
147
122
  keyBuffer = ""
148
123
  }
149
124
  lastKeyTime = now
150
125
 
151
- // Terminatör tuş kontrolü
152
- if isTerminator(keyCode) {
153
- if !keyBuffer.isEmpty {
154
- let code = keyBuffer
155
- keyBuffer = ""
156
- onBarcodeScanned(code)
157
- return true // Tuşu yut
126
+ // Terminatör: Enter / Numpad Enter / Tab
127
+ if keyCode == .returnOrEnter || keyCode == .keypadEnter || keyCode == .tab {
128
+ let code = keyBuffer
129
+ keyBuffer = ""
130
+ if !code.isEmpty {
131
+ let callback = onBarcodeScanned
132
+ DispatchQueue.main.async {
133
+ callback?(code)
134
+ }
158
135
  }
159
- // Tampon boşken terminatörü normal davranışa bırak
160
- return false
136
+ return
161
137
  }
162
138
 
163
- // Karakter tuşu mu?
164
- if let char = characterFromKey(key) {
139
+ let shiftPressed =
140
+ input.button(forKeyCode: .leftShift)?.isPressed == true ||
141
+ input.button(forKeyCode: .rightShift)?.isPressed == true
142
+
143
+ if let char = Self.character(for: keyCode, shifted: shiftPressed) {
165
144
  keyBuffer.append(char)
166
- return true // Tuşu yut (UI'ya ulaşmasını engelle)
167
145
  }
168
-
169
- return false
170
146
  }
171
147
 
172
- private func isTerminator(_ keyCode: Int) -> Bool {
173
- return terminatorKeyCodes.contains(keyCode)
174
- }
148
+ // MARK: - GCKeyCode Karakter eşleştirmesi
175
149
 
176
- private func characterFromKey(_ key: UIKey) -> Character? {
177
- // Karakter önceliği:
178
- // 1. modifiersiz karakter (temel ASCII)
179
- // 2. modifiers varsa ve shift varsa büyük harf
180
-
181
- var char: Character?
182
-
183
- // UIKey.characters ile dene (String, Optional değil)
184
- let chars = key.characters
185
- if !chars.isEmpty {
186
- char = chars.first
187
- }
188
-
189
- // UIKey.charactersIgnoringModifiers ile dene (String, Optional değil)
190
- if char == nil {
191
- let charsIgnoringModifiers = key.charactersIgnoringModifiers
192
- if !charsIgnoringModifiers.isEmpty {
193
- char = charsIgnoringModifiers.first
194
- }
150
+ private static func character(for keyCode: GCKeyCode, shifted: Bool) -> Character? {
151
+ if let letter = letters[keyCode] {
152
+ return shifted ? Character(letter.uppercased()) : letter
195
153
  }
196
-
197
- // Eğer hala nil ise, keyCode'dan çevir
198
- if char == nil {
199
- char = characterFromKeyCode(key.keyCode.rawValue)
154
+ if let pair = symbols[keyCode] {
155
+ return shifted ? pair.shifted : pair.normal
200
156
  }
201
-
202
- return char
157
+ return keypad[keyCode]
203
158
  }
204
159
 
205
- private func characterFromKeyCode(_ keyCode: Int) -> Character? {
206
- // iOS fiziksel klavye key kodlarından karakter dönüşümü
207
- // Bluetooth barkod okuyucular genellikle standart USB HID key kodları kullanır
208
- let mapping: [Int: Character] = [
209
- // Rakamlar (ana klavye)
210
- 23: "0", 22: "1", 26: "2", 20: "3", 25: "4",
211
- 29: "5", 27: "6", 24: "7", 28: "8", 21: "9",
212
-
213
- // Numpad rakamlar
214
- 98: "0", 89: "1", 90: "2", 91: "3", 92: "4",
215
- 93: "5", 94: "6", 95: "7", 96: "8", 97: "9",
216
-
217
- // Harfler (ana klavye)
218
- 12: "q", 13: "w", 14: "e", 15: "r", 17: "t",
219
- 16: "y", 32: "u", 34: "i", 31: "o", 35: "p",
220
- 0: "a", 1: "s", 2: "d", 3: "f", 5: "g",
221
- 4: "h", 38: "k", 37: "j", 40: "l", 6: "z",
222
- 7: "x", 8: "c", 9: "v", 11: "b", 45: "n",
223
- 46: "m",
224
-
225
- // Özel karakterler
226
- 51: "\u{0008}", // Backspace (silme)
227
- 117: "\u{007F}", // Delete
228
- 42: ",", 43: "-", 44: ".", 47: "/",
229
- 39: "'", 33: "[", 30: "]", 41: ";", 50: "`",
230
- 115: "\u{001B}", // Escape
231
-
232
- // Boşluk
233
- 49: " ",
234
- ]
235
-
236
- return mapping[keyCode]
237
- }
238
-
239
- // MARK: - Method Swizzling
240
-
241
- private func swizzlePressesMethods() {
242
- guard let viewController = viewController else { return }
243
-
244
- let originalPressesBeganSelector = #selector(UIViewController.pressesBegan(_:with:))
245
- let swizzledPressesBeganSelector = #selector(UIViewController.raxon_pressesBegan(_:with:))
246
-
247
- let originalPressesEndedSelector = #selector(UIViewController.pressesEnded(_:with:))
248
- let swizzledPressesEndedSelector = #selector(UIViewController.raxon_pressesEnded(_:with:))
249
-
250
- swizzleMethod(
251
- for: UIViewController.self,
252
- originalSelector: originalPressesBeganSelector,
253
- swizzledSelector: swizzledPressesBeganSelector
254
- )
255
-
256
- swizzleMethod(
257
- for: UIViewController.self,
258
- originalSelector: originalPressesEndedSelector,
259
- swizzledSelector: swizzledPressesEndedSelector
260
- )
261
-
262
- // Bu view controller'a weak referans ile handler'a erişim sağla
263
- objc_setAssociatedObject(
264
- viewController,
265
- &AssociatedKeys.keyboardHandler,
266
- self,
267
- .OBJC_ASSOCIATION_RETAIN_NONATOMIC
268
- )
269
- }
270
-
271
- private func unswizzlePressesMethods() {
272
- // Method swizzling geri alınamaz ama handler'ı temizleyebiliriz
273
- if let viewController = viewController {
274
- objc_setAssociatedObject(
275
- viewController,
276
- &AssociatedKeys.keyboardHandler,
277
- nil,
278
- .OBJC_ASSOCIATION_RETAIN_NONATOMIC
279
- )
280
- }
281
- }
282
-
283
- private func swizzleMethod(
284
- for classType: AnyClass,
285
- originalSelector: Selector,
286
- swizzledSelector: Selector
287
- ) {
288
- guard let originalMethod = class_getInstanceMethod(classType, originalSelector),
289
- let swizzledMethod = class_getInstanceMethod(classType, swizzledSelector) else {
290
- return
291
- }
292
-
293
- let didAddMethod = class_addMethod(
294
- classType,
295
- originalSelector,
296
- method_getImplementation(swizzledMethod),
297
- method_getTypeEncoding(swizzledMethod)
298
- )
299
-
300
- if didAddMethod {
301
- class_replaceMethod(
302
- classType,
303
- swizzledSelector,
304
- method_getImplementation(originalMethod),
305
- method_getTypeEncoding(originalMethod)
306
- )
307
- } else {
308
- method_exchangeImplementations(originalMethod, swizzledMethod)
309
- }
310
- }
311
- }
160
+ private static let letters: [GCKeyCode: Character] = [
161
+ .keyA: "a", .keyB: "b", .keyC: "c", .keyD: "d", .keyE: "e",
162
+ .keyF: "f", .keyG: "g", .keyH: "h", .keyI: "i", .keyJ: "j",
163
+ .keyK: "k", .keyL: "l", .keyM: "m", .keyN: "n", .keyO: "o",
164
+ .keyP: "p", .keyQ: "q", .keyR: "r", .keyS: "s", .keyT: "t",
165
+ .keyU: "u", .keyV: "v", .keyW: "w", .keyX: "x", .keyY: "y",
166
+ .keyZ: "z",
167
+ ]
312
168
 
313
- // MARK: - AssociatedKeys
169
+ private static let symbols: [GCKeyCode: (normal: Character, shifted: Character)] = [
170
+ .one: ("1", "!"), .two: ("2", "@"), .three: ("3", "#"),
171
+ .four: ("4", "$"), .five: ("5", "%"), .six: ("6", "^"),
172
+ .seven: ("7", "&"), .eight: ("8", "*"), .nine: ("9", "("),
173
+ .zero: ("0", ")"),
174
+ .spacebar: (" ", " "),
175
+ .hyphen: ("-", "_"),
176
+ .equalSign: ("=", "+"),
177
+ .openBracket: ("[", "{"),
178
+ .closeBracket: ("]", "}"),
179
+ .backslash: ("\\", "|"),
180
+ .semicolon: (";", ":"),
181
+ .quote: ("'", "\""),
182
+ .graveAccentAndTilde: ("`", "~"),
183
+ .comma: (",", "<"),
184
+ .period: (".", ">"),
185
+ .slash: ("/", "?"),
186
+ ]
314
187
 
315
- private enum AssociatedKeys {
316
- static var keyboardHandler: UInt8 = 0
188
+ private static let keypad: [GCKeyCode: Character] = [
189
+ .keypad0: "0", .keypad1: "1", .keypad2: "2", .keypad3: "3",
190
+ .keypad4: "4", .keypad5: "5", .keypad6: "6", .keypad7: "7",
191
+ .keypad8: "8", .keypad9: "9",
192
+ .keypadSlash: "/", .keypadAsterisk: "*",
193
+ .keypadHyphen: "-", .keypadPlus: "+",
194
+ .keypadPeriod: ".", .keypadEqualSign: "=",
195
+ ]
317
196
  }
318
-
319
- // MARK: - UIViewController Extension
320
-
321
- @available(iOS 13.4, *)
322
- extension UIViewController {
323
- @objc dynamic func raxon_pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
324
- // Orijinal implementasyonu çağır (swizzling yüzünden bu aslında orijinal)
325
- raxon_pressesBegan(presses, with: event)
326
-
327
- // Handler varsa tuşları işle
328
- if let handler = objc_getAssociatedObject(self, &AssociatedKeys.keyboardHandler)
329
- as? ExternalKeyboardHandler {
330
- for press in presses {
331
- _ = handler.handlePress(press)
332
- }
333
- }
334
- }
335
-
336
- @objc dynamic func raxon_pressesEnded(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
337
- // Orijinal implementasyonu çağır
338
- raxon_pressesEnded(presses, with: event)
339
- }
340
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raxon-barcode-scanner",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Zebra and enterprise barcode scanner hook for React Native Expo apps",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -46,7 +46,11 @@
46
46
  "expo-module.config.json",
47
47
  "src"
48
48
  ],
49
- "dependencies": {},
49
+ "dependencies": {
50
+ "expo": "~56.0.0-preview.9",
51
+ "react": "19.2.3",
52
+ "react-native": "0.85.3"
53
+ },
50
54
  "devDependencies": {
51
55
  "@babel/core": "^7.26.0",
52
56
  "@types/jest": "^29.2.1",
@@ -6,8 +6,8 @@ import {
6
6
  } from './RaxonBarcodeScanner.types';
7
7
 
8
8
  declare class RaxonBarcodeScannerModule extends NativeModule<RaxonBarcodeScannerModuleEvents> {
9
- startListening(options?: BarcodeScannerOptions): void;
10
- stopListening(): void;
9
+ startListening(options: BarcodeScannerOptions): Promise<void>;
10
+ stopListening(): Promise<void>;
11
11
  }
12
12
 
13
13
  export default requireNativeModule<RaxonBarcodeScannerModule>('RaxonBarcodeScanner');
@@ -23,14 +23,18 @@ export function useBarcodeScanner(
23
23
  return;
24
24
  }
25
25
 
26
- RaxonBarcodeScanner.startListening(options);
26
+ let subscription: { remove: () => void } | null = null;
27
27
 
28
- const subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
29
- onReadBarcodeRef.current(event);
30
- });
28
+ (async () => {
29
+ await RaxonBarcodeScanner.startListening(options ?? {});
30
+
31
+ subscription = RaxonBarcodeScanner.addListener('onBarcodeScanned', (event) => {
32
+ onReadBarcodeRef.current(event);
33
+ });
34
+ })();
31
35
 
32
36
  return () => {
33
- subscription.remove();
37
+ subscription?.remove();
34
38
  RaxonBarcodeScanner.stopListening();
35
39
  };
36
40
  }, [