capacitor-pozo-printer/android/src/main/java/com/pozo/printer/UsbPrinter.java

662 lines
21 KiB
Java
Raw Normal View History

2026-03-18 09:43:44 +05:30
package com.pozo.printer;
2026-07-01 11:57:40 +05:30
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
2026-03-18 09:43:44 +05:30
import android.content.Context;
2026-07-01 11:57:40 +05:30
import android.content.Intent;
import android.content.IntentFilter;
2026-03-18 09:43:44 +05:30
import android.hardware.usb.*;
2026-07-01 11:57:40 +05:30
import android.os.Build;
import android.util.Log;
import androidx.core.content.ContextCompat;
import com.dantsu.escposprinter.EscPosPrinter;
import com.dantsu.escposprinter.connection.usb.UsbConnection;
2026-03-18 09:43:44 +05:30
import com.getcapacitor.JSObject;
2026-07-01 11:57:40 +05:30
import com.pozo.printer.helper.ReceiptBuilder;
import com.pozo.printer.model.TokenItem;
import com.pozo.printer.utils.EscPosUtils;
2026-03-18 09:43:44 +05:30
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public class UsbPrinter {
2026-07-01 11:57:40 +05:30
private static final String ACTION_USB_PERMISSION =
"com.pozo.printer.USB_PERMISSION";
public static UsbDevice connectedUsbDevice;
private static ConnectCallback pendingConnectCallback;
private static final BroadcastReceiver usbReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!ACTION_USB_PERMISSION.equals(intent.getAction())) {
return;
}
if (pendingConnectCallback == null) {
return;
}
boolean granted =
intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED,
false);
UsbDevice device;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
device = intent.getParcelableExtra(
UsbManager.EXTRA_DEVICE,
UsbDevice.class);
} else {
device = intent.getParcelableExtra(
UsbManager.EXTRA_DEVICE);
}
if (granted && device != null) {
connectedUsbDevice = device;
JSObject obj = new JSObject();
obj.put(
"name",
device.getProductName() != null
? device.getProductName()
: "USB Printer");
obj.put(
"address",
String.valueOf(device.getDeviceId()));
obj.put("connected", true);
pendingConnectCallback.onConnected(obj);
} else {
pendingConnectCallback.onError(
"USB Permission Denied");
}
pendingConnectCallback = null;
}
};
public static void initialize(Context context) {
IntentFilter filter =
new IntentFilter(ACTION_USB_PERMISSION);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(
usbReceiver,
filter,
Context.RECEIVER_NOT_EXPORTED);
} else {
context.registerReceiver(
usbReceiver,
filter);
}
2026-03-18 09:43:44 +05:30
}
2026-07-01 11:57:40 +05:30
public static UsbDevice findUsbPrinter(Context context) {
UsbManager manager =
(UsbManager) context.getSystemService(Context.USB_SERVICE);
if (manager == null)
return null;
for (UsbDevice device : manager.getDeviceList().values()) {
for (int i = 0; i < device.getInterfaceCount(); i++) {
if (device.getInterface(i).getInterfaceClass()
== UsbConstants.USB_CLASS_PRINTER) {
return device;
}
}
}
return null;
}
// ─── Callback interface ─────────────────────────────────────────
public interface ConnectCallback {
void onConnected(JSObject printer);
void onError(String message);
2026-03-18 09:43:44 +05:30
}
2026-07-01 11:57:40 +05:30
public interface PrintCallback {
void onSuccess();
void onError(String message);
2026-03-18 09:43:44 +05:30
}
2026-07-01 11:57:40 +05:30
// ─── Scan ───────────────────────────────────────────────────────
public static List<JSObject> scanDevices(Context context) {
List<JSObject> result = new ArrayList<>();
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
if (manager == null) return result;
for (UsbDevice device : manager.getDeviceList().values()) {
boolean isPrinter = false;
for (int i = 0; i < device.getInterfaceCount(); i++) {
UsbInterface usbInterface = device.getInterface(i);
if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER) {
isPrinter = true;
break;
}
}
if (isPrinter) {
JSObject obj = new JSObject();
obj.put("name", device.getProductName() != null ?
device.getProductName() : "USB Printer");
obj.put("address", String.valueOf(device.getDeviceId()));
result.add(obj);
}
}
return result;
2026-03-18 09:43:44 +05:30
}
2026-07-01 11:57:40 +05:30
private static final String TAG = "USB_CONNECT";
public static void connectUsb(
Context context,
ConnectCallback callback) throws Exception {
Log.d(TAG, "===== connectUsb() called =====");
UsbManager manager =
(UsbManager) context.getSystemService(Context.USB_SERVICE);
Log.d(TAG, "UsbManager: " + manager);
if (manager == null) {
Log.e(TAG, "UsbManager unavailable");
throw new Exception("UsbManager unavailable");
}
Log.d(TAG, "Searching for USB printer...");
UsbDevice printer = findUsbPrinter(context);
Log.d(TAG, "Printer found: " + printer);
if (printer == null) {
Log.e(TAG, "No USB printer found");
throw new Exception("No USB printer found");
}
Log.d(TAG, "Device Name: " + printer.getDeviceName());
Log.d(TAG, "Product Name: " + printer.getProductName());
Log.d(TAG, "Vendor ID: " + printer.getVendorId());
Log.d(TAG, "Product ID: " + printer.getProductId());
Log.d(TAG, "Device ID: " + printer.getDeviceId());
boolean hasPermission = manager.hasPermission(printer);
Log.d(TAG, "Has USB Permission: " + hasPermission);
// Already has permission
if (hasPermission) {
Log.d(TAG, "Permission already granted.");
connectedUsbDevice = printer;
JSObject obj = new JSObject();
obj.put(
"name",
printer.getProductName() != null
? printer.getProductName()
: "USB Printer");
obj.put(
"address",
String.valueOf(printer.getDeviceId()));
obj.put("connected", true);
Log.d(TAG, "Calling callback.onConnected()");
callback.onConnected(obj);
Log.d(TAG, "connectUsb() completed successfully.");
return;
}
Log.d(TAG, "Permission NOT granted. Requesting permission...");
// Save callback until permission result arrives
pendingConnectCallback = callback;
int flags;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
flags =
PendingIntent.FLAG_UPDATE_CURRENT
| PendingIntent.FLAG_MUTABLE;
Log.d(TAG, "Using FLAG_MUTABLE");
} else {
flags = PendingIntent.FLAG_UPDATE_CURRENT;
Log.d(TAG, "Using FLAG_UPDATE_CURRENT");
}
PendingIntent permissionIntent =
PendingIntent.getBroadcast(
context,
0,
new Intent(ACTION_USB_PERMISSION),
flags);
Log.d(TAG, "PendingIntent created.");
manager.requestPermission(
printer,
permissionIntent);
Log.d(TAG, "requestPermission() called. Waiting for BroadcastReceiver...");
}
// ─── Find device ────────────────────────────────────────────────
public static UsbDevice findDevice(UsbManager manager, String deviceIdStr) {
HashMap<String, UsbDevice> devices = manager.getDeviceList();
if (deviceIdStr != null) {
int id = Integer.parseInt(deviceIdStr);
for (UsbDevice d : devices.values()) {
if (d.getDeviceId() == id) return d;
}
return null;
}
return devices.isEmpty() ? null : devices.values().iterator().next();
}
// ─── Print with auto permission ─────────────────────────────────
public static void printWithPermission(
Context context,
byte[] data,
String deviceIdStr,
PrintCallback callback
) {
UsbManager manager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
if (manager == null) {
callback.onError("USB service not available");
return;
}
UsbDevice target = findDevice(manager, deviceIdStr);
if (target == null) {
callback.onError("USB printer not found");
return;
}
if (manager.hasPermission(target)) {
// ── Already permitted → print immediately ──
new Thread(() -> {
try {
sendData(manager, target, data);
callback.onSuccess();
} catch (Exception e) {
callback.onError(e.getMessage());
}
}).start();
} else {
// ── No permission → show dialog, then print on grant ──
PendingIntent pi = PendingIntent.getBroadcast(
context, 0,
new Intent(ACTION_USB_PERMISSION),
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
if (!ACTION_USB_PERMISSION.equals(intent.getAction())) return;
context.unregisterReceiver(this);
boolean granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
if (granted) {
new Thread(() -> {
try {
sendData(manager, target, data);
callback.onSuccess();
} catch (Exception e) {
callback.onError(e.getMessage());
}
}).start();
} else {
callback.onError("USB permission denied by user");
}
}
};
context.registerReceiver(receiver, new IntentFilter(ACTION_USB_PERMISSION));
manager.requestPermission(target, pi);
}
}
// ─── Raw data transfer ──────────────────────────────────────────
private static void sendData(UsbManager manager, UsbDevice device, byte[] data) throws Exception {
Log.d("Gokullog", "sendData: " + device.getProductName());
UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = null;
for (int i = 0; i < intf.getEndpointCount(); i++) {
UsbEndpoint ep = intf.getEndpoint(i);
if (ep.getDirection() == UsbConstants.USB_DIR_OUT) {
endpoint = ep;
break;
}
}
if (endpoint == null) throw new Exception("No OUT endpoint on USB device");
UsbDeviceConnection connection = manager.openDevice(device);
if (connection == null) throw new Exception("Cannot open USB device");
connection.claimInterface(intf, true);
try {
int chunkSize = endpoint.getMaxPacketSize();
int offset = 0;
while (offset < data.length) {
int size = Math.min(chunkSize, data.length - offset);
byte[] chunk = new byte[size];
System.arraycopy(data, offset, chunk, 0, size);
int transferred = connection.bulkTransfer(endpoint, chunk, size, 5000);
if (transferred < 0) throw new Exception("Transfer failed at offset " + offset);
offset += size;
}
} finally {
connection.releaseInterface(intf);
connection.close();
}
}
public static void printUsb(
Context context,
String usbDeviceId,
String receiptText,
String estimateText,
String logoImage,
String totalAmount,
String estimateAmount,
String footerPrint,
String upiId,
List<List<TokenItem>> salesTokens,
List<List<TokenItem>> estimateTokens,
String orderId,
String estimateOrderId,
String salesId,
String salesIdEst) throws Exception {
UsbManager usbManager =
(UsbManager) context.getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList =
usbManager.getDeviceList();
UsbDevice selectedDevice = null;
for (UsbDevice device : deviceList.values()) {
if (String.valueOf(device.getDeviceId()).equals(usbDeviceId)) {
selectedDevice = device;
break;
}
}
if (selectedDevice == null) {
throw new Exception("USB printer not found");
}
if (!usbManager.hasPermission(selectedDevice)) {
PendingIntent permissionIntent =
PendingIntent.getBroadcast(
context,
0,
new Intent(ACTION_USB_PERMISSION),
PendingIntent.FLAG_IMMUTABLE);
BroadcastReceiver usbReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(
Context ctx,
Intent intent) {
if (!ACTION_USB_PERMISSION.equals(
intent.getAction())) {
return;
}
try {
ctx.unregisterReceiver(this);
} catch (Exception ignored) {
}
UsbDevice device;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
device = intent.getParcelableExtra(
UsbManager.EXTRA_DEVICE,
UsbDevice.class);
} else {
device = intent.getParcelableExtra(
UsbManager.EXTRA_DEVICE);
}
boolean granted =
intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED,
false);
if (!granted || device == null) {
Log.e("USB", "Permission denied");
return;
}
try {
printUsbInternal(
usbManager,
device,
receiptText,
estimateText,
logoImage,
totalAmount,
estimateAmount,
footerPrint,
upiId,
salesTokens,
estimateTokens,
orderId,
estimateOrderId,
salesId,
salesIdEst);
} catch (Exception e) {
Log.e("USB", "Print failed", e);
}
}
};
ContextCompat.registerReceiver(
context,
usbReceiver,
new IntentFilter(ACTION_USB_PERMISSION),
ContextCompat.RECEIVER_NOT_EXPORTED);
usbManager.requestPermission(
selectedDevice,
permissionIntent);
return;
}
printUsbInternal(
usbManager,
selectedDevice,
receiptText,
estimateText,
logoImage,
totalAmount,
estimateAmount,
footerPrint,
upiId,
salesTokens,
estimateTokens,
orderId,
estimateOrderId,
salesId,
salesIdEst);
}
private static void printUsbInternal(
UsbManager usbManager,
UsbDevice selectedDevice,
String receiptText,
String estimateText,
String logoImage,
String totalAmount,
String estimateAmount,
String footerPrint,
String upiId,
List<List<TokenItem>> salesTokens,
List<List<TokenItem>> estimateTokens,
String orderId,
String estimateOrderId,
String salesId,
String salesIdEst) throws Exception {
UsbConnection connection =
new UsbConnection(
usbManager,
selectedDevice);
long t = System.currentTimeMillis();
EscPosPrinter printer =
new EscPosPrinter(
connection,
203,
80f,
45);
Log.d("USB_TIME",
"Printer = "
+ (System.currentTimeMillis() - t));
if (receiptText != null &&
!receiptText.isEmpty()) {
t = System.currentTimeMillis();
String receipt =
ReceiptBuilder.buildReceipt(
printer,
receiptText,
logoImage,
upiId,
totalAmount,
footerPrint);
Log.d("USB_TIME",
"Receipt = "
+ (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
printer.printFormattedTextAndCut(receipt);
Log.d("USB_TIME",
"Print = "
+ (System.currentTimeMillis() - t));
}
if (estimateText != null &&
!estimateText.isEmpty()) {
String estimate =
ReceiptBuilder.buildReceipt(
printer,
estimateText,
logoImage,
upiId,
estimateAmount,
footerPrint);
printer.printFormattedTextAndCut(estimate);
}
if (salesTokens != null &&
!salesTokens.isEmpty()) {
EscPosUtils.printUsbOrNetworkTokens(
printer,
salesTokens,
orderId,
salesId,
"Sales");
}
if (estimateTokens != null &&
!estimateTokens.isEmpty()) {
EscPosUtils.printUsbOrNetworkTokens(
printer,
estimateTokens,
estimateOrderId,
salesIdEst,
"Estimate");
}
printer.disconnectPrinter();
Log.d("USB", "Print Success");
}
2026-03-18 09:43:44 +05:30
}