@ruhiverse/thermal-printer-plugin 1.0.8 → 1.0.11

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.
@@ -2,6 +2,8 @@
2
2
  <uses-permission android:name="android.permission.BLUETOOTH" />
3
3
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
4
4
  <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
5
- <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
5
+ <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" />
6
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
7
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
6
8
  <uses-permission android:name="android.permission.USB_PERMISSION" />
7
9
  </manifest>
@@ -4,6 +4,8 @@ import android.Manifest;
4
4
  import android.app.Activity;
5
5
  import android.app.AlertDialog;
6
6
  import android.app.PendingIntent;
7
+ import android.bluetooth.BluetoothAdapter;
8
+ import android.bluetooth.BluetoothDevice;
7
9
  import android.content.BroadcastReceiver;
8
10
  import android.content.Context;
9
11
  import android.content.Intent;
@@ -35,6 +37,8 @@ import com.ruhiverse.thermalprinter.AsyncBluetoothEscPosPrint;
35
37
  import com.ruhiverse.thermalprinter.AsyncEscPosPrint;
36
38
  import com.ruhiverse.thermalprinter.AsyncEscPosPrinter;
37
39
  import com.ruhiverse.thermalprinter.AsyncUsbEscPosPrint;
40
+ import java.util.HashSet;
41
+ import java.util.Set;
38
42
 
