662 lines
21 KiB
Java
662 lines
21 KiB
Java
package com.pozo.printer;
|
|
|
|
import android.app.PendingIntent;
|
|
import android.content.BroadcastReceiver;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.IntentFilter;
|
|
import android.hardware.usb.*;
|
|
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;
|
|
import com.getcapacitor.JSObject;
|
|
import com.pozo.printer.helper.ReceiptBuilder;
|
|
import com.pozo.printer.model.TokenItem;
|
|
import com.pozo.printer.utils.EscPosUtils;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
|
|
public class UsbPrinter {
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
public interface PrintCallback {
|
|
void onSuccess();
|
|
|
|
void onError(String message);
|
|
}
|
|
|
|
// ─── 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;
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
} |