@ruhiverse/thermal-printer-plugin 1.0.7 → 1.0.9
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.
|
@@ -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;
|
|
@@ -11,6 +13,8 @@ import android.content.IntentFilter;
|
|
|
11
13
|
import android.content.pm.PackageManager;
|
|
12
14
|
import android.hardware.usb.UsbDevice;
|
|
13
15
|
import android.hardware.usb.UsbManager;
|
|
16
|
+
import android.os.Handler;
|
|
17
|
+
import android.os.Looper;
|
|
14
18
|
import android.util.Log;
|
|
15
19
|
import androidx.core.app.ActivityCompat;
|
|
16
20
|
import androidx.core.content.ContextCompat;
|
|
@@ -35,6 +39,8 @@ import com.ruhiverse.thermalprinter.AsyncBluetoothEscPosPrint;
|
|
|
35
39
|
import com.ruhiverse.thermalprinter.AsyncEscPosPrint;
|
|
36
40
|
import com.ruhiverse.thermalprinter.AsyncEscPosPrinter;
|
|
37
41
|
import com.ruhiverse.thermalprinter.AsyncUsbEscPosPrint;
|
|
42
|
+
import java.util.HashSet;
|
|
43
|
+
import java.util.Set;
|
|
38
44
|
|
|
39
45
|
@CapacitorPlugin(
|
|
40
46
|
name = "ThermalPrinter",
|
|
@@ -60,6 +66,12 @@ public class ThermalPrinterPlugin extends Plugin {
|
|
|
60
66
|
public static final int PERMISSION_BLUETOOTH_CONNECT = 3;
|
|
61
67
|
public static final int PERMISSION_BLUETOOTH_SCAN = 4;
|
|
62
68
|
private static final String ACTION_USB_PERMISSION = "com.ruhiverse.thermalprinter.USB_PERMISSION";
|
|
69
|
+
|
|
70
|
+
// Bluetooth discovery
|
|
71
|
+
private Set<BluetoothDevice> discoveredDevices = new HashSet<>();
|
|
72
|
+
private PluginCall discoveryCall;
|
|
73
|
+
private BroadcastReceiver discoveryReceiver;
|
|
74
|
+
private Handler discoveryHandler = new Handler(Looper.getMainLooper());
|
|
63
75
|
|
|
64
76
|
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
|
|
65
77
|
public void onReceive(Context context, Intent intent) {
|
|
@@ -263,43 +275,203 @@ public class ThermalPrinterPlugin extends Plugin {
|
|
|
263
275
|
this.checkBluetoothPermissions(
|
|
264
276
|
() -> {
|
|
265
277
|
try {
|
|
266
|
-
BluetoothConnection[] bluetoothPrinters = (new BluetoothPrintersConnections()).getList();
|
|
267
278
|
JSArray printers = new JSArray();
|
|
268
|
-
|
|
279
|
+
discoveredDevices.clear();
|
|
280
|
+
discoveryCall = call;
|
|
281
|
+
|
|
282
|
+
// First, get paired printers
|
|
283
|
+
BluetoothConnection[] bluetoothPrinters = (new BluetoothPrintersConnections()).getList();
|
|
284
|
+
Set<String> pairedAddresses = new HashSet<>();
|
|
285
|
+
|
|
286
|
+
if (bluetoothPrinters != null && bluetoothPrinters.length > 0) {
|
|
269
287
|
for (BluetoothConnection printer : bluetoothPrinters) {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
288
|
+
try {
|
|
289
|
+
JSObject printerObj = new JSObject();
|
|
290
|
+
String name = printer.getDevice().getName();
|
|
291
|
+
String address = printer.getDevice().getAddress();
|
|
292
|
+
|
|
293
|
+
// Handle null/empty names
|
|
294
|
+
if (name == null || name.isEmpty()) {
|
|
295
|
+
name = "Unknown Device";
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
printerObj.put("name", name);
|
|
299
|
+
printerObj.put("address", address != null ? address : "");
|
|
300
|
+
printers.put(printerObj);
|
|
301
|
+
pairedAddresses.add(address);
|
|
302
|
+
|
|
303
|
+
Log.d(TAG, "Found paired Bluetooth printer: " + name + " (" + address + ")");
|
|
304
|
+
} catch (Exception e) {
|
|
305
|
+
Log.e(TAG, "Error processing printer: " + e.getMessage());
|
|
306
|
+
}
|
|
274
307
|
}
|
|
308
|
+
} else {
|
|
309
|
+
Log.d(TAG, "No paired Bluetooth printers found. Starting discovery...");
|
|
275
310
|
}
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
311
|
+
|
|
312
|
+
// Start Bluetooth discovery for unpaired devices
|
|
313
|
+
startBluetoothDiscovery(printers, pairedAddresses);
|
|
279
314
|
} catch (Exception e) {
|
|
315
|
+
Log.e(TAG, "Error listing Bluetooth printers: " + e.getMessage(), e);
|
|
280
316
|
call.reject("Error listing Bluetooth printers: " + e.getMessage());
|
|
281
317
|
}
|
|
282
318
|
}
|
|
283
319
|
);
|
|
284
320
|
}
|
|
321
|
+
|
|
322
|
+
private void startBluetoothDiscovery(JSArray printers, java.util.Set<String> pairedAddresses) {
|
|
323
|
+
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
|
|
324
|
+
|
|
325
|
+
if (bluetoothAdapter == null) {
|
|
326
|
+
Log.d(TAG, "Bluetooth not supported on this device");
|
|
327
|
+
JSObject ret = new JSObject();
|
|
328
|
+
ret.put("printers", printers);
|
|
329
|
+
if (discoveryCall != null) {
|
|
330
|
+
discoveryCall.resolve(ret);
|
|
331
|
+
discoveryCall = null;
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!bluetoothAdapter.isEnabled()) {
|
|
337
|
+
Log.d(TAG, "Bluetooth is not enabled");
|
|
338
|
+
JSObject ret = new JSObject();
|
|
339
|
+
ret.put("printers", printers);
|
|
340
|
+
if (discoveryCall != null) {
|
|
341
|
+
discoveryCall.resolve(ret);
|
|
342
|
+
discoveryCall = null;
|
|
343
|
+
}
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Register receiver for discovered devices
|
|
348
|
+
discoveryReceiver = new BroadcastReceiver() {
|
|
349
|
+
@Override
|
|
350
|
+
public void onReceive(Context context, Intent intent) {
|
|
351
|
+
String action = intent.getAction();
|
|
352
|
+
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
|
|
353
|
+
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
|
|
354
|
+
if (device != null && !pairedAddresses.contains(device.getAddress())) {
|
|
355
|
+
discoveredDevices.add(device);
|
|
356
|
+
String name = device.getName();
|
|
357
|
+
if (name == null || name.isEmpty()) {
|
|
358
|
+
name = "Unknown Device";
|
|
359
|
+
}
|
|
360
|
+
Log.d(TAG, "Discovered Bluetooth device: " + name + " (" + device.getAddress() + ")");
|
|
361
|
+
}
|
|
362
|
+
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
|
|
363
|
+
// Discovery finished, add discovered devices to results
|
|
364
|
+
JSArray finalPrinters = printers;
|
|
365
|
+
for (BluetoothDevice device : discoveredDevices) {
|
|
366
|
+
try {
|
|
367
|
+
JSObject printerObj = new JSObject();
|
|
368
|
+
String name = device.getName();
|
|
369
|
+
if (name == null || name.isEmpty()) {
|
|
370
|
+
name = "Unknown Device";
|
|
371
|
+
}
|
|
372
|
+
printerObj.put("name", name);
|
|
373
|
+
printerObj.put("address", device.getAddress());
|
|
374
|
+
finalPrinters.put(printerObj);
|
|
375
|
+
Log.d(TAG, "Added discovered device: " + name + " (" + device.getAddress() + ")");
|
|
376
|
+
} catch (Exception e) {
|
|
377
|
+
Log.e(TAG, "Error adding discovered device: " + e.getMessage());
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Unregister receiver
|
|
382
|
+
try {
|
|
383
|
+
getContext().unregisterReceiver(discoveryReceiver);
|
|
384
|
+
} catch (Exception e) {
|
|
385
|
+
Log.e(TAG, "Error unregistering receiver: " + e.getMessage());
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Return results
|
|
389
|
+
JSObject ret = new JSObject();
|
|
390
|
+
ret.put("printers", finalPrinters);
|
|
391
|
+
if (discoveryCall != null) {
|
|
392
|
+
discoveryCall.resolve(ret);
|
|
393
|
+
discoveryCall = null;
|
|
394
|
+
}
|
|
395
|
+
discoveryReceiver = null;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
// Register receiver
|
|
401
|
+
IntentFilter filter = new IntentFilter();
|
|
402
|
+
filter.addAction(BluetoothDevice.ACTION_FOUND);
|
|
403
|
+
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
|
|
404
|
+
getContext().registerReceiver(discoveryReceiver, filter);
|
|
405
|
+
|
|
406
|
+
// Start discovery
|
|
407
|
+
if (bluetoothAdapter.isDiscovering()) {
|
|
408
|
+
bluetoothAdapter.cancelDiscovery();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
Log.d(TAG, "Starting Bluetooth discovery...");
|
|
412
|
+
boolean started = bluetoothAdapter.startDiscovery();
|
|
413
|
+
|
|
414
|
+
if (!started) {
|
|
415
|
+
Log.d(TAG, "Failed to start Bluetooth discovery");
|
|
416
|
+
try {
|
|
417
|
+
getContext().unregisterReceiver(discoveryReceiver);
|
|
418
|
+
} catch (Exception e) {
|
|
419
|
+
Log.e(TAG, "Error unregistering receiver: " + e.getMessage());
|
|
420
|
+
}
|
|
421
|
+
JSObject ret = new JSObject();
|
|
422
|
+
ret.put("printers", printers);
|
|
423
|
+
if (discoveryCall != null) {
|
|
424
|
+
discoveryCall.resolve(ret);
|
|
425
|
+
discoveryCall = null;
|
|
426
|
+
}
|
|
427
|
+
discoveryReceiver = null;
|
|
428
|
+
} else {
|
|
429
|
+
// Stop discovery after 8 seconds
|
|
430
|
+
discoveryHandler.postDelayed(() -> {
|
|
431
|
+
if (bluetoothAdapter.isDiscovering()) {
|
|
432
|
+
bluetoothAdapter.cancelDiscovery();
|
|
433
|
+
Log.d(TAG, "Discovery timeout, stopping scan");
|
|
434
|
+
}
|
|
435
|
+
}, 8000);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
285
438
|
|
|
286
439
|
@PluginMethod
|
|
287
440
|
public void listUsbPrinters(PluginCall call) {
|
|
288
441
|
try {
|
|
289
442
|
UsbConnection[] usbPrinters = (new UsbPrintersConnections(getContext())).getList();
|
|
290
443
|
JSArray printers = new JSArray();
|
|
291
|
-
|
|
444
|
+
|
|
445
|
+
if (usbPrinters != null && usbPrinters.length > 0) {
|
|
292
446
|
for (UsbConnection printer : usbPrinters) {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
447
|
+
try {
|
|
448
|
+
JSObject printerObj = new JSObject();
|
|
449
|
+
String name = printer.getDevice().getDeviceName();
|
|
450
|
+
String serial = printer.getDevice().getSerialNumber();
|
|
451
|
+
|
|
452
|
+
// Handle null/empty names
|
|
453
|
+
if (name == null || name.isEmpty()) {
|
|
454
|
+
name = "USB Printer";
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
printerObj.put("name", name);
|
|
458
|
+
printerObj.put("address", serial != null ? serial : printer.getDevice().getDeviceId() + "");
|
|
459
|
+
printers.put(printerObj);
|
|
460
|
+
|
|
461
|
+
Log.d(TAG, "Found USB printer: " + name + " (ID: " + printer.getDevice().getDeviceId() + ")");
|
|
462
|
+
} catch (Exception e) {
|
|
463
|
+
Log.e(TAG, "Error processing USB printer: " + e.getMessage());
|
|
464
|
+
}
|
|
297
465
|
}
|
|
466
|
+
} else {
|
|
467
|
+
Log.d(TAG, "No USB printers found. Make sure printer is connected via USB.");
|
|
298
468
|
}
|
|
469
|
+
|
|
299
470
|
JSObject ret = new JSObject();
|
|
300
471
|
ret.put("printers", printers);
|
|
301
472
|
call.resolve(ret);
|
|
302
473
|
} catch (Exception e) {
|
|
474
|
+
Log.e(TAG, "Error listing USB printers: " + e.getMessage(), e);
|
|
303
475
|
call.reject("Error listing USB printers: " + e.getMessage());
|
|
304
476
|
}
|
|
305
477
|
}
|
|
@@ -283,22 +283,27 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
283
283
|
}
|
|
284
284
|
|
|
285
285
|
@objc func listBluetoothPrinters(_ call: CAPPluginCall) {
|
|
286
|
+
print("ThermalPrinter: listBluetoothPrinters called")
|
|
286
287
|
var printers: [[String: String]] = []
|
|
287
288
|
|
|
288
289
|
// First, get MFi/ExternalAccessory printers
|
|
289
290
|
let manager = EAAccessoryManager.shared()
|
|
290
291
|
let accessories = manager.connectedAccessories
|
|
291
292
|
|
|
293
|
+
print("ThermalPrinter: Found \(accessories.count) ExternalAccessory devices")
|
|
294
|
+
|
|
292
295
|
let mfiPrinters: [[String: String]] = accessories.compactMap { accessory in
|
|
293
296
|
guard accessory.protocolStrings.contains(where: { supportedPrinterProtocols.contains($0) }) else {
|
|
294
297
|
return nil
|
|
295
298
|
}
|
|
299
|
+
print("ThermalPrinter: Found MFi printer: \(accessory.name)")
|
|
296
300
|
return [
|
|
297
301
|
"name": accessory.name,
|
|
298
302
|
"address": accessory.serialNumber.isEmpty ? "\(accessory.connectionID)" : accessory.serialNumber,
|
|
299
303
|
]
|
|
300
304
|
}
|
|
301
305
|
printers.append(contentsOf: mfiPrinters)
|
|
306
|
+
print("ThermalPrinter: Found \(mfiPrinters.count) MFi printers")
|
|
302
307
|
|
|
303
308
|
// Then scan for BLE printers
|
|
304
309
|
discoveredPeripherals.removeAll()
|
|
@@ -306,30 +311,40 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
306
311
|
|
|
307
312
|
// Initialize central manager if needed
|
|
308
313
|
if centralManager == nil {
|
|
314
|
+
print("ThermalPrinter: Initializing CBCentralManager")
|
|
309
315
|
centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main)
|
|
310
316
|
}
|
|
311
317
|
|
|
312
318
|
guard let centralManager = centralManager else {
|
|
319
|
+
print("ThermalPrinter: Central manager is nil, returning \(printers.count) printers")
|
|
313
320
|
call.resolve(["printers": printers])
|
|
314
321
|
return
|
|
315
322
|
}
|
|
316
323
|
|
|
324
|
+
print("ThermalPrinter: Bluetooth state: \(centralManager.state.rawValue)")
|
|
325
|
+
|
|
317
326
|
// Check Bluetooth state
|
|
318
327
|
switch centralManager.state {
|
|
319
328
|
case .poweredOn:
|
|
320
329
|
// Start scanning immediately
|
|
330
|
+
print("ThermalPrinter: Bluetooth powered on, starting scan")
|
|
321
331
|
startBLEScan(for: call, existingPrinters: printers)
|
|
322
332
|
case .poweredOff:
|
|
333
|
+
print("ThermalPrinter: Bluetooth powered off, returning \(printers.count) printers")
|
|
323
334
|
call.resolve(["printers": printers])
|
|
324
335
|
case .unauthorized:
|
|
336
|
+
print("ThermalPrinter: Bluetooth unauthorized")
|
|
325
337
|
call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
|
|
326
338
|
case .unsupported:
|
|
339
|
+
print("ThermalPrinter: Bluetooth unsupported")
|
|
327
340
|
call.reject("Bluetooth is not supported on this device.")
|
|
328
341
|
case .resetting, .unknown:
|
|
342
|
+
print("ThermalPrinter: Bluetooth state unknown/resetting, waiting for update")
|
|
329
343
|
// Wait for state update
|
|
330
344
|
// The state will be updated via centralManagerDidUpdateState
|
|
331
345
|
break
|
|
332
346
|
@unknown default:
|
|
347
|
+
print("ThermalPrinter: Unknown Bluetooth state, returning \(printers.count) printers")
|
|
333
348
|
call.resolve(["printers": printers])
|
|
334
349
|
}
|
|
335
350
|
}
|
|
@@ -345,28 +360,32 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
345
360
|
return
|
|
346
361
|
}
|
|
347
362
|
|
|
363
|
+
// Clear previous discoveries
|
|
364
|
+
discoveredPeripherals.removeAll()
|
|
365
|
+
|
|
348
366
|
// Scan for all peripherals
|
|
367
|
+
print("ThermalPrinter: Starting BLE scan...")
|
|
349
368
|
centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: false])
|
|
350
369
|
|
|
351
|
-
// Stop scanning after 5
|
|
352
|
-
DispatchQueue.main.asyncAfter(deadline: .now() +
|
|
370
|
+
// Stop scanning after 8 seconds (increased from 5 to give more time)
|
|
371
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 8.0) {
|
|
353
372
|
centralManager.stopScan()
|
|
373
|
+
print("ThermalPrinter: Stopped BLE scan. Found \(self.discoveredPeripherals.count) peripherals")
|
|
354
374
|
|
|
355
375
|
var printers = existingPrinters
|
|
356
376
|
|
|
357
|
-
// Add discovered BLE printers - return all devices
|
|
377
|
+
// Add discovered BLE printers - return all devices, even without names
|
|
358
378
|
let blePrinters: [[String: String]] = self.discoveredPeripherals.compactMap { peripheral in
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
|
|
362
|
-
return nil
|
|
363
|
-
}
|
|
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"
|
|
364
382
|
return [
|
|
365
383
|
"name": name,
|
|
366
384
|
"address": peripheral.identifier.uuidString,
|
|
367
385
|
]
|
|
368
386
|
}
|
|
369
387
|
|
|
388
|
+
print("ThermalPrinter: Returning \(blePrinters.count) BLE printers")
|
|
370
389
|
printers.append(contentsOf: blePrinters)
|
|
371
390
|
call.resolve(["printers": printers])
|
|
372
391
|
self.listPrintersCall = nil
|
|
@@ -376,10 +395,13 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
376
395
|
// MARK: - CBCentralManagerDelegate
|
|
377
396
|
|
|
378
397
|
public func centralManagerDidUpdateState(_ central: CBCentralManager) {
|
|
398
|
+
print("ThermalPrinter: Central manager state updated: \(central.state.rawValue)")
|
|
399
|
+
|
|
379
400
|
// If we're waiting to list printers, start scanning now
|
|
380
401
|
if let call = listPrintersCall {
|
|
381
402
|
switch central.state {
|
|
382
403
|
case .poweredOn:
|
|
404
|
+
print("ThermalPrinter: Bluetooth now powered on, starting scan")
|
|
383
405
|
// Get existing MFi printers first
|
|
384
406
|
let manager = EAAccessoryManager.shared()
|
|
385
407
|
let accessories = manager.connectedAccessories
|
|
@@ -394,15 +416,19 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
394
416
|
}
|
|
395
417
|
startBLEScan(for: call, existingPrinters: mfiPrinters)
|
|
396
418
|
case .poweredOff:
|
|
419
|
+
print("ThermalPrinter: Bluetooth powered off")
|
|
397
420
|
call.resolve(["printers": []])
|
|
398
421
|
listPrintersCall = nil
|
|
399
422
|
case .unauthorized:
|
|
423
|
+
print("ThermalPrinter: Bluetooth unauthorized")
|
|
400
424
|
call.reject("Bluetooth access denied. Please enable Bluetooth permissions in Settings.")
|
|
401
425
|
listPrintersCall = nil
|
|
402
426
|
case .unsupported:
|
|
427
|
+
print("ThermalPrinter: Bluetooth unsupported")
|
|
403
428
|
call.reject("Bluetooth is not supported on this device.")
|
|
404
429
|
listPrintersCall = nil
|
|
405
430
|
default:
|
|
431
|
+
print("ThermalPrinter: Bluetooth state: \(central.state.rawValue)")
|
|
406
432
|
break
|
|
407
433
|
}
|
|
408
434
|
}
|
|
@@ -412,6 +438,8 @@ public class ThermalPrinterPlugin: CAPPlugin, CBCentralManagerDelegate, CBPeriph
|
|
|
412
438
|
// Avoid duplicates
|
|
413
439
|
if !discoveredPeripherals.contains(where: { $0.identifier == peripheral.identifier }) {
|
|
414
440
|
discoveredPeripherals.append(peripheral)
|
|
441
|
+
let name = peripheral.name ?? "Unknown"
|
|
442
|
+
print("ThermalPrinter: Discovered peripheral: \(name) (\(peripheral.identifier.uuidString))")
|
|
415
443
|
}
|
|
416
444
|
|
|
417
445
|
// If we're looking for a specific printer to print to
|