39
43
  @CapacitorPlugin(
40
44
  name = "ThermalPrinter",
@@ -60,6 +64,8 @@ public class ThermalPrinterPlugin extends Plugin {
60
64
  public static final int PERMISSION_BLUETOOTH_CONNECT = 3;
61
65
  public static final int PERMISSION_BLUETOOTH_SCAN = 4;
62
66
  private static final String ACTION_USB_PERMISSION = "com.ruhiverse.thermalprinter.USB_PERMISSION";
67
+
68
+
63
69
 
64
70
  private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
65
71
  public void onReceive(Context context, Intent intent) {
@@ -263,34 +269,84 @@ public class ThermalPrinterPlugin extends Plugin {
263
269
  this.checkBluetoothPermissions(
264
270
  () -> {
265
271
  try {
266
- BluetoothConnection[] bluetoothPrinters = (new BluetoothPrintersConnections()).getList();
267
272
  JSArray printers = new JSArray();
273
+ Set<String> addedAddresses = new HashSet<>();
268
274
 
269
- if (bluetoothPrinters != null && bluetoothPrinters.length > 0) {
270
- for (BluetoothConnection printer : bluetoothPrinters) {
275
+ // Method 1: Get ALL paired Bluetooth devices directly from BluetoothAdapter
276
+ // This is more reliable than BluetoothPrintersConnections which filters by device class
277
+ BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
278
+
279
+ if (bluetoothAdapter == null) {
280
+ Log.e(TAG, "Bluetooth not supported on this device");
281
+ JSObject ret = new JSObject();
282
+ ret.put("printers", printers);
283
+ call.resolve(ret);
284
+ return;
285
+ }
286
+
287
+ if (!bluetoothAdapter.isEnabled()) {
288
+ Log.e(TAG, "Bluetooth is not enabled");
289
+ call.reject("Bluetooth is not enabled. Please turn on Bluetooth.");
290
+ return;
291
+ }
292
+
293
+ Set<BluetoothDevice> bondedDevices = bluetoothAdapter.getBondedDevices();
294
+ Log.d(TAG, "Found " + (bondedDevices != null ? bondedDevices.size() : 0) + " paired Bluetooth devices");
295
+
296
+ if (bondedDevices != null) {
297
+ for (BluetoothDevice device : bondedDevices) {
271
298
  try {
272
- JSObject printerObj = new JSObject();
273
- String name = printer.getDevice().getName();
274
- String address = printer.getDevice().getAddress();
299
+ String name = device.getName();
300
+ String address = device.getAddress();
275
301
 
276
- // Handle null/empty names
277
302
  if (name == null || name.isEmpty()) {
278
303
  name = "Unknown Device";
279
304
  }
305
+ if (address == null) {
306
+ continue;
307
+ }
280
308
 
309
+ JSObject printerObj = new JSObject();
281
310
  printerObj.put("name", name);
282
- printerObj.put("address", address != null ? address : "");
311
+ printerObj.put("address", address);
283
312
  printers.put(printerObj);
313
+ addedAddresses.add(address);
284
314
 
285
- Log.d(TAG, "Found Bluetooth printer: " + name + " (" + address + ")");
315
+ Log.d(TAG, "Paired device: " + name + " (" + address + ")");
286
316
  } catch (Exception e) {
287
- Log.e(TAG, "Error processing printer: " + e.getMessage());
317
+ Log.e(TAG, "Error processing paired device: " + e.getMessage());
288
318
  }
289
319
  }
290
- } else {
291
- Log.d(TAG, "No paired Bluetooth printers found. Make sure printers are paired in Android Settings.");
292
320
  }
293
321
 
322
+ // Method 2: Also try the ESCPOS library (may find additional devices)
323
+ try {
324
+ BluetoothConnection[] escposPrinters = (new BluetoothPrintersConnections()).getList();
325
+ if (escposPrinters != null) {
326
+ for (BluetoothConnection printer : escposPrinters) {
327
+ String address = printer.getDevice().getAddress();
328
+ if (address != null && !addedAddresses.contains(address)) {
329
+ String name = printer.getDevice().getName();
330
+ if (name == null || name.isEmpty()) {
331
+ name = "Unknown Device";
332
+ }
333
+
334
+ JSObject printerObj = new JSObject();
335
+ printerObj.put("name", name);
336
+ printerObj.put("address", address);
337
+ printers.put(printerObj);
338
+ addedAddresses.add(address);
339
+
340
+ Log.d(TAG, "ESCPOS printer: " + name + " (" + address + ")");
341
+ }
342
+ }
343
+ }
344
+ } catch (Exception e) {
345
+ Log.d(TAG, "ESCPOS library scan: " + e.getMessage());
346
+ }
347
+
348
+ Log.d(TAG, "Total printers found: " + printers.length());
349
+
294
350
  JSObject ret = new JSObject();
295
351
  ret.put("printers", printers);
296
352
  call.resolve(ret);
@@ -286,26 +286,32 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
286
286
  print("ThermalPrinter: listBluetoothPrinters called")
287
287
  var printers: [[String: String]] = []
288
288
 
289
- // First, get MFi/ExternalAccessory printers
289
+ // Method 1: Get MFi/ExternalAccessory printers (classic Bluetooth printers)
290
+ // Most thermal printers use classic Bluetooth, which on iOS requires
291
+ // ExternalAccessory framework + MFi certification
290
292
  let manager = EAAccessoryManager.shared()
291
- let accessories = manager.connectedAccessories
293
+ let allAccessories = manager.connectedAccessories
292
294
 
293
- print("ThermalPrinter: Found \(accessories.count) ExternalAccessory devices")
295
+ print("ThermalPrinter: Found \(allAccessories.count) total ExternalAccessory devices")
294
296
 
295
- let mfiPrinters: [[String: String]] = accessories.compactMap { accessory in
296
- guard accessory.protocolStrings.contains(where: { supportedPrinterProtocols.contains($0) }) else {
297
- return nil
298
- }
299
- print("ThermalPrinter: Found MFi printer: \(accessory.name)")
300
- return [
301
- "name": accessory.name,
302
- "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
303
- ]
297
+ // Return ALL connected accessories (not just those matching printer protocols)
298
+ // because many printers use custom protocol strings
299
+ for accessory in allAccessories {
300
+ let name = accessory.name
301
+ let address = accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber
302
+ let protocols = accessory.protocolStrings.joined(separator: ", ")
303
+
304
+ print("ThermalPrinter: Accessory: \(name), protocols: [\(protocols)]")
305
+
306
+ printers.append([
307
+ "name": name,
308
+ "address": address,
309
+ ])
304
310
  }
305
- printers.append(contentsOf: mfiPrinters)
306
- print("ThermalPrinter: Found \(mfiPrinters.count) MFi printers")
307
311
 
308
- // Then scan for BLE printers
312
+ print("ThermalPrinter: Found \(printers.count) ExternalAccessory printers")
313
+
314
+ // Method 2: Also scan for BLE printers (some thermal printers support BLE)
309
315
  discoveredPeripherals.removeAll()
310
316
  listPrintersCall = call
311
317
 
@@ -323,69 +329,59 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
323
329
 
324
330
  print("ThermalPrinter: Bluetooth state: \(centralManager.state.rawValue)")
325
331
 
326
- // Check Bluetooth state
327
332
  switch centralManager.state {
328
333
  case .poweredOn:
329
- // Start scanning immediately
330
- print("ThermalPrinter: Bluetooth powered on, starting scan")
334
+ print("ThermalPrinter: Bluetooth powered on, starting BLE scan")
331
335
  startBLEScan(for: call, existingPrinters: printers)
332
336
  case .poweredOff:
333
- print("ThermalPrinter: Bluetooth powered off, returning \(printers.count) printers")
337
+ print("ThermalPrinter: Bluetooth powered off")
338
+ // Still return any MFi printers we found
334
339
  call.resolve(["printers": printers])
340
+ listPrintersCall = nil
335
341
  case .unauthorized:
336
342
  print("ThermalPrinter: Bluetooth unauthorized")
337
- call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
338
- case .unsupported:
339
- print("ThermalPrinter: Bluetooth unsupported")
340
- call.reject("Bluetooth is not supported on this device.")
343
+ // Still return any MFi printers, but warn about BLE
344
+ call.resolve(["printers": printers])
345
+ listPrintersCall = nil
341
346
  case .resetting, .unknown:
342
347
  print("ThermalPrinter: Bluetooth state unknown/resetting, waiting for update")
343
- // Wait for state update
344
- // The state will be updated via centralManagerDidUpdateState
345
348
  break
346
- @unknown default:
347
- print("ThermalPrinter: Unknown Bluetooth state, returning \(printers.count) printers")
349
+ default:
348
350
  call.resolve(["printers": printers])
351
+ listPrintersCall = nil
349
352
  }
350
353
  }
351
354
 
352
355
  private func startBLEScan(for call: CAPPluginCall, existingPrinters: [[String: String]]) {
353
- guard let centralManager = centralManager else {
354
- call.resolve(["printers": existingPrinters])
355
- return
356
- }
357
-
358
- guard centralManager.state == .poweredOn else {
356
+ guard let centralManager = centralManager, centralManager.state == .poweredOn else {
359
357
  call.resolve(["printers": existingPrinters])
358
+ listPrintersCall = nil
360
359
  return
361
360
  }
362
361
 
363
- // Clear previous discoveries
364
362
  discoveredPeripherals.removeAll()
365
363
 
366
- // Scan for all peripherals
367
364
  print("ThermalPrinter: Starting BLE scan...")
368
365
  centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
369
366
 
370
- // Stop scanning after 8 seconds (increased from 5 to give more time)
371
- DispatchQueue.main.asyncAfter(deadline: .now() + 8.0) {
367
+ // Scan for 4 seconds (shorter BLE devices respond quickly)
368
+ DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
372
369
  centralManager.stopScan()
373
370
  print("ThermalPrinter: Stopped BLE scan. Found \(self.discoveredPeripherals.count) peripherals")
374
371
 
375
372
  var printers = existingPrinters
376
373
 
377
- // Add discovered BLE printers - return all devices, even without names
378
374
  let blePrinters: [[String: String]] = self.discoveredPeripherals.compactMap { peripheral in
379
- // Include all peripherals, even if they don't have names
380
- // Some printers don't advertise names until connected
381
- let name = peripheral.name ?? "Unknown Device"
375
+ guard let name = peripheral.name, !name.isEmpty else {
376
+ return nil
377
+ }
382
378
  return [
383
379
  "name": name,
384
380
  "address": peripheral.identifier.uuidString,
385
381
  ]
386
382
  }
387
383
 
388
- print("ThermalPrinter: Returning \(blePrinters.count) BLE printers")
384
+ print("ThermalPrinter: Returning \(blePrinters.count) BLE printers + \(existingPrinters.count) MFi printers")
389
385
  printers.append(contentsOf: blePrinters)
390
386
  call.resolve(["printers": printers])
391
387
  self.listPrintersCall = nil
@@ -399,36 +395,25 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
399
395
 
400
396
  // If we're waiting to list printers, start scanning now
401
397
  if let call = listPrintersCall {
398
+ // Get MFi printers
399
+ var mfiPrinters: [[String: String]] = []
400
+ let manager = EAAccessoryManager.shared()
401
+ for accessory in manager.connectedAccessories {
402
+ mfiPrinters.append([
403
+ "name": accessory.name,
404
+ "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
405
+ ])
406
+ }
407
+
402
408
  switch central.state {
403
409
  case .poweredOn:
404
- print("ThermalPrinter: Bluetooth now powered on, starting scan")
405
- // Get existing MFi printers first
406
- let manager = EAAccessoryManager.shared()
407
- let accessories = manager.connectedAccessories
408
- let mfiPrinters: [[String: String]] = accessories.compactMap { accessory in
409
- guard accessory.protocolStrings.contains(where: { supportedPrinterProtocols.contains($0) }) else {
410
- return nil
411
- }
412
- return [
413
- "name": accessory.name,
414
- "address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
415
- ]
416
- }
410
+ print("ThermalPrinter: Bluetooth now powered on, starting BLE scan")
417
411
  startBLEScan(for: call, existingPrinters: mfiPrinters)
418
- case .poweredOff:
419
- print("ThermalPrinter: Bluetooth powered off")
420
- call.resolve(["printers": []])
421
- listPrintersCall = nil
422
- case .unauthorized:
423
- print("ThermalPrinter: Bluetooth unauthorized")
424
- call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
425
- listPrintersCall = nil
426
- case .unsupported:
427
- print("ThermalPrinter: Bluetooth unsupported")
428
- call.reject("Bluetooth is not supported on this device.")
412
+ case .poweredOff, .unauthorized, .unsupported:
413
+ print("ThermalPrinter: Bluetooth not available (state: \(central.state.rawValue))")
414
+ call.resolve(["printers": mfiPrinters])
429
415
  listPrintersCall = nil
430
416
  default:
431
- print("ThermalPrinter: Bluetooth state: \(central.state.rawValue)")
432
417
  break
433
418
  }
434
419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruhiverse/thermal-printer-plugin",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "Capacitor plugin for thermal printing via USB and Bluetooth",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",