@nitra/zebra 6.2.0 → 7.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.
Files changed (31) hide show
  1. package/NitraZebra.podspec +16 -13
  2. package/Package.swift +28 -0
  3. package/README.md +72 -0
  4. package/android/build.gradle +16 -14
  5. package/android/src/main/AndroidManifest.xml +2 -22
  6. package/android/src/main/java/dev/nitra/zebra/Zebra.java +11 -0
  7. package/android/src/main/java/dev/nitra/zebra/ZebraPlugin.java +22 -0
  8. package/dist/plugin.js +842 -1296
  9. package/dist/plugin.js.map +1 -1
  10. package/ios/Sources/ZebraPlugin/Zebra.swift +8 -0
  11. package/ios/Sources/ZebraPlugin/ZebraPlugin.swift +23 -0
  12. package/ios/Tests/ZebraPluginTests/ZebraTests.swift +15 -0
  13. package/package.json +46 -54
  14. package/android/.gradle/8.9/checksums/checksums.lock +0 -0
  15. package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
  16. package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
  17. package/android/.gradle/8.9/gc.properties +0 -0
  18. package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
  19. package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
  20. package/android/.gradle/vcs-1/gc.properties +0 -0
  21. package/android/.project +0 -28
  22. package/android/capacitor.settings.gradle +0 -3
  23. package/android/gradle.properties +0 -19
  24. package/android/proguard-rules.pro +0 -21
  25. package/android/src/main/java/com/nitra/zebra_printer_plugin/ZebraPrinter.java +0 -1418
  26. package/android/src/main/res/xml/capacitor_plugins.xml +0 -4
  27. package/android/variables.gradle +0 -14
  28. package/ios/Info.plist +0 -36
  29. package/ios/Plugin/ZebraPrinterPlugin.m +0 -16
  30. package/ios/Plugin/ZebraPrinterPlugin.swift +0 -465
  31. /package/android/{.gradle/8.9/dependencies-accessors/gc.properties → src/main/res/.gitkeep} +0 -0
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <plugins>
3
- <plugin name="ZebraPrinter" class="com.nitra.zebra_printer_plugin.ZebraPrinter" />
4
- </plugins>
@@ -1,14 +0,0 @@
1
- ext {
2
- minSdkVersion = 22
3
- compileSdkVersion = 34
4
- targetSdkVersion = 34
5
- androidxActivityVersion = '1.8.2'
6
- androidxAppCompatVersion = '1.6.1'
7
- androidxCoordinatorLayoutVersion = '1.2.0'
8
- androidxCoreVersion = '1.12.0'
9
- androidxFragmentVersion = '1.6.2'
10
- junitVersion = '4.13.2'
11
- androidxJunitVersion = '1.1.5'
12
- androidxEspressoCoreVersion = '3.5.1'
13
- cordovaAndroidVersion = '12.0.0'
14
- }
package/ios/Info.plist DELETED
@@ -1,36 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
- <plist version="1.0">
4
- <dict>
5
- <!-- Bluetooth permissions for iOS 13+ -->
6
- <key>NSBluetoothAlwaysUsageDescription</key>
7
- <string>This app needs Bluetooth access to connect to Zebra printers for printing labels and
8
- receipts.</string>
9
-
10
- <!-- Legacy Bluetooth permissions for iOS 12 and earlier -->
11
- <key>NSBluetoothPeripheralUsageDescription</key>
12
- <string>This app needs Bluetooth access to connect to Zebra printers for printing labels and
13
- receipts.</string>
14
-
15
- <!-- Background modes for Bluetooth -->
16
- <key>UIBackgroundModes</key>
17
- <array>
18
- <string>bluetooth-central</string>
19
- </array>
20
-
21
- <!-- Required device capabilities -->
22
- <key>UIRequiredDeviceCapabilities</key>
23
- <array>
24
- <string>bluetooth-le</string>
25
- </array>
26
-
27
- <!-- Privacy usage descriptions for better permission handling -->
28
- <key>NSLocationWhenInUseUsageDescription</key>
29
- <string>This app needs location access to scan for nearby Bluetooth devices like Zebra
30
- printers.</string>
31
-
32
- <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
33
- <string>This app needs location access to scan for nearby Bluetooth devices like Zebra
34
- printers.</string>
35
- </dict>
36
- </plist>
@@ -1,16 +0,0 @@
1
- #import <Foundation/Foundation.h>
2
- #import <Capacitor/Capacitor.h>
3
-
4
- // Define the plugin using the CAP_PLUGIN macro
5
- CAP_PLUGIN(ZebraPrinterPlugin, "ZebraPrinter",
6
- CAP_PLUGIN_METHOD(echo, CAPPluginReturnPromise);
7
- CAP_PLUGIN_METHOD(checkPermissions, CAPPluginReturnPromise);
8
- CAP_PLUGIN_METHOD(requestPermissions, CAPPluginReturnPromise);
9
- CAP_PLUGIN_METHOD(printText, CAPPluginReturnPromise);
10
- CAP_PLUGIN_METHOD(getStatus, CAPPluginReturnPromise);
11
- CAP_PLUGIN_METHOD(checkPrinterStatus, CAPPluginReturnPromise);
12
- CAP_PLUGIN_METHOD(scanForPrinters, CAPPluginReturnPromise);
13
- CAP_PLUGIN_METHOD(connect, CAPPluginReturnPromise);
14
- CAP_PLUGIN_METHOD(disconnect, CAPPluginReturnPromise);
15
- )
16
-
@@ -1,465 +0,0 @@
1
- import Foundation
2
- import Capacitor
3
- import CoreBluetooth
4
- import SystemConfiguration
5
-
6
- @objc(ZebraPrinterPlugin)
7
- public class ZebraPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeripheralDelegate {
8
- private var centralManager: CBCentralManager!
9
- private var connectedPeripheral: CBPeripheral?
10
- private var printerService: CBService?
11
- private var printerCharacteristic: CBCharacteristic?
12
- private var discoveredPeripherals: [CBPeripheral] = [] // Store discovered printers
13
-
14
- override public func load() {
15
- super.load()
16
- centralManager = CBCentralManager(delegate: self, queue: nil)
17
- }
18
-
19
- // MARK: - CBCentralManagerDelegate
20
- public func centralManagerDidUpdateState(_ central: CBCentralManager) {
21
- switch central.state {
22
- case .poweredOn:
23
- print("✅ Bluetooth is ON")
24
- // Automatically start scanning when Bluetooth is turned on
25
- print("🔍 Auto-starting scan...")
26
- central.scanForPeripherals(withServices: nil, options: nil)
27
- case .poweredOff:
28
- print("❌ Bluetooth is OFF")
29
- case .unauthorized:
30
- print("❌ Bluetooth unauthorized")
31
- case .unsupported:
32
- print("❌ Bluetooth unsupported")
33
- case .unknown:
34
- print("❌ Bluetooth state unknown")
35
- case .resetting:
36
- print("🔄 Bluetooth is resetting")
37
- @unknown default:
38
- print("❌ Unknown Bluetooth state: \(central.state)")
39
- }
40
- }
41
-
42
- public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
43
- print("📱 Found device: \(peripheral.name ?? "Unknown")")
44
-
45
- // Auto-connect to Zebra printer
46
- if peripheral.name?.contains("Zebra") == true ||
47
- peripheral.name?.contains("ZT") == true ||
48
- peripheral.name?.contains("XXZHN") == true { // Add specific printer ID
49
- print("🖨️ Found Zebra printer: \(peripheral.name ?? "Unknown")")
50
-
51
- // Store the printer so we keep a reference
52
- if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
53
- discoveredPeripherals.append(peripheral)
54
- }
55
-
56
- print("🔌 Auto-connecting to printer...")
57
- central.connect(peripheral, options: nil)
58
- }
59
- }
60
-
61
- public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
62
- print("✅ Connected to: \(peripheral.name ?? "Unknown")")
63
- connectedPeripheral = peripheral
64
- peripheral.delegate = self
65
- peripheral.discoverServices(nil)
66
-
67
- // Store the printer as the connected one
68
- if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
69
- discoveredPeripherals.append(peripheral)
70
- }
71
- }
72
-
73
- public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
74
- print("❌ Failed to connect: \(error?.localizedDescription ?? "Unknown error")")
75
- }
76
-
77
- public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
78
- print("🔌 Disconnected from: \(peripheral.name ?? "Unknown")")
79
- connectedPeripheral = nil
80
- printerService = nil
81
- printerCharacteristic = nil
82
- }
83
-
84
- // MARK: - CBPeripheralDelegate
85
- public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
86
- guard error == nil else {
87
- print("❌ Service discovery error: \(error!.localizedDescription)")
88
- return
89
- }
90
-
91
- for service in peripheral.services ?? [] {
92
- print("🔍 Found service: \(service.uuid)")
93
- peripheral.discoverCharacteristics(nil, for: service)
94
- }
95
- }
96
-
97
- public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
98
- guard error == nil else {
99
- print("❌ Characteristic discovery error: \(error!.localizedDescription)")
100
- return
101
- }
102
-
103
- for characteristic in service.characteristics ?? [] {
104
- print("🔍 Found characteristic: \(characteristic.uuid)")
105
-
106
- // Choose the correct characteristic - usually the one that has the "write" property
107
- if characteristic.properties.contains(.write) || characteristic.properties.contains(.writeWithoutResponse) {
108
- // If this is the first characteristic with a write property, use it
109
- if printerCharacteristic == nil {
110
- printerService = service
111
- printerCharacteristic = characteristic
112
- print("✅ Selected printer characteristic: \(characteristic.uuid)")
113
- }
114
- }
115
- }
116
- }
117
-
118
- // MARK: - Plugin Methods
119
- @objc public override func checkPermissions(_ call: CAPPluginCall) {
120
- print("🔍 Checking iOS Bluetooth permissions...")
121
-
122
- let bluetoothState = centralManager.state
123
- let hasBluetooth = bluetoothState == .poweredOn
124
- let isAuthorized = bluetoothState != .unauthorized
125
- let isSupported = bluetoothState != .unsupported
126
-
127
- call.resolve([
128
- "hasPermissions": hasBluetooth && isAuthorized,
129
- "bluetoothSupported": isSupported,
130
- "bleSupported": true, // iOS always supports BLE
131
- "bluetoothState": bluetoothState.rawValue,
132
- "missingPermissions": isAuthorized ? [] : ["NSBluetoothAlwaysUsageDescription"]
133
- ])
134
- }
135
-
136
- @objc public override func requestPermissions(_ call: CAPPluginCall) {
137
- print("🔐 Requesting iOS Bluetooth permissions...")
138
-
139
- // On iOS, permissions are handled through Info.plist
140
- // The actual permission request happens when the user first tries to use Bluetooth
141
- let bluetoothState = centralManager.state
142
-
143
- if bluetoothState == .unauthorized {
144
- call.reject("Bluetooth unauthorized", "Please allow Bluetooth access in Settings > Privacy & Security > Bluetooth")
145
- } else if bluetoothState == .poweredOff {
146
- call.reject("Bluetooth is OFF", "Please turn on Bluetooth in Settings")
147
- } else if bluetoothState == .poweredOn {
148
- call.resolve([
149
- "success": true,
150
- "message": "Bluetooth permissions granted"
151
- ])
152
- } else {
153
- call.reject("Bluetooth not ready", "Bluetooth state: \(bluetoothState.rawValue)")
154
- }
155
- }
156
-
157
- @objc func echo(_ call: CAPPluginCall) {
158
- let value = call.getString("value") ?? ""
159
- call.resolve([
160
- "value": value
161
- ])
162
- }
163
-
164
- // MARK: - Print text (FIXED - sends RAW ZPL)
165
- @objc func printText(_ call: CAPPluginCall) {
166
- let text = call.getString("text") ?? ""
167
- let fontSize = call.getInt("fontSize") ?? 12
168
- let bold = call.getBool("bold") ?? false
169
- let alignment = call.getString("alignment") ?? "left"
170
-
171
- print("Printing text: \(text)")
172
-
173
- // Check printer status first
174
- guard let peripheral = connectedPeripheral,
175
- let characteristic = printerCharacteristic else {
176
- call.reject("Print failed", "Not connected to printer")
177
- return
178
- }
179
-
180
- // CRITICAL FIX: Check if it's already a ZPL command
181
- var dataToSend: String
182
- if text.hasPrefix("^XA") || text.hasPrefix("~") || text.hasPrefix("!") {
183
- // It's already a ZPL/CPCL command - send it RAW
184
- print("Sending RAW ZPL/CPCL command...")
185
- dataToSend = text
186
- } else {
187
- // It's plain text - create ZPL wrapper with proper formatting
188
- print("Converting plain text to ZPL...")
189
- let x = alignment == "center" ? 200 : (alignment == "right" ? 350 : 50)
190
- let fontStyle = bold ? "B" : "N"
191
- // Improved ZPL command with proper line breaks and formatting
192
- dataToSend = "^XA\n^FO\(x),50^A0\(fontStyle),\(fontSize),\(fontSize)^FD\(text)^FS\n^XZ"
193
- }
194
-
195
- print("📦 Final data to send: \(dataToSend)")
196
-
197
- // Send to printer
198
- if let data = dataToSend.data(using: .utf8) {
199
- if sendDataToPrinter(data) {
200
- print("✅ Successfully sent \(data.count) bytes")
201
- call.resolve([
202
- "success": true,
203
- "message": "Data sent to printer",
204
- "zpl": dataToSend,
205
- "bytes": data.count
206
- ])
207
- } else {
208
- call.reject("Print failed", "Failed to send data to printer")
209
- }
210
- } else {
211
- call.reject("Print failed", "Invalid data encoding")
212
- }
213
- }
214
-
215
- @objc func getStatus(_ call: CAPPluginCall) {
216
- let isConnected = connectedPeripheral != nil && printerCharacteristic != nil
217
- var result: [String: Any] = [
218
- "connected": isConnected,
219
- "status": isConnected ? "connected" : "disconnected"
220
- ]
221
-
222
- if isConnected, let peripheral = connectedPeripheral {
223
- result["printerAddress"] = peripheral.identifier.uuidString
224
- result["printerType"] = "bluetooth"
225
- }
226
-
227
- call.resolve(result)
228
- }
229
-
230
- // NEW: Check printer status with ZPL command
231
- @objc func checkPrinterStatus(_ call: CAPPluginCall) {
232
- guard let peripheral = connectedPeripheral,
233
- let characteristic = printerCharacteristic else {
234
- call.reject("Status check failed", "Not connected to printer")
235
- return
236
- }
237
-
238
- // Send printer status command
239
- let statusCommand = "~HS" // Zebra printer status command
240
- if let data = statusCommand.data(using: .utf8) {
241
- if sendDataToPrinter(data) {
242
- call.resolve([
243
- "success": true,
244
- "message": "Status command sent",
245
- "command": statusCommand
246
- ])
247
- } else {
248
- call.reject("Status check failed", "Failed to send status command")
249
- }
250
- } else {
251
- call.reject("Status check failed", "Invalid status command")
252
- }
253
- }
254
-
255
- @objc func scanForPrinters(_ call: CAPPluginCall) {
256
- print("🔍 Starting printer scan...")
257
- print("📱 Bluetooth state: \(centralManager.state.rawValue)")
258
-
259
- switch centralManager.state {
260
- case .poweredOn:
261
- print("✅ Bluetooth is ON, starting scan...")
262
- centralManager.scanForPeripherals(withServices: nil, options: nil)
263
-
264
- // Return currently discovered printers
265
- var printers: [[String: Any]] = []
266
- for peripheral in discoveredPeripherals {
267
- if let name = peripheral.name,
268
- (name.contains("Zebra") || name.contains("ZT") || name.contains("XXZHN")) {
269
- printers.append([
270
- "name": name,
271
- "address": peripheral.identifier.uuidString,
272
- "type": "bluetooth",
273
- "paired": false // BLE devices are not "paired" in traditional sense
274
- ])
275
- }
276
- }
277
-
278
- call.resolve([
279
- "success": true,
280
- "printers": printers,
281
- "count": printers.count,
282
- "message": "Found \(printers.count) printer(s)"
283
- ])
284
- case .poweredOff:
285
- print("❌ Bluetooth is OFF")
286
- call.reject("Bluetooth is OFF", "Please turn on Bluetooth in Settings")
287
- case .unauthorized:
288
- print("❌ Bluetooth unauthorized")
289
- call.reject("Bluetooth unauthorized", "Please allow Bluetooth access in Settings")
290
- case .unsupported:
291
- print("❌ Bluetooth unsupported")
292
- call.reject("Bluetooth unsupported", "This device doesn't support Bluetooth")
293
- case .unknown:
294
- print("❌ Bluetooth state unknown")
295
- call.reject("Bluetooth state unknown", "Please check Bluetooth settings")
296
- case .resetting:
297
- print("🔄 Bluetooth is resetting")
298
- call.reject("Bluetooth is resetting", "Please wait and try again")
299
- @unknown default:
300
- print("❌ Unknown Bluetooth state")
301
- call.reject("Unknown Bluetooth state", "Please check Bluetooth settings")
302
- }
303
- }
304
-
305
- @objc func connect(_ call: CAPPluginCall) {
306
- print("🔌 Connect method called")
307
-
308
- // Get connection parameters
309
- let address = call.getString("address")
310
- let type = call.getString("type") ?? "bluetooth"
311
-
312
- print("📱 Connection request - Address: \(address ?? "none"), Type: \(type)")
313
-
314
- // For iOS, we only support Bluetooth connections
315
- if type != "bluetooth" {
316
- call.reject("Unsupported connection type", "iOS only supports Bluetooth connections")
317
- return
318
- }
319
-
320
- // If address is provided, try to connect to specific device
321
- if let targetAddress = address {
322
- // Find peripheral with matching address
323
- if let targetPeripheral = discoveredPeripherals.first(where: { $0.identifier.uuidString == targetAddress }) {
324
- print("🔌 Connecting to specific printer: \(targetPeripheral.name ?? "Unknown")")
325
- centralManager.connect(targetPeripheral, options: nil)
326
- call.resolve([
327
- "success": true,
328
- "connected": false,
329
- "address": targetAddress,
330
- "type": "bluetooth",
331
- "message": "Connecting to printer..."
332
- ])
333
- return
334
- } else {
335
- call.reject("Printer not found", "Printer with address \(targetAddress) not found")
336
- return
337
- }
338
- }
339
-
340
- // If no address provided, try to connect to first available printer
341
- if let firstPrinter = discoveredPeripherals.first {
342
- print("🔌 Connecting to first available printer: \(firstPrinter.name ?? "Unknown")")
343
- centralManager.connect(firstPrinter, options: nil)
344
- call.resolve([
345
- "success": true,
346
- "connected": false,
347
- "address": firstPrinter.identifier.uuidString,
348
- "type": "bluetooth",
349
- "message": "Connecting to printer..."
350
- ])
351
- } else {
352
- print("❌ No printers discovered")
353
- call.reject("No printers found", "No Zebra printers discovered. Please scan for printers first.")
354
- }
355
-
356
- }
357
-
358
- @objc func disconnect(_ call: CAPPluginCall) {
359
- if let peripheral = connectedPeripheral {
360
- centralManager.cancelPeripheralConnection(peripheral)
361
- }
362
- call.resolve([
363
- "success": true,
364
- "connected": false
365
- ])
366
- }
367
-
368
- // MARK: - Network Info (iOS)
369
- @objc func getNetworkInfo(_ call: CAPPluginCall) {
370
- let (ip, ssid) = getWiFiInfo()
371
- var result: [String: Any] = [
372
- "supported": true,
373
- "type": ip == nil ? "unknown" : "wifi"
374
- ]
375
- if let ip = ip {
376
- result["ip"] = ip
377
- }
378
- if let ssid = ssid {
379
- result["ssid"] = ssid
380
- }
381
- call.resolve(result)
382
- }
383
-
384
- // MARK: - Helper Methods
385
- private func sendDataToPrinter(_ data: Data) -> Bool {
386
- guard let peripheral = connectedPeripheral,
387
- let characteristic = printerCharacteristic else {
388
- print("❌ Not connected to printer")
389
- return false
390
- }
391
-
392
- // FIXED: Use proper BLE MTU size (20 bytes for BLE, 512 for BLE 4.2+)
393
- // But we'll use a conservative approach with 20 bytes chunks
394
- let mtuSize = 20 // Standard BLE MTU size
395
- print("📏 MTU size: \(mtuSize)")
396
-
397
- // Split data into chunks
398
- var offset = 0
399
- while offset < data.count {
400
- let chunkSize = min(mtuSize, data.count - offset)
401
- let chunk = data.subdata(in: offset..<(offset + chunkSize))
402
-
403
- print("📤 Sending chunk \(offset/mtuSize + 1): \(chunkSize) bytes")
404
-
405
- // Add delay before sending to ensure printer is ready
406
- if offset > 0 {
407
- Thread.sleep(forTimeInterval: 0.05) // 50ms delay between chunks
408
- }
409
-
410
- peripheral.writeValue(chunk, for: characteristic, type: .withoutResponse)
411
-
412
- offset += chunkSize
413
- }
414
-
415
- // Add final delay to ensure all data is processed
416
- Thread.sleep(forTimeInterval: 0.1)
417
-
418
- return true
419
- }
420
-
421
- private func getWiFiInfo() -> (String?, String?) {
422
- var address: String?
423
- var ssid: String?
424
-
425
- // IP address from en0
426
- var ifaddr: UnsafeMutablePointer<ifaddrs>?
427
- if getifaddrs(&ifaddr) == 0 {
428
- var ptr = ifaddr
429
- while ptr != nil {
430
- defer { ptr = ptr?.pointee.ifa_next }
431
- guard let interface = ptr?.pointee else { continue }
432
- let name = String(cString: interface.ifa_name)
433
- let addrFamily = interface.ifa_addr.pointee.sa_family
434
- if name == "en0", addrFamily == UInt8(AF_INET) {
435
- var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST))
436
- var addr = interface.ifa_addr.pointee
437
- let res = getnameinfo(&addr,
438
- socklen_t(interface.ifa_addr.pointee.sa_len),
439
- &hostname,
440
- socklen_t(hostname.count),
441
- nil,
442
- socklen_t(0),
443
- NI_NUMERICHOST)
444
- if res == 0 {
445
- address = String(cString: hostname)
446
- }
447
- }
448
- }
449
- freeifaddrs(ifaddr)
450
- }
451
-
452
- // SSID (best effort; may be nil without entitlements/permissions)
453
- if let interfaces = CNCopySupportedInterfaces() as? [String] {
454
- for interface in interfaces {
455
- if let dict = CNCopyCurrentNetworkInfo(interface as CFString) as NSDictionary? {
456
- ssid = dict[kCNNetworkInfoKeySSID as String] as? String
457
- break
458
- }
459
- }
460
- }
461
-
462
- return (address, ssid)
463
- }
464
- }
465
-