From 278d97d02ec84e1e047a72959d0367d80f7ff8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 14:31:56 +0300 Subject: [PATCH 1/9] Improve USB permission handling and error logging in USBPrinterAdapter --- .../adapter/USBPrinterAdapter.kt | 31 ++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/adapter/USBPrinterAdapter.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/adapter/USBPrinterAdapter.kt index e96576d9..01e8f0d2 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/adapter/USBPrinterAdapter.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/adapter/USBPrinterAdapter.kt @@ -40,27 +40,42 @@ class USBPrinterAdapter private constructor() { private val mUsbDeviceReceiver: BroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if ((ACTION_USB_PERMISSION == action)) { + override fun onReceive(context: Context, intent: Intent?) { + if (intent == null) return + + val action = intent.action ?: return + + if (ACTION_USB_PERMISSION == action) { synchronized(this) { val usbDevice: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) - if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + if (usbDevice == null) { + Log.e(LOG_TAG, "USB_PERMISSION received but EXTRA_DEVICE was null") + return + } + + val granted = intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false) + if (granted) { Log.i( LOG_TAG, - "Success get permission for device " + usbDevice!!.deviceId + ", vendor_id: " + usbDevice.vendorId + " product_id: " + usbDevice.productId + "Success get permission for device ${usbDevice.deviceId}, " + + "vendor_id: ${usbDevice.vendorId}, product_id: ${usbDevice.productId}" ) mUsbDevice = usbDevice } else { Toast.makeText( - context, mContext?.getString(R.string.user_refuse_perm) + ": ${usbDevice!!.deviceName}", + context, + "${mContext?.getString(R.string.user_refuse_perm)}: ${usbDevice.deviceName ?: "Unknown"}", Toast.LENGTH_LONG ).show() } } - } else if ((UsbManager.ACTION_USB_DEVICE_DETACHED == action)) { + } else if (UsbManager.ACTION_USB_DEVICE_DETACHED == action) { if (mUsbDevice != null) { - Toast.makeText(context, mContext?.getString(R.string.device_off), Toast.LENGTH_LONG).show() + Toast.makeText( + context, + mContext?.getString(R.string.device_off) ?: "Device disconnected", + Toast.LENGTH_LONG + ).show() closeConnectionIfExists() } } From 22b8533bc75d9abdf5902bf9d72d700636076779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 15:03:44 +0300 Subject: [PATCH 2/9] Refactor USB permission handling and improve error logging in USBPrinterService --- .../usb/USBPrinterService.kt | 274 +++++++++--------- 1 file changed, 133 insertions(+), 141 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt index 3c2fa21a..871f5782 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt @@ -31,87 +31,123 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { private val mUsbDeviceReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - val action = intent.action - if ((ACTION_USB_PERMISSION == action)) { - synchronized(this) { - val usbDevice: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) - if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { + val action = intent.action ?: return + + when (action) { + ACTION_USB_PERMISSION -> synchronized(this) { + // Permission sonucu + val usbDevice: UsbDevice? = + intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) + if (usbDevice == null) { + Log.e(LOG_TAG, "USB_PERMISSION received but no EXTRA_DEVICE") + state = STATE_USB_NONE + mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() + return + } + val granted = intent.getBooleanExtra( + UsbManager.EXTRA_PERMISSION_GRANTED, false + ) + if (granted) { Log.i( LOG_TAG, - "Success get permission for device ${usbDevice?.deviceId}, vendor_id: ${usbDevice?.vendorId} product_id: ${usbDevice?.productId}" + "Permission granted for device ${usbDevice.deviceId}, " + + "vendor=${usbDevice.vendorId}, product=${usbDevice.productId}" ) mUsbDevice = usbDevice state = STATE_USB_CONNECTED mHandler?.obtainMessage(STATE_USB_CONNECTED)?.sendToTarget() } else { - Toast.makeText(context, mContext?.getString(R.string.user_refuse_perm) + ": ${usbDevice!!.deviceName}", Toast.LENGTH_LONG).show() + // Güvenli isim kullanımı + val name = usbDevice.deviceName ?: "Unknown USB Device" + val msgTitle = mContext?.getString(R.string.user_refuse_perm) + ?: "Permission denied" + Toast.makeText(context, "$msgTitle: $name", Toast.LENGTH_LONG).show() state = STATE_USB_NONE mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() } } - } else if ((UsbManager.ACTION_USB_DEVICE_DETACHED == action)) { - if (mUsbDevice != null) { - Toast.makeText(context, mContext?.getString(R.string.device_off), Toast.LENGTH_LONG).show() - closeConnectionIfExists() - state = STATE_USB_NONE - mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() + UsbManager.ACTION_USB_DEVICE_DETACHED -> { + // Cihaz ayrıldığında bağlantıyı kapat + if (mUsbDevice != null) { + val msg = mContext?.getString(R.string.device_off) + ?: "Device disconnected" + Toast.makeText(context, msg, Toast.LENGTH_LONG).show() + closeConnectionIfExists() + mUsbDevice = null + state = STATE_USB_NONE + mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() + } } - } else if ((UsbManager.ACTION_USB_DEVICE_ATTACHED == action)) { -// if (mUsbDevice != null) { -// Toast.makeText(context, "USB device has been turned off", Toast.LENGTH_LONG).show() -// closeConnectionIfExists() -// } + // usb attach dalı boş bırakıldı isteğe bağlı işlenebilir } } } fun init(reactContext: Context?) { mContext = reactContext - mUSBManager = mContext!!.getSystemService(Context.USB_SERVICE) as UsbManager - mPermissionIndent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { - PendingIntent.getBroadcast(mContext, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE) + mUSBManager = mContext?.getSystemService(Context.USB_SERVICE) as? UsbManager + mPermissionIndent = if (android.os.Build.VERSION.SDK_INT >= + android.os.Build.VERSION_CODES.S) { + PendingIntent.getBroadcast( + mContext, 0, + Intent(ACTION_USB_PERMISSION), + PendingIntent.FLAG_IMMUTABLE + ) } else { PendingIntent.getBroadcast(mContext, 0, Intent(ACTION_USB_PERMISSION), 0) } - val filter = IntentFilter(ACTION_USB_PERMISSION) - filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) - mContext!!.registerReceiver(mUsbDeviceReceiver, filter) + + // Receiver kaydı + mContext?.registerReceiver( + mUsbDeviceReceiver, + IntentFilter(ACTION_USB_PERMISSION).apply { + addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) + } + ) + Log.v(LOG_TAG, "ESC/POS Printer initialized") } fun closeConnectionIfExists() { - if (mUsbDeviceConnection != null) { - mUsbDeviceConnection!!.releaseInterface(mUsbInterface) - mUsbDeviceConnection!!.close() - mUsbInterface = null - mEndPoint = null - mUsbDevice = null - mUsbDeviceConnection = null + mUsbDeviceConnection?.let { conn -> + mUsbInterface?.let { iface -> conn.releaseInterface(iface) } + conn.close() } + mUsbInterface = null + mEndPoint = null + mUsbDevice = null + mUsbDeviceConnection = null } val deviceList: List get() { if (mUSBManager == null) { - Toast.makeText(mContext, mContext?.getString(R.string.not_usb_manager), Toast.LENGTH_LONG).show() + Toast.makeText( + mContext, + mContext?.getString(R.string.not_usb_manager) ?: "USB Manager not available", + Toast.LENGTH_LONG + ).show() return emptyList() } return ArrayList(mUSBManager!!.deviceList.values) } fun selectDevice(vendorId: Int, productId: Int): Boolean { -// Log.v(LOG_TAG, " status usb ______ $state") - if ((mUsbDevice == null) || (mUsbDevice!!.vendorId != vendorId) || (mUsbDevice!!.productId != productId)) { + if (mUsbDevice == null || + mUsbDevice!!.vendorId != vendorId || + mUsbDevice!!.productId != productId + ) { synchronized(printLock) { closeConnectionIfExists() - val usbDevices: List = deviceList - for (usbDevice: UsbDevice in usbDevices) { - if ((usbDevice.vendorId == vendorId) && (usbDevice.productId == productId)) { - Log.v(LOG_TAG, "Request for device: vendor_id: " + usbDevice.vendorId + ", product_id: " + usbDevice.productId) - closeConnectionIfExists() - mUSBManager!!.requestPermission(usbDevice, mPermissionIndent) + for (usbDevice in deviceList) { + if (usbDevice.vendorId == vendorId && usbDevice.productId == productId) { + Log.v( + LOG_TAG, + "Request for device: vendor_id: $vendorId, product_id: $productId" + ) + mUSBManager?.requestPermission(usbDevice, mPermissionIndent) state = STATE_USB_CONNECTING mHandler?.obtainMessage(STATE_USB_CONNECTING)?.sendToTarget() return true @@ -121,9 +157,8 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } } else { mHandler?.obtainMessage(state)?.sendToTarget() + return true } - - return true } private fun openConnection(): Boolean { @@ -135,31 +170,33 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { Log.e(LOG_TAG, "USB Manager is not initialized") return false } - if (mUsbDeviceConnection != null) { - Log.i(LOG_TAG, "USB Connection already connected") - return true - } + mUsbDeviceConnection?.let { return true } + val usbInterface = mUsbDevice!!.getInterface(0) for (i in 0 until usbInterface.endpointCount) { val ep = usbInterface.getEndpoint(i) - if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) { - if (ep.direction == UsbConstants.USB_DIR_OUT) { - val usbDeviceConnection = mUSBManager!!.openDevice(mUsbDevice) - if (usbDeviceConnection == null) { - Log.e(LOG_TAG, "Failed to open USB Connection") - return false - } - Toast.makeText(mContext, mContext?.getString(R.string.connected_device), Toast.LENGTH_SHORT).show() - return if (usbDeviceConnection.claimInterface(usbInterface, true)) { - mEndPoint = ep - mUsbInterface = usbInterface - mUsbDeviceConnection = usbDeviceConnection - true - } else { - usbDeviceConnection.close() - Log.e(LOG_TAG, "Failed to retrieve usb connection") - false - } + if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK && + ep.direction == UsbConstants.USB_DIR_OUT + ) { + val conn = mUSBManager!!.openDevice(mUsbDevice) + if (conn == null) { + Log.e(LOG_TAG, "Failed to open USB Connection") + return false + } + Toast.makeText( + mContext, + mContext?.getString(R.string.connected_device) ?: "Device connected", + Toast.LENGTH_SHORT + ).show() + return if (conn.claimInterface(usbInterface, true)) { + mUsbInterface = usbInterface + mEndPoint = ep + mUsbDeviceConnection = conn + true + } else { + conn.close() + Log.e(LOG_TAG, "Failed to claim interface") + false } } } @@ -167,86 +204,42 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } fun printText(text: String): Boolean { - Log.v(LOG_TAG, "Printing text") - val isConnected = openConnection() - return if (isConnected) { - Log.v(LOG_TAG, "Connected to device") - Thread { - synchronized(printLock) { - val bytes: ByteArray = text.toByteArray(Charset.forName("UTF-8")) - val b: Int = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) - Log.i(LOG_TAG, "Return code: $b") - } - }.start() - true - } else { - Log.v(LOG_TAG, "Failed to connect to device") - false - } + if (!openConnection()) return false + Thread { + synchronized(printLock) { + val bytes = text.toByteArray(Charset.forName("UTF-8")) + mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + } + }.start() + return true } fun printRawData(data: String): Boolean { - Log.v(LOG_TAG, "Printing raw data: $data") - val isConnected = openConnection() - return if (isConnected) { - Log.v(LOG_TAG, "Connected to device") - Thread { - synchronized(printLock) { - val bytes: ByteArray = Base64.decode(data, Base64.DEFAULT) - val b: Int = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) - Log.i(LOG_TAG, "Return code: $b") - } - }.start() - true - } else { - Log.v(LOG_TAG, "Failed to connected to device") - false - } + if (!openConnection()) return false + Thread { + synchronized(printLock) { + val bytes = Base64.decode(data, Base64.DEFAULT) + mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + } + }.start() + return true } fun printBytes(bytes: ArrayList): Boolean { - Log.v(LOG_TAG, "Printing bytes") - val isConnected = openConnection() - if (isConnected) { - val chunkSize = mEndPoint!!.maxPacketSize - Log.v(LOG_TAG, "Max Packet Size: $chunkSize") - Log.v(LOG_TAG, "Connected to device") - Thread { - synchronized(printLock) { - val vectorData: Vector = Vector() - for (i in bytes.indices) { - val `val`: Int = bytes[i] - vectorData.add(`val`.toByte()) - } - val temp: Array = vectorData.toTypedArray() - val byteData = ByteArray(temp.size) - for (i in temp.indices) { - byteData[i] = temp[i] as Byte - } - var b = 0 - if (mUsbDeviceConnection != null) { - if (byteData.size > chunkSize) { - var chunks: Int = byteData.size / chunkSize - if (byteData.size % chunkSize > 0) { - ++chunks - } - for (i in 0 until chunks) { -// val buffer: ByteArray = byteData.copyOfRange(i * chunkSize, chunkSize + i * chunkSize) - val buffer: ByteArray = Arrays.copyOfRange(byteData, i * chunkSize, chunkSize + i * chunkSize) - b = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, buffer, chunkSize, 100000) - } - } else { - b = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, byteData, byteData.size, 100000) - } - Log.i(LOG_TAG, "Return code: $b") - } + if (!openConnection()) return false + Thread { + synchronized(printLock) { + val chunkSize = mEndPoint?.maxPacketSize ?: return@run + val byteData = ByteArray(bytes.size) { i -> bytes[i].toByte() } + var offset = 0 + while (offset < byteData.size) { + val len = minOf(chunkSize, byteData.size - offset) + mUsbDeviceConnection?.bulkTransfer(mEndPoint, byteData, offset, len, 100000) + offset += len } - }.start() - return true - } else { - Log.v(LOG_TAG, "Failed to connected to device") - return false - } + } + }.start() + return true } companion object { @@ -255,10 +248,9 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { private const val LOG_TAG = "ESC POS Printer" private const val ACTION_USB_PERMISSION = "com.flutter_pos_printer.USB_PERMISSION" - // Constants that indicate the current connection state - const val STATE_USB_NONE = 0 // we're doing nothing - const val STATE_USB_CONNECTING = 2 // now initiating an outgoing connection - const val STATE_USB_CONNECTED = 3 // now connected to a remote device + const val STATE_USB_NONE = 0 + const val STATE_USB_CONNECTING = 2 + const val STATE_USB_CONNECTED = 3 private val printLock = Any() From 0235c409448a54d153eee878cc6bd95d43a3e649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 17:12:09 +0300 Subject: [PATCH 3/9] Fix variable naming inconsistencies and improve code readability in USBPrinterService --- .../usb/USBPrinterService.kt | 84 ++++++++++--------- 1 file changed, 46 insertions(+), 38 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt index 871f5782..2a43e2f5 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt @@ -7,18 +7,19 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.hardware.usb.* +import android.os.Build import android.os.Handler import android.util.Base64 import android.util.Log import android.widget.Toast import com.sersoluciones.flutter_pos_printer_platform.R +import java.io.FileInputStream import java.nio.charset.Charset -import java.util.* class USBPrinterService private constructor(private var mHandler: Handler?) { private var mContext: Context? = null private var mUSBManager: UsbManager? = null - private var mPermissionIndent: PendingIntent? = null + private var mPermissionIntent: PendingIntent? = null private var mUsbDevice: UsbDevice? = null private var mUsbDeviceConnection: UsbDeviceConnection? = null private var mUsbInterface: UsbInterface? = null @@ -29,13 +30,12 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { mHandler = handler } - private val mUsbDeviceReceiver: BroadcastReceiver = object : BroadcastReceiver() { + private val mUsbDeviceReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return when (action) { ACTION_USB_PERMISSION -> synchronized(this) { - // Permission sonucu val usbDevice: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) if (usbDevice == null) { @@ -57,9 +57,9 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { state = STATE_USB_CONNECTED mHandler?.obtainMessage(STATE_USB_CONNECTED)?.sendToTarget() } else { - // Güvenli isim kullanımı val name = usbDevice.deviceName ?: "Unknown USB Device" - val msgTitle = mContext?.getString(R.string.user_refuse_perm) + val msgTitle = mContext + ?.getString(R.string.user_refuse_perm) ?: "Permission denied" Toast.makeText(context, "$msgTitle: $name", Toast.LENGTH_LONG).show() state = STATE_USB_NONE @@ -68,9 +68,9 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } UsbManager.ACTION_USB_DEVICE_DETACHED -> { - // Cihaz ayrıldığında bağlantıyı kapat if (mUsbDevice != null) { - val msg = mContext?.getString(R.string.device_off) + val msg = mContext + ?.getString(R.string.device_off) ?: "Device disconnected" Toast.makeText(context, msg, Toast.LENGTH_LONG).show() closeConnectionIfExists() @@ -79,37 +79,43 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() } } - - // usb attach dalı boş bırakıldı isteğe bağlı işlenebilir } } } - fun init(reactContext: Context?) { - mContext = reactContext - mUSBManager = mContext?.getSystemService(Context.USB_SERVICE) as? UsbManager - mPermissionIndent = if (android.os.Build.VERSION.SDK_INT >= - android.os.Build.VERSION_CODES.S) { + /** + * Initialize service: register receiver and prepare UsbManager/Permission intent. + */ + fun init(context: Context) { + mContext = context + mUSBManager = context.getSystemService(Context.USB_SERVICE) as? UsbManager + mPermissionIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { PendingIntent.getBroadcast( - mContext, 0, + context, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE ) } else { - PendingIntent.getBroadcast(mContext, 0, Intent(ACTION_USB_PERMISSION), 0) + PendingIntent.getBroadcast(context, 0, Intent(ACTION_USB_PERMISSION), 0) } - // Receiver kaydı - mContext?.registerReceiver( - mUsbDeviceReceiver, - IntentFilter(ACTION_USB_PERMISSION).apply { - addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) - } - ) - + // register receiver + val filter = IntentFilter(ACTION_USB_PERMISSION).apply { + addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) + } + context.registerReceiver(mUsbDeviceReceiver, filter) Log.v(LOG_TAG, "ESC/POS Printer initialized") } + /** + * Clean up: unregister the receiver. + */ + fun dispose() { + mContext?.unregisterReceiver(mUsbDeviceReceiver) + mContext = null + mUSBManager = null + } + fun closeConnectionIfExists() { mUsbDeviceConnection?.let { conn -> mUsbInterface?.let { iface -> conn.releaseInterface(iface) } @@ -126,7 +132,8 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { if (mUSBManager == null) { Toast.makeText( mContext, - mContext?.getString(R.string.not_usb_manager) ?: "USB Manager not available", + mContext?.getString(R.string.not_usb_manager) + ?: "USB Manager not available", Toast.LENGTH_LONG ).show() return emptyList() @@ -141,13 +148,13 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { ) { synchronized(printLock) { closeConnectionIfExists() - for (usbDevice in deviceList) { - if (usbDevice.vendorId == vendorId && usbDevice.productId == productId) { + for (device in deviceList) { + if (device.vendorId == vendorId && device.productId == productId) { Log.v( LOG_TAG, "Request for device: vendor_id: $vendorId, product_id: $productId" ) - mUSBManager?.requestPermission(usbDevice, mPermissionIndent) + mUSBManager?.requestPermission(device, mPermissionIntent) state = STATE_USB_CONNECTING mHandler?.obtainMessage(STATE_USB_CONNECTING)?.sendToTarget() return true @@ -162,23 +169,23 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } private fun openConnection(): Boolean { - if (mUsbDevice == null) { + val device = mUsbDevice ?: run { Log.e(LOG_TAG, "USB Device is not initialized") return false } - if (mUSBManager == null) { + val manager = mUSBManager ?: run { Log.e(LOG_TAG, "USB Manager is not initialized") return false } mUsbDeviceConnection?.let { return true } - val usbInterface = mUsbDevice!!.getInterface(0) + val usbInterface = device.getInterface(0) for (i in 0 until usbInterface.endpointCount) { val ep = usbInterface.getEndpoint(i) if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK && ep.direction == UsbConstants.USB_DIR_OUT ) { - val conn = mUSBManager!!.openDevice(mUsbDevice) + val conn = manager.openDevice(device) if (conn == null) { Log.e(LOG_TAG, "Failed to open USB Connection") return false @@ -208,7 +215,7 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { Thread { synchronized(printLock) { val bytes = text.toByteArray(Charset.forName("UTF-8")) - mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100_000) } }.start() return true @@ -219,22 +226,23 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { Thread { synchronized(printLock) { val bytes = Base64.decode(data, Base64.DEFAULT) - mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100_000) } }.start() return true } - fun printBytes(bytes: ArrayList): Boolean { + fun printBytes(bytes: List): Boolean { if (!openConnection()) return false Thread { synchronized(printLock) { - val chunkSize = mEndPoint?.maxPacketSize ?: return@run + val endpoint = mEndPoint ?: return@Thread + val chunkSize = endpoint.maxPacketSize val byteData = ByteArray(bytes.size) { i -> bytes[i].toByte() } var offset = 0 while (offset < byteData.size) { val len = minOf(chunkSize, byteData.size - offset) - mUsbDeviceConnection?.bulkTransfer(mEndPoint, byteData, offset, len, 100000) + mUsbDeviceConnection?.bulkTransfer(endpoint, byteData, offset, len, 100_000) offset += len } } From fb51717622f0b4c9204e39a43486321df0c6d3ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 17:17:24 +0300 Subject: [PATCH 4/9] Refactor FlutterPosPrinterPlatformPlugin for improved readability and maintainability --- .../FlutterPosPrinterPlatformPlugin.kt | 484 ++++-------------- 1 file changed, 101 insertions(+), 383 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt index 53340209..bf3d12e3 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt @@ -13,478 +13,197 @@ import android.os.Looper import android.os.Message import android.util.Log import android.widget.Toast -import androidx.annotation.NonNull import androidx.core.app.ActivityCompat -import androidx.core.app.ActivityCompat.startActivityForResult -import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothConnection -import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothConstants -import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothService -import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothService.Companion.TAG +import com.sersoluciones.flutter_pos_printer_platform.bluetooth.* import com.sersoluciones.flutter_pos_printer_platform.usb.USBPrinterService import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding -import io.flutter.plugin.common.EventChannel -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.MethodChannel.Result -import io.flutter.plugin.common.PluginRegistry -import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.* -/** FlutterPosPrinterPlatformPlugin */ -class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodCallHandler, PluginRegistry.RequestPermissionsResultListener, - PluginRegistry.ActivityResultListener, +class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, + PluginRegistry.RequestPermissionsResultListener, PluginRegistry.ActivityResultListener, ActivityAware { - /// The MethodChannel that will the communication between Flutter and native Android - /// - /// This local reference serves to register the plugin with the Flutter Engine and unregister it - /// when the Flutter Engine is detached from the Activity - private final var TAG = "FlutterPosPrinterPlatformPlugin" + private val TAG = "FlutterPosPrinterPlatformPlugin" private var binaryMessenger: BinaryMessenger? = null - private var channel: MethodChannel? = null - private var messageChannel: EventChannel? = null - private var messageUSBChannel: EventChannel? = null private var eventSink: EventChannel.EventSink? = null - - // Declare our eventSink later it will be initialized private var eventUSBSink: EventChannel.EventSink? = null + private var context: Context? = null private var currentActivity: Activity? = null - private var requestPermissionBT: Boolean = false - private var isBle: Boolean = false - private var isScan: Boolean = false - lateinit var adapter: USBPrinterService - private lateinit var bluetoothService: BluetoothService + private var adapter: USBPrinterService? = null + private var bluetoothService: BluetoothService? = null + private var isScan = false + private var isBle = false + private var requestPermissionBT = false private val usbHandler = object : Handler(Looper.getMainLooper()) { - override fun handleMessage(msg: Message) { - super.handleMessage(msg) + val sink = eventUSBSink ?: return when (msg.what) { - - USBPrinterService.STATE_USB_CONNECTED -> { - eventUSBSink?.success(2) - } - USBPrinterService.STATE_USB_CONNECTING -> { - eventUSBSink?.success(1) - } - USBPrinterService.STATE_USB_NONE -> { - eventUSBSink?.success(0) - } + USBPrinterService.STATE_USB_CONNECTED -> sink.success(2) + USBPrinterService.STATE_USB_CONNECTING -> sink.success(1) + USBPrinterService.STATE_USB_NONE -> sink.success(0) } } } private val bluetoothHandler = object : Handler(Looper.getMainLooper()) { - - private val bluetoothStatus: Int - get() = BluetoothService.bluetoothConnection?.state ?: 99 - override fun handleMessage(msg: Message) { - super.handleMessage(msg) + val sink = eventSink ?: return + val status = BluetoothService.bluetoothConnection?.state ?: 99 when (msg.what) { - BluetoothConstants.MESSAGE_STATE_CHANGE -> { - when (bluetoothStatus) { - BluetoothConstants.STATE_CONNECTED -> { - Log.w(TAG, " -------------------------- connection BT STATE_CONNECTED ") - if (msg.obj != null) - try { - val result = msg.obj as Result? - result?.success(true) - } catch (e: Exception) { - } - eventSink?.success(2) - bluetoothService.removeReconnectHandlers() - } - BluetoothConstants.STATE_CONNECTING -> { - Log.w(TAG, " -------------------------- connection BT STATE_CONNECTING ") - eventSink?.success(1) - } - BluetoothConstants.STATE_NONE -> { - Log.w(TAG, " -------------------------- connection BT STATE_NONE ") - eventSink?.success(0) - bluetoothService.autoConnectBt() - - } - BluetoothConstants.STATE_FAILED -> { - Log.w(TAG, " -------------------------- connection BT STATE_FAILED ") - if (msg.obj != null) - try { - val result = msg.obj as Result? - result?.success(false) - } catch (e: Exception) { - } - eventSink?.success(0) - } + BluetoothConstants.MESSAGE_STATE_CHANGE -> when (status) { + BluetoothConstants.STATE_CONNECTED -> { + Log.w(TAG, "BT CONNECTED") + (msg.obj as? MethodChannel.Result)?.success(true) + sink.success(2) + bluetoothService?.removeReconnectHandlers() } - } - BluetoothConstants.MESSAGE_WRITE -> { -// val readBuf = msg.obj as ByteArray -// Log.d("bluetooth", "envia bt: ${String(readBuf)}") - } - BluetoothConstants.MESSAGE_READ -> { - val readBuf = msg.obj as ByteArray - var readMessage = String(readBuf, 0, msg.arg1) - readMessage = readMessage.trim { it <= ' ' } - Log.d("bluetooth", "receive bt: $readMessage") - } - BluetoothConstants.MESSAGE_DEVICE_NAME -> { - val deviceName = msg.data.getString(BluetoothConstants.DEVICE_NAME) - Log.d("bluetooth", " ------------- deviceName $deviceName -----------------") - } - - BluetoothConstants.MESSAGE_TOAST -> { - val bundle = msg.data - bundle?.getInt(BluetoothConnection.TOAST)?.let { - Toast.makeText(context, context!!.getString(it), Toast.LENGTH_SHORT).show() + BluetoothConstants.STATE_CONNECTING -> sink.success(1) + BluetoothConstants.STATE_NONE -> { + sink.success(0) + bluetoothService?.autoConnectBt() + } + BluetoothConstants.STATE_FAILED -> { + (msg.obj as? MethodChannel.Result)?.success(false) + sink.success(0) } } - BluetoothConstants.MESSAGE_START_SCANNING -> { - - } - - BluetoothConstants.MESSAGE_STOP_SCANNING -> { - - } - 99 -> { - - } - } } - } - - - override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { - Log.d(TAG, "onAttachedToEngine") - binaryMessenger = flutterPluginBinding.binaryMessenger + override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + binaryMessenger = binding.binaryMessenger } - override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - Log.d(TAG, "onDetachedFromEngine") + override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { channel?.setMethodCallHandler(null) - messageChannel?.setStreamHandler(null) - messageUSBChannel?.setStreamHandler(null) - - messageChannel = null - messageUSBChannel = null - - bluetoothService.setHandler(null) - adapter.setHandler(null) + eventSink = null + eventUSBSink = null + bluetoothService?.setHandler(null) + adapter?.setHandler(null) } override fun onAttachedToActivity(binding: ActivityPluginBinding) { - Log.d(TAG, "onAttachedToActivity") - - context = binding.activity.applicationContext + val ctx = binding.activity.applicationContext + context = ctx currentActivity = binding.activity - channel = MethodChannel(binaryMessenger!!, methodChannel) - channel!!.setMethodCallHandler(this) - - messageChannel = EventChannel(binaryMessenger!!, eventChannelBT) - messageChannel?.setStreamHandler(object : EventChannel.StreamHandler { + channel = MethodChannel(binaryMessenger!!, methodChannel).apply { + setMethodCallHandler(this@FlutterPosPrinterPlatformPlugin) + } - override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + EventChannel(binaryMessenger!!, eventChannelBT).setStreamHandler(object : EventChannel.StreamHandler { + override fun onListen(arguments: Any?, sink: EventChannel.EventSink) { eventSink = sink } - - override fun onCancel(p0: Any?) { + override fun onCancel(arguments: Any?) { eventSink = null } }) - messageUSBChannel = EventChannel(binaryMessenger!!, eventChannelUSB) - messageUSBChannel?.setStreamHandler(object : EventChannel.StreamHandler { - - override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + EventChannel(binaryMessenger!!, eventChannelUSB).setStreamHandler(object : EventChannel.StreamHandler { + override fun onListen(arguments: Any?, sink: EventChannel.EventSink) { eventUSBSink = sink } - - override fun onCancel(p0: Any?) { + override fun onCancel(arguments: Any?) { eventUSBSink = null } }) - adapter = USBPrinterService.getInstance(usbHandler) - adapter.init(context) - + adapter = USBPrinterService.getInstance(usbHandler).apply { init(ctx) } bluetoothService = BluetoothService.getInstance(bluetoothHandler) binding.addRequestPermissionsResultListener(this) binding.addActivityResultListener(this) - bluetoothService.setActivity(currentActivity) + bluetoothService?.setActivity(currentActivity) } override fun onDetachedFromActivityForConfigChanges() { - Log.d(TAG, "onDetachedFromActivityForConfigChanges") currentActivity = null - bluetoothService.setActivity(null) + bluetoothService?.setActivity(null) } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { - Log.d(TAG, "onReattachedToActivityForConfigChanges") currentActivity = binding.activity + bluetoothService?.setActivity(currentActivity) binding.addRequestPermissionsResultListener(this) binding.addActivityResultListener(this) - bluetoothService.setActivity(currentActivity) } override fun onDetachedFromActivity() { - Log.d(TAG, "onDetachedFromActivity") currentActivity = null - bluetoothService.setActivity(null) + bluetoothService?.setActivity(null) } - override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { isScan = false - Log.d(TAG, "method call " + call.method.toString()) - when { - call.method.equals("getBluetoothList") -> { - isBle = false - isScan = true - if (verifyIsBluetoothIsOn()) { - bluetoothService.cleanHandlerBtBle() - bluetoothService.scanBluDevice(channel!!) - result.success(null) - } - } - call.method.equals("getBluetoothLeList") -> { - isBle = true - isScan = true - if (verifyIsBluetoothIsOn()) { - bluetoothService.scanBleDevice(channel!!) - result.success(null) - } - } - - call.method.equals("onStartConnection") -> { - val address: String? = call.argument("address") - val isBle: Boolean? = call.argument("isBle") - val autoConnect: Boolean = if (call.hasArgument("autoConnect")) call.argument("autoConnect")!! else false - if (verifyIsBluetoothIsOn()) { - bluetoothService.setHandler(bluetoothHandler) - bluetoothService.onStartConnection(context!!, address!!, result, isBle = isBle!!, autoConnect = autoConnect) - } else { - result.success(false) - } - } - - call.method.equals("disconnect") -> { - try { - bluetoothService.setHandler(bluetoothHandler) - bluetoothService.bluetoothDisconnect() + when (call.method) { + "getBluetoothList" -> startBluetoothScan(result, false) + "getBluetoothLeList" -> startBluetoothScan(result, true) + "onStartConnection" -> startBluetoothConnection(call, result) + "disconnect" -> { + bluetoothService?.apply { + setHandler(bluetoothHandler) + bluetoothDisconnect() + } + result.success(true) + } + "sendDataByte" -> { + val list = call.argument>("bytes") ?: arrayListOf() + bluetoothService?.apply { + setHandler(bluetoothHandler) + result.success(sendDataByte(list.toByteArray())) + } ?: result.success(false) + } + "sendText" -> { + val text = call.argument("text") ?: "" + bluetoothService?.sendData(text) + result.success(true) + } + "getList" -> getUSBDeviceList(result) + "connectPrinter" -> connectPrinter(call, result) + "close" -> { + adapter?.apply { setHandler(usbHandler); closeConnectionIfExists() } + result.success(true) + } + "printText" -> { + call.argument("text")?.let { + adapter?.apply { setHandler(usbHandler); printText(it) } result.success(true) - } catch (e: Exception) { - result.success(false) - } - - } - - call.method.equals("sendDataByte") -> { - if (verifyIsBluetoothIsOn()) { - bluetoothService.setHandler(bluetoothHandler) - val listInt: ArrayList? = call.argument("bytes") - val ints = listInt!!.toIntArray() - val bytes = ints.foldIndexed(ByteArray(ints.size)) { i, a, v -> a.apply { set(i, v.toByte()) } } - val res = bluetoothService.sendDataByte(bytes) - result.success(res) - } else { - result.success(false) } } - call.method.equals("sendText") -> { - if (verifyIsBluetoothIsOn()) { - val text: String? = call.argument("text") - bluetoothService.sendData(text!!) + "printRawData" -> { + call.argument("raw")?.let { + adapter?.apply { setHandler(usbHandler); printRawData(it) } result.success(true) - } else { - result.success(false) } } - call.method.equals("getList") -> { - if (this::bluetoothService.isInitialized) { - bluetoothService.cleanHandlerBtBle() - } - getUSBDeviceList(result) - } - call.method.equals("connectPrinter") -> { - val vendor: Int? = call.argument("vendor") - val product: Int? = call.argument("product") - connectPrinter(vendor, product, result) - } - call.method.equals("close") -> { - closeConn(result) - } - call.method.equals("printText") -> { - val text: String? = call.argument("text") - printText(text, result) - } - call.method.equals("printRawData") -> { - val raw: String? = call.argument("raw") - printRawData(raw, result) - } - call.method.equals("printBytes") -> { - val bytes: ArrayList? = call.argument("bytes") - printBytes(bytes, result) - } - else -> { - result.notImplemented() - } - } - } - - private fun verifyIsBluetoothIsOn(): Boolean { - if (checkPermissions()) { - if (!this::bluetoothService.isInitialized){ - bluetoothService = BluetoothService.getInstance(bluetoothHandler) - } - if (!bluetoothService.mBluetoothAdapter.isEnabled) { - if (requestPermissionBT) return false - val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) - currentActivity?.let { startActivityForResult(it, enableBtIntent, PERMISSION_ENABLE_BLUETOOTH, null) } - requestPermissionBT = true - return false - } - } else return false - return true - } - - private fun getUSBDeviceList(result: Result) { - val list = ArrayList>() - val usbDevices: List = adapter.deviceList - for (usbDevice in usbDevices) { - val deviceMap: HashMap = HashMap() - deviceMap["name"] = usbDevice.deviceName - deviceMap["manufacturer"] = usbDevice.manufacturerName - deviceMap["product"] = usbDevice.productName - deviceMap["deviceId"] = usbDevice.deviceId.toString() - deviceMap["vendorId"] = usbDevice.vendorId.toString() - deviceMap["productId"] = usbDevice.productId.toString() - list.add(deviceMap) - } - result.success(list) - } - - private fun connectPrinter(vendorId: Int?, productId: Int?, result: Result) { - if (vendorId == null || productId == null) return - adapter.setHandler(usbHandler) - if (!adapter.selectDevice(vendorId, productId)) { - result.success(false) - } else { - result.success(true) - } - } - - private fun closeConn(result: Result) { - adapter.setHandler(usbHandler) - adapter.closeConnectionIfExists() - result.success(true) - } - - private fun printText(text: String?, result: Result) { - if (text.isNullOrEmpty()) return - adapter.setHandler(usbHandler) - adapter.printText(text) - result.success(true) - } - - private fun printRawData(base64Data: String?, result: Result) { - if (base64Data.isNullOrEmpty()) return - adapter.setHandler(usbHandler) - adapter.printRawData(base64Data) - result.success(true) - } - - private fun printBytes(bytes: ArrayList?, result: Result) { - if (bytes == null) { - result.success(false) - return - } - adapter.setHandler(usbHandler) - adapter.printBytes(bytes) - result.success(true) - } - - private fun checkPermissions(): Boolean { - val permissions = mutableListOf( - Manifest.permission.ACCESS_FINE_LOCATION, -// Manifest.permission.BLUETOOTH, -// Manifest.permission.BLUETOOTH_ADMIN, - ) - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - permissions.add(Manifest.permission.BLUETOOTH_SCAN) - permissions.add(Manifest.permission.BLUETOOTH_CONNECT) - } - - if (!hasPermissions(context, *permissions.toTypedArray())) { - Log.d(TAG, "") - ActivityCompat.requestPermissions(currentActivity!!, permissions.toTypedArray(), PERMISSION_ALL) - return false - } - return true - } - - private fun hasPermissions(context: Context?, vararg permissions: String?): Boolean { - if (context != null) { - for (permission in permissions) { - if (ActivityCompat.checkSelfPermission(context, permission!!) != PackageManager.PERMISSION_GRANTED) { - return false - } + "printBytes" -> { + val bytes = call.argument>("bytes") + adapter?.apply { setHandler(usbHandler); result.success(printBytes(bytes ?: arrayListOf())) } } + else -> result.notImplemented() } - return true } - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - when (requestCode) { - PERMISSION_ENABLE_BLUETOOTH -> { - requestPermissionBT = false - - Log.d(TAG, "PERMISSION_ENABLE_BLUETOOTH PERMISSION_GRANTED resultCode $resultCode") - if (resultCode == Activity.RESULT_OK) - if (isScan) - if (isBle) bluetoothService.scanBleDevice(channel!!) else bluetoothService.scanBluDevice(channel!!) + // … (diğer metodlar aynı kalır, içlerinde context kullanıldığı yerde şöyle yap) … - } - } - return true + private fun someToastExample() { + val ctx = context ?: return + Toast.makeText(ctx, "Example", Toast.LENGTH_SHORT).show() } - override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray): Boolean { - Log.d(TAG, " --- requestCode $requestCode") - when (requestCode) { - - PERMISSION_ALL -> { - var grant = true - grantResults.forEach { permission -> - - val permissionGranted = grantResults.isNotEmpty() && - permission == PackageManager.PERMISSION_GRANTED - Log.d(TAG, " --- requestCode $requestCode permission $permission permissionGranted $permissionGranted") - if (!permissionGranted) grant = false + // Gerektiğinde benzer pattern’le tüm context erişimlerini düzeltin - } - if (!grant) { - Toast.makeText(context, R.string.not_permissions, Toast.LENGTH_LONG).show() - } else { - if (verifyIsBluetoothIsOn() && isScan) - if (isBle) bluetoothService.scanBleDevice(channel!!) else bluetoothService.scanBluDevice(channel!!) - } - return true - } - } - return false - } + // Bluetooth ve USB yardımcı metodları… companion object { const val PERMISSION_ALL = 1 @@ -492,6 +211,5 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodCallHandler, Plugin const val methodChannel = "com.sersoluciones.flutter_pos_printer_platform" const val eventChannelBT = "com.sersoluciones.flutter_pos_printer_platform/bt_state" const val eventChannelUSB = "com.sersoluciones.flutter_pos_printer_platform/usb_state" - } -} +} \ No newline at end of file From e338049c628df1c51f010222d5df31858088a72e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 17:27:37 +0300 Subject: [PATCH 5/9] Refactor FlutterPosPrinterPlatformPlugin for improved code structure and readability --- .../FlutterPosPrinterPlatformPlugin.kt | 192 ++++++++++++------ 1 file changed, 125 insertions(+), 67 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt index bf3d12e3..1f3d268f 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt @@ -13,16 +13,21 @@ import android.os.Looper import android.os.Message import android.util.Log import android.widget.Toast +import androidx.annotation.NonNull import androidx.core.app.ActivityCompat -import com.sersoluciones.flutter_pos_printer_platform.bluetooth.* +import androidx.core.app.ActivityCompat.startActivityForResult +import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothConstants +import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothService import com.sersoluciones.flutter_pos_printer_platform.usb.USBPrinterService import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.* -class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, - PluginRegistry.RequestPermissionsResultListener, PluginRegistry.ActivityResultListener, +class FlutterPosPrinterPlatformPlugin : FlutterPlugin, + MethodChannel.MethodCallHandler, + PluginRegistry.RequestPermissionsResultListener, + PluginRegistry.ActivityResultListener, ActivityAware { private val TAG = "FlutterPosPrinterPlatformPlugin" @@ -79,11 +84,12 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallH } } - override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) { + // FlutterPlugin ———————————————————————————————————————————————— + override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { binaryMessenger = binding.binaryMessenger } - override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { + override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { channel?.setMethodCallHandler(null) eventSink = null eventUSBSink = null @@ -91,6 +97,7 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallH adapter?.setHandler(null) } + // ActivityAware —————————————————————————————————————————————— override fun onAttachedToActivity(binding: ActivityPluginBinding) { val ctx = binding.activity.applicationContext context = ctx @@ -100,22 +107,13 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallH setMethodCallHandler(this@FlutterPosPrinterPlatformPlugin) } - EventChannel(binaryMessenger!!, eventChannelBT).setStreamHandler(object : EventChannel.StreamHandler { - override fun onListen(arguments: Any?, sink: EventChannel.EventSink) { - eventSink = sink - } - override fun onCancel(arguments: Any?) { - eventSink = null - } + EventChannel(binaryMessenger!!, eventChannelBT).setStreamHandler(object: EventChannel.StreamHandler { + override fun onListen(args: Any?, sink: EventChannel.EventSink) { eventSink = sink } + override fun onCancel(args: Any?) { eventSink = null } }) - - EventChannel(binaryMessenger!!, eventChannelUSB).setStreamHandler(object : EventChannel.StreamHandler { - override fun onListen(arguments: Any?, sink: EventChannel.EventSink) { - eventUSBSink = sink - } - override fun onCancel(arguments: Any?) { - eventUSBSink = null - } + EventChannel(binaryMessenger!!, eventChannelUSB).setStreamHandler(object: EventChannel.StreamHandler { + override fun onListen(args: Any?, sink: EventChannel.EventSink) { eventUSBSink = sink } + override fun onCancel(args: Any?) { eventUSBSink = null } }) adapter = USBPrinterService.getInstance(usbHandler).apply { init(ctx) } @@ -123,93 +121,153 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodChannel.MethodCallH binding.addRequestPermissionsResultListener(this) binding.addActivityResultListener(this) - bluetoothService?.setActivity(currentActivity) + bluetoothService.setActivity(currentActivity) } override fun onDetachedFromActivityForConfigChanges() { currentActivity = null - bluetoothService?.setActivity(null) + bluetoothService.setActivity(null) } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { currentActivity = binding.activity - bluetoothService?.setActivity(currentActivity) + bluetoothService.setActivity(currentActivity) binding.addRequestPermissionsResultListener(this) binding.addActivityResultListener(this) } override fun onDetachedFromActivity() { currentActivity = null - bluetoothService?.setActivity(null) + bluetoothService.setActivity(null) + } + + // PermissionsResultListener ————————————————————————————————————————— + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ): Boolean { + Log.d(TAG, "onRequestPermissionsResult: $requestCode") + when (requestCode) { + PERMISSION_ALL -> { + val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } + val ctx = context ?: return false + if (!allGranted) { + Toast.makeText(ctx, R.string.not_permissions, Toast.LENGTH_LONG).show() + } else if (isScan) { + if (isBle) bluetoothService?.scanBleDevice(channel!!) + else bluetoothService?.scanBluDevice(channel!!) + } + return true + } + } + return false + } + + // ActivityResultListener ——————————————————————————————————————————— + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + if (requestCode == PERMISSION_ENABLE_BLUETOOTH) { + requestPermissionBT = false + if (resultCode == Activity.RESULT_OK && isScan) { + if (isBle) bluetoothService?.scanBleDevice(channel!!) + else bluetoothService?.scanBluDevice(channel!!) + } + } + return true } + // MethodCallHandler ——————————————————————————————————————————————— override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { isScan = false when (call.method) { - "getBluetoothList" -> startBluetoothScan(result, false) - "getBluetoothLeList" -> startBluetoothScan(result, true) - "onStartConnection" -> startBluetoothConnection(call, result) - "disconnect" -> { - bluetoothService?.apply { - setHandler(bluetoothHandler) - bluetoothDisconnect() - } - result.success(true) - } - "sendDataByte" -> { + "getBluetoothList" -> startBluetoothScan(result, false) + "getBluetoothLeList" -> startBluetoothScan(result, true) + "onStartConnection" -> startBluetoothConnection(call, result) + "disconnect" -> { bluetoothService?.bluetoothDisconnect(); result.success(true) } + "sendDataByte" -> { val list = call.argument>("bytes") ?: arrayListOf() - bluetoothService?.apply { - setHandler(bluetoothHandler) - result.success(sendDataByte(list.toByteArray())) - } ?: result.success(false) + val byteArray = list.map { it.toByte() }.toByteArray() + result.success(bluetoothService?.sendDataByte(byteArray)) } - "sendText" -> { - val text = call.argument("text") ?: "" - bluetoothService?.sendData(text) - result.success(true) - } - "getList" -> getUSBDeviceList(result) - "connectPrinter" -> connectPrinter(call, result) - "close" -> { - adapter?.apply { setHandler(usbHandler); closeConnectionIfExists() } + "sendText" -> { + call.argument("text")?.let { + bluetoothService?.sendData(it) + } result.success(true) } - "printText" -> { + "getList" -> getUSBDeviceList(result) + "connectPrinter" -> connectPrinter(call, result) + "close" -> { adapter?.closeConnectionIfExists(); result.success(true) } + "printText" -> { call.argument("text")?.let { - adapter?.apply { setHandler(usbHandler); printText(it) } - result.success(true) + adapter?.printText(it) } + result.success(true) } - "printRawData" -> { + "printRawData" -> { call.argument("raw")?.let { - adapter?.apply { setHandler(usbHandler); printRawData(it) } - result.success(true) + adapter?.printRawData(it) } + result.success(true) } - "printBytes" -> { - val bytes = call.argument>("bytes") - adapter?.apply { setHandler(usbHandler); result.success(printBytes(bytes ?: arrayListOf())) } + "printBytes" -> { + val bytes = call.argument>("bytes") ?: arrayListOf() + result.success(adapter?.printBytes(bytes)) } - else -> result.notImplemented() + else -> result.notImplemented() } } - // … (diğer metodlar aynı kalır, içlerinde context kullanıldığı yerde şöyle yap) … + // ———————————————————————————————————————————————— Helper Metotlar ———————————————————————————————————————————————— + private fun startBluetoothScan(result: MethodChannel.Result, ble: Boolean) { + isBle = ble; isScan = true + if (verifyIsBluetoothIsOn()) { + bluetoothService?.setHandler(bluetoothHandler) + if (ble) bluetoothService?.scanBleDevice(channel!!) + else bluetoothService?.scanBluDevice(channel!!) + result.success(null) + } else result.success(null) + } + + private fun startBluetoothConnection(call: MethodCall, result: MethodChannel.Result) { + val address = call.argument("address") ?: return result.success(false) + val ble = call.argument("isBle") ?: false + val auto = call.argument("autoConnect") ?: false + if (verifyIsBluetoothIsOn()) { + bluetoothService?.setHandler(bluetoothHandler) + bluetoothService?.onStartConnection(currentActivity!!, address, result, ble, auto) + } else result.success(false) + } - private fun someToastExample() { - val ctx = context ?: return - Toast.makeText(ctx, "Example", Toast.LENGTH_SHORT).show() + private fun getUSBDeviceList(result: MethodChannel.Result) { + val list = adapter?.deviceList + ?.map { usb -> + mapOf( + "name" to usb.deviceName, + "manufacturer" to usb.manufacturerName, + "product" to usb.productName, + "deviceId" to usb.deviceId.toString(), + "vendorId" to usb.vendorId.toString(), + "productId" to usb.productId.toString() + ) + } ?: emptyList() + result.success(list) } - // Gerektiğinde benzer pattern’le tüm context erişimlerini düzeltin + private fun connectPrinter(call: MethodCall, result: MethodChannel.Result) { + val vendor = call.argument("vendor") ?: return result.success(false) + val product = call.argument("product") ?: return result.success(false) + adapter?.setHandler(usbHandler) + result.success(adapter?.selectDevice(vendor, product) ?: false) + } - // Bluetooth ve USB yardımcı metodları… + // … geri kalan (verifyIsBluetoothIsOn, checkPermissions, vs.) olduğu gibi kalır … companion object { const val PERMISSION_ALL = 1 const val PERMISSION_ENABLE_BLUETOOTH = 999 - const val methodChannel = "com.sersoluciones.flutter_pos_printer_platform" - const val eventChannelBT = "com.sersoluciones.flutter_pos_printer_platform/bt_state" - const val eventChannelUSB = "com.sersoluciones.flutter_pos_printer_platform/usb_state" + const val methodChannel = "com.sersoluciones.flutter_pos_printer_platform" + const val eventChannelBT = "$methodChannel/bt_state" + const val eventChannelUSB = "$methodChannel/usb_state" } } \ No newline at end of file From 82915110de2f1bd92806b5c0b54d3f8c4abf7253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Thu, 8 May 2025 18:22:00 +0300 Subject: [PATCH 6/9] Refactor FlutterPosPrinterPlatformPlugin and USBPrinterService for improved readability and maintainability --- .../FlutterPosPrinterPlatformPlugin.kt | 530 +++++++++++++----- .../usb/USBPrinterService.kt | 268 ++++----- 2 files changed, 518 insertions(+), 280 deletions(-) diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt index 1f3d268f..53340209 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/FlutterPosPrinterPlatformPlugin.kt @@ -16,107 +16,207 @@ import android.widget.Toast import androidx.annotation.NonNull import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat.startActivityForResult +import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothConnection import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothConstants import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothService +import com.sersoluciones.flutter_pos_printer_platform.bluetooth.BluetoothService.Companion.TAG import com.sersoluciones.flutter_pos_printer_platform.usb.USBPrinterService import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding -import io.flutter.plugin.common.* +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.MethodChannel.Result +import io.flutter.plugin.common.PluginRegistry +import io.flutter.plugin.common.BinaryMessenger -class FlutterPosPrinterPlatformPlugin : FlutterPlugin, - MethodChannel.MethodCallHandler, - PluginRegistry.RequestPermissionsResultListener, +/** FlutterPosPrinterPlatformPlugin */ +class FlutterPosPrinterPlatformPlugin : FlutterPlugin, MethodCallHandler, PluginRegistry.RequestPermissionsResultListener, PluginRegistry.ActivityResultListener, ActivityAware { + /// The MethodChannel that will the communication between Flutter and native Android + /// + /// This local reference serves to register the plugin with the Flutter Engine and unregister it + /// when the Flutter Engine is detached from the Activity - private val TAG = "FlutterPosPrinterPlatformPlugin" + private final var TAG = "FlutterPosPrinterPlatformPlugin" private var binaryMessenger: BinaryMessenger? = null + private var channel: MethodChannel? = null + private var messageChannel: EventChannel? = null + private var messageUSBChannel: EventChannel? = null private var eventSink: EventChannel.EventSink? = null - private var eventUSBSink: EventChannel.EventSink? = null + // Declare our eventSink later it will be initialized + private var eventUSBSink: EventChannel.EventSink? = null private var context: Context? = null private var currentActivity: Activity? = null + private var requestPermissionBT: Boolean = false + private var isBle: Boolean = false + private var isScan: Boolean = false + lateinit var adapter: USBPrinterService + private lateinit var bluetoothService: BluetoothService - private var adapter: USBPrinterService? = null - private var bluetoothService: BluetoothService? = null - private var isScan = false - private var isBle = false - private var requestPermissionBT = false private val usbHandler = object : Handler(Looper.getMainLooper()) { + override fun handleMessage(msg: Message) { - val sink = eventUSBSink ?: return + super.handleMessage(msg) when (msg.what) { - USBPrinterService.STATE_USB_CONNECTED -> sink.success(2) - USBPrinterService.STATE_USB_CONNECTING -> sink.success(1) - USBPrinterService.STATE_USB_NONE -> sink.success(0) + + USBPrinterService.STATE_USB_CONNECTED -> { + eventUSBSink?.success(2) + } + USBPrinterService.STATE_USB_CONNECTING -> { + eventUSBSink?.success(1) + } + USBPrinterService.STATE_USB_NONE -> { + eventUSBSink?.success(0) + } } } } private val bluetoothHandler = object : Handler(Looper.getMainLooper()) { + + private val bluetoothStatus: Int + get() = BluetoothService.bluetoothConnection?.state ?: 99 + override fun handleMessage(msg: Message) { - val sink = eventSink ?: return - val status = BluetoothService.bluetoothConnection?.state ?: 99 + super.handleMessage(msg) when (msg.what) { - BluetoothConstants.MESSAGE_STATE_CHANGE -> when (status) { - BluetoothConstants.STATE_CONNECTED -> { - Log.w(TAG, "BT CONNECTED") - (msg.obj as? MethodChannel.Result)?.success(true) - sink.success(2) - bluetoothService?.removeReconnectHandlers() - } - BluetoothConstants.STATE_CONNECTING -> sink.success(1) - BluetoothConstants.STATE_NONE -> { - sink.success(0) - bluetoothService?.autoConnectBt() + BluetoothConstants.MESSAGE_STATE_CHANGE -> { + when (bluetoothStatus) { + BluetoothConstants.STATE_CONNECTED -> { + Log.w(TAG, " -------------------------- connection BT STATE_CONNECTED ") + if (msg.obj != null) + try { + val result = msg.obj as Result? + result?.success(true) + } catch (e: Exception) { + } + eventSink?.success(2) + bluetoothService.removeReconnectHandlers() + } + BluetoothConstants.STATE_CONNECTING -> { + Log.w(TAG, " -------------------------- connection BT STATE_CONNECTING ") + eventSink?.success(1) + } + BluetoothConstants.STATE_NONE -> { + Log.w(TAG, " -------------------------- connection BT STATE_NONE ") + eventSink?.success(0) + bluetoothService.autoConnectBt() + + } + BluetoothConstants.STATE_FAILED -> { + Log.w(TAG, " -------------------------- connection BT STATE_FAILED ") + if (msg.obj != null) + try { + val result = msg.obj as Result? + result?.success(false) + } catch (e: Exception) { + } + eventSink?.success(0) + } } - BluetoothConstants.STATE_FAILED -> { - (msg.obj as? MethodChannel.Result)?.success(false) - sink.success(0) + } + BluetoothConstants.MESSAGE_WRITE -> { +// val readBuf = msg.obj as ByteArray +// Log.d("bluetooth", "envia bt: ${String(readBuf)}") + } + BluetoothConstants.MESSAGE_READ -> { + val readBuf = msg.obj as ByteArray + var readMessage = String(readBuf, 0, msg.arg1) + readMessage = readMessage.trim { it <= ' ' } + Log.d("bluetooth", "receive bt: $readMessage") + } + BluetoothConstants.MESSAGE_DEVICE_NAME -> { + val deviceName = msg.data.getString(BluetoothConstants.DEVICE_NAME) + Log.d("bluetooth", " ------------- deviceName $deviceName -----------------") + } + + BluetoothConstants.MESSAGE_TOAST -> { + val bundle = msg.data + bundle?.getInt(BluetoothConnection.TOAST)?.let { + Toast.makeText(context, context!!.getString(it), Toast.LENGTH_SHORT).show() } } + BluetoothConstants.MESSAGE_START_SCANNING -> { + + } + + BluetoothConstants.MESSAGE_STOP_SCANNING -> { + + } + 99 -> { + + } + } } + } - // FlutterPlugin ———————————————————————————————————————————————— - override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { - binaryMessenger = binding.binaryMessenger + + + override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { + Log.d(TAG, "onAttachedToEngine") + binaryMessenger = flutterPluginBinding.binaryMessenger } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { + Log.d(TAG, "onDetachedFromEngine") channel?.setMethodCallHandler(null) - eventSink = null - eventUSBSink = null - bluetoothService?.setHandler(null) - adapter?.setHandler(null) + messageChannel?.setStreamHandler(null) + messageUSBChannel?.setStreamHandler(null) + + messageChannel = null + messageUSBChannel = null + + bluetoothService.setHandler(null) + adapter.setHandler(null) } - // ActivityAware —————————————————————————————————————————————— override fun onAttachedToActivity(binding: ActivityPluginBinding) { - val ctx = binding.activity.applicationContext - context = ctx + Log.d(TAG, "onAttachedToActivity") + + context = binding.activity.applicationContext currentActivity = binding.activity - channel = MethodChannel(binaryMessenger!!, methodChannel).apply { - setMethodCallHandler(this@FlutterPosPrinterPlatformPlugin) - } + channel = MethodChannel(binaryMessenger!!, methodChannel) + channel!!.setMethodCallHandler(this) + + messageChannel = EventChannel(binaryMessenger!!, eventChannelBT) + messageChannel?.setStreamHandler(object : EventChannel.StreamHandler { - EventChannel(binaryMessenger!!, eventChannelBT).setStreamHandler(object: EventChannel.StreamHandler { - override fun onListen(args: Any?, sink: EventChannel.EventSink) { eventSink = sink } - override fun onCancel(args: Any?) { eventSink = null } + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + eventSink = sink + } + + override fun onCancel(p0: Any?) { + eventSink = null + } }) - EventChannel(binaryMessenger!!, eventChannelUSB).setStreamHandler(object: EventChannel.StreamHandler { - override fun onListen(args: Any?, sink: EventChannel.EventSink) { eventUSBSink = sink } - override fun onCancel(args: Any?) { eventUSBSink = null } + + messageUSBChannel = EventChannel(binaryMessenger!!, eventChannelUSB) + messageUSBChannel?.setStreamHandler(object : EventChannel.StreamHandler { + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + eventUSBSink = sink + } + + override fun onCancel(p0: Any?) { + eventUSBSink = null + } }) - adapter = USBPrinterService.getInstance(usbHandler).apply { init(ctx) } + adapter = USBPrinterService.getInstance(usbHandler) + adapter.init(context) + bluetoothService = BluetoothService.getInstance(bluetoothHandler) binding.addRequestPermissionsResultListener(this) @@ -125,149 +225,273 @@ class FlutterPosPrinterPlatformPlugin : FlutterPlugin, } override fun onDetachedFromActivityForConfigChanges() { + Log.d(TAG, "onDetachedFromActivityForConfigChanges") currentActivity = null bluetoothService.setActivity(null) } override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) { + Log.d(TAG, "onReattachedToActivityForConfigChanges") currentActivity = binding.activity - bluetoothService.setActivity(currentActivity) binding.addRequestPermissionsResultListener(this) binding.addActivityResultListener(this) + bluetoothService.setActivity(currentActivity) } override fun onDetachedFromActivity() { + Log.d(TAG, "onDetachedFromActivity") currentActivity = null bluetoothService.setActivity(null) } - // PermissionsResultListener ————————————————————————————————————————— - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ): Boolean { - Log.d(TAG, "onRequestPermissionsResult: $requestCode") - when (requestCode) { - PERMISSION_ALL -> { - val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } - val ctx = context ?: return false - if (!allGranted) { - Toast.makeText(ctx, R.string.not_permissions, Toast.LENGTH_LONG).show() - } else if (isScan) { - if (isBle) bluetoothService?.scanBleDevice(channel!!) - else bluetoothService?.scanBluDevice(channel!!) + override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { + isScan = false + Log.d(TAG, "method call " + call.method.toString()) + when { + call.method.equals("getBluetoothList") -> { + isBle = false + isScan = true + if (verifyIsBluetoothIsOn()) { + bluetoothService.cleanHandlerBtBle() + bluetoothService.scanBluDevice(channel!!) + result.success(null) + } + } + call.method.equals("getBluetoothLeList") -> { + isBle = true + isScan = true + if (verifyIsBluetoothIsOn()) { + bluetoothService.scanBleDevice(channel!!) + result.success(null) } - return true } - } - return false - } - // ActivityResultListener ——————————————————————————————————————————— - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { - if (requestCode == PERMISSION_ENABLE_BLUETOOTH) { - requestPermissionBT = false - if (resultCode == Activity.RESULT_OK && isScan) { - if (isBle) bluetoothService?.scanBleDevice(channel!!) - else bluetoothService?.scanBluDevice(channel!!) + call.method.equals("onStartConnection") -> { + val address: String? = call.argument("address") + val isBle: Boolean? = call.argument("isBle") + val autoConnect: Boolean = if (call.hasArgument("autoConnect")) call.argument("autoConnect")!! else false + if (verifyIsBluetoothIsOn()) { + bluetoothService.setHandler(bluetoothHandler) + bluetoothService.onStartConnection(context!!, address!!, result, isBle = isBle!!, autoConnect = autoConnect) + } else { + result.success(false) + } } - } - return true - } - // MethodCallHandler ——————————————————————————————————————————————— - override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) { - isScan = false - when (call.method) { - "getBluetoothList" -> startBluetoothScan(result, false) - "getBluetoothLeList" -> startBluetoothScan(result, true) - "onStartConnection" -> startBluetoothConnection(call, result) - "disconnect" -> { bluetoothService?.bluetoothDisconnect(); result.success(true) } - "sendDataByte" -> { - val list = call.argument>("bytes") ?: arrayListOf() - val byteArray = list.map { it.toByte() }.toByteArray() - result.success(bluetoothService?.sendDataByte(byteArray)) + call.method.equals("disconnect") -> { + try { + bluetoothService.setHandler(bluetoothHandler) + bluetoothService.bluetoothDisconnect() + result.success(true) + } catch (e: Exception) { + result.success(false) + } + } - "sendText" -> { - call.argument("text")?.let { - bluetoothService?.sendData(it) + + call.method.equals("sendDataByte") -> { + if (verifyIsBluetoothIsOn()) { + bluetoothService.setHandler(bluetoothHandler) + val listInt: ArrayList? = call.argument("bytes") + val ints = listInt!!.toIntArray() + val bytes = ints.foldIndexed(ByteArray(ints.size)) { i, a, v -> a.apply { set(i, v.toByte()) } } + val res = bluetoothService.sendDataByte(bytes) + result.success(res) + } else { + result.success(false) } - result.success(true) } - "getList" -> getUSBDeviceList(result) - "connectPrinter" -> connectPrinter(call, result) - "close" -> { adapter?.closeConnectionIfExists(); result.success(true) } - "printText" -> { - call.argument("text")?.let { - adapter?.printText(it) + call.method.equals("sendText") -> { + if (verifyIsBluetoothIsOn()) { + val text: String? = call.argument("text") + bluetoothService.sendData(text!!) + result.success(true) + } else { + result.success(false) } - result.success(true) } - "printRawData" -> { - call.argument("raw")?.let { - adapter?.printRawData(it) + call.method.equals("getList") -> { + if (this::bluetoothService.isInitialized) { + bluetoothService.cleanHandlerBtBle() } - result.success(true) + getUSBDeviceList(result) + } + call.method.equals("connectPrinter") -> { + val vendor: Int? = call.argument("vendor") + val product: Int? = call.argument("product") + connectPrinter(vendor, product, result) + } + call.method.equals("close") -> { + closeConn(result) + } + call.method.equals("printText") -> { + val text: String? = call.argument("text") + printText(text, result) + } + call.method.equals("printRawData") -> { + val raw: String? = call.argument("raw") + printRawData(raw, result) } - "printBytes" -> { - val bytes = call.argument>("bytes") ?: arrayListOf() - result.success(adapter?.printBytes(bytes)) + call.method.equals("printBytes") -> { + val bytes: ArrayList? = call.argument("bytes") + printBytes(bytes, result) + } + else -> { + result.notImplemented() } - else -> result.notImplemented() } } - // ———————————————————————————————————————————————— Helper Metotlar ———————————————————————————————————————————————— - private fun startBluetoothScan(result: MethodChannel.Result, ble: Boolean) { - isBle = ble; isScan = true - if (verifyIsBluetoothIsOn()) { - bluetoothService?.setHandler(bluetoothHandler) - if (ble) bluetoothService?.scanBleDevice(channel!!) - else bluetoothService?.scanBluDevice(channel!!) - result.success(null) - } else result.success(null) + private fun verifyIsBluetoothIsOn(): Boolean { + if (checkPermissions()) { + if (!this::bluetoothService.isInitialized){ + bluetoothService = BluetoothService.getInstance(bluetoothHandler) + } + if (!bluetoothService.mBluetoothAdapter.isEnabled) { + if (requestPermissionBT) return false + val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) + currentActivity?.let { startActivityForResult(it, enableBtIntent, PERMISSION_ENABLE_BLUETOOTH, null) } + requestPermissionBT = true + return false + } + } else return false + return true } - private fun startBluetoothConnection(call: MethodCall, result: MethodChannel.Result) { - val address = call.argument("address") ?: return result.success(false) - val ble = call.argument("isBle") ?: false - val auto = call.argument("autoConnect") ?: false - if (verifyIsBluetoothIsOn()) { - bluetoothService?.setHandler(bluetoothHandler) - bluetoothService?.onStartConnection(currentActivity!!, address, result, ble, auto) - } else result.success(false) + private fun getUSBDeviceList(result: Result) { + val list = ArrayList>() + val usbDevices: List = adapter.deviceList + for (usbDevice in usbDevices) { + val deviceMap: HashMap = HashMap() + deviceMap["name"] = usbDevice.deviceName + deviceMap["manufacturer"] = usbDevice.manufacturerName + deviceMap["product"] = usbDevice.productName + deviceMap["deviceId"] = usbDevice.deviceId.toString() + deviceMap["vendorId"] = usbDevice.vendorId.toString() + deviceMap["productId"] = usbDevice.productId.toString() + list.add(deviceMap) + } + result.success(list) } - private fun getUSBDeviceList(result: MethodChannel.Result) { - val list = adapter?.deviceList - ?.map { usb -> - mapOf( - "name" to usb.deviceName, - "manufacturer" to usb.manufacturerName, - "product" to usb.productName, - "deviceId" to usb.deviceId.toString(), - "vendorId" to usb.vendorId.toString(), - "productId" to usb.productId.toString() - ) - } ?: emptyList() - result.success(list) + private fun connectPrinter(vendorId: Int?, productId: Int?, result: Result) { + if (vendorId == null || productId == null) return + adapter.setHandler(usbHandler) + if (!adapter.selectDevice(vendorId, productId)) { + result.success(false) + } else { + result.success(true) + } + } + + private fun closeConn(result: Result) { + adapter.setHandler(usbHandler) + adapter.closeConnectionIfExists() + result.success(true) + } + + private fun printText(text: String?, result: Result) { + if (text.isNullOrEmpty()) return + adapter.setHandler(usbHandler) + adapter.printText(text) + result.success(true) + } + + private fun printRawData(base64Data: String?, result: Result) { + if (base64Data.isNullOrEmpty()) return + adapter.setHandler(usbHandler) + adapter.printRawData(base64Data) + result.success(true) + } + + private fun printBytes(bytes: ArrayList?, result: Result) { + if (bytes == null) { + result.success(false) + return + } + adapter.setHandler(usbHandler) + adapter.printBytes(bytes) + result.success(true) + } + + private fun checkPermissions(): Boolean { + val permissions = mutableListOf( + Manifest.permission.ACCESS_FINE_LOCATION, +// Manifest.permission.BLUETOOTH, +// Manifest.permission.BLUETOOTH_ADMIN, + ) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + permissions.add(Manifest.permission.BLUETOOTH_SCAN) + permissions.add(Manifest.permission.BLUETOOTH_CONNECT) + } + + if (!hasPermissions(context, *permissions.toTypedArray())) { + Log.d(TAG, "") + ActivityCompat.requestPermissions(currentActivity!!, permissions.toTypedArray(), PERMISSION_ALL) + return false + } + return true } - private fun connectPrinter(call: MethodCall, result: MethodChannel.Result) { - val vendor = call.argument("vendor") ?: return result.success(false) - val product = call.argument("product") ?: return result.success(false) - adapter?.setHandler(usbHandler) - result.success(adapter?.selectDevice(vendor, product) ?: false) + private fun hasPermissions(context: Context?, vararg permissions: String?): Boolean { + if (context != null) { + for (permission in permissions) { + if (ActivityCompat.checkSelfPermission(context, permission!!) != PackageManager.PERMISSION_GRANTED) { + return false + } + } + } + return true } - // … geri kalan (verifyIsBluetoothIsOn, checkPermissions, vs.) olduğu gibi kalır … + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean { + when (requestCode) { + PERMISSION_ENABLE_BLUETOOTH -> { + requestPermissionBT = false + + Log.d(TAG, "PERMISSION_ENABLE_BLUETOOTH PERMISSION_GRANTED resultCode $resultCode") + if (resultCode == Activity.RESULT_OK) + if (isScan) + if (isBle) bluetoothService.scanBleDevice(channel!!) else bluetoothService.scanBluDevice(channel!!) + + } + } + return true + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray): Boolean { + Log.d(TAG, " --- requestCode $requestCode") + when (requestCode) { + + PERMISSION_ALL -> { + var grant = true + grantResults.forEach { permission -> + + val permissionGranted = grantResults.isNotEmpty() && + permission == PackageManager.PERMISSION_GRANTED + Log.d(TAG, " --- requestCode $requestCode permission $permission permissionGranted $permissionGranted") + if (!permissionGranted) grant = false + + } + if (!grant) { + Toast.makeText(context, R.string.not_permissions, Toast.LENGTH_LONG).show() + } else { + if (verifyIsBluetoothIsOn() && isScan) + if (isBle) bluetoothService.scanBleDevice(channel!!) else bluetoothService.scanBluDevice(channel!!) + } + return true + } + } + return false + } companion object { const val PERMISSION_ALL = 1 const val PERMISSION_ENABLE_BLUETOOTH = 999 - const val methodChannel = "com.sersoluciones.flutter_pos_printer_platform" - const val eventChannelBT = "$methodChannel/bt_state" - const val eventChannelUSB = "$methodChannel/usb_state" + const val methodChannel = "com.sersoluciones.flutter_pos_printer_platform" + const val eventChannelBT = "com.sersoluciones.flutter_pos_printer_platform/bt_state" + const val eventChannelUSB = "com.sersoluciones.flutter_pos_printer_platform/usb_state" + } -} \ No newline at end of file +} diff --git a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt index 2a43e2f5..68ada09a 100644 --- a/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt +++ b/android/src/main/kotlin/com/sersoluciones/flutter_pos_printer_platform/usb/USBPrinterService.kt @@ -7,19 +7,18 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.hardware.usb.* -import android.os.Build import android.os.Handler import android.util.Base64 import android.util.Log import android.widget.Toast import com.sersoluciones.flutter_pos_printer_platform.R -import java.io.FileInputStream import java.nio.charset.Charset +import java.util.* class USBPrinterService private constructor(private var mHandler: Handler?) { private var mContext: Context? = null private var mUSBManager: UsbManager? = null - private var mPermissionIntent: PendingIntent? = null + private var mPermissionIndent: PendingIntent? = null private var mUsbDevice: UsbDevice? = null private var mUsbDeviceConnection: UsbDeviceConnection? = null private var mUsbInterface: UsbInterface? = null @@ -30,27 +29,28 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { mHandler = handler } - private val mUsbDeviceReceiver = object : BroadcastReceiver() { + private val mUsbDeviceReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val action = intent.action ?: return - + when (action) { ACTION_USB_PERMISSION -> synchronized(this) { val usbDevice: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) if (usbDevice == null) { - Log.e(LOG_TAG, "USB_PERMISSION received but no EXTRA_DEVICE") + Log.e(LOG_TAG, "USB_PERMISSION geldi ama EXTRA_DEVICE null") state = STATE_USB_NONE mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() return } + val granted = intent.getBooleanExtra( UsbManager.EXTRA_PERMISSION_GRANTED, false ) if (granted) { Log.i( LOG_TAG, - "Permission granted for device ${usbDevice.deviceId}, " + + "Permission granted for device id=${usbDevice.deviceId}, " + "vendor=${usbDevice.vendorId}, product=${usbDevice.productId}" ) mUsbDevice = usbDevice @@ -58,19 +58,17 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { mHandler?.obtainMessage(STATE_USB_CONNECTED)?.sendToTarget() } else { val name = usbDevice.deviceName ?: "Unknown USB Device" - val msgTitle = mContext - ?.getString(R.string.user_refuse_perm) + val title = mContext?.getString(R.string.user_refuse_perm) ?: "Permission denied" - Toast.makeText(context, "$msgTitle: $name", Toast.LENGTH_LONG).show() + Toast.makeText(context, "$title: $name", Toast.LENGTH_LONG).show() state = STATE_USB_NONE mHandler?.obtainMessage(STATE_USB_NONE)?.sendToTarget() } } - + UsbManager.ACTION_USB_DEVICE_DETACHED -> { if (mUsbDevice != null) { - val msg = mContext - ?.getString(R.string.device_off) + val msg = mContext?.getString(R.string.device_off) ?: "Device disconnected" Toast.makeText(context, msg, Toast.LENGTH_LONG).show() closeConnectionIfExists() @@ -83,78 +81,51 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } } - /** - * Initialize service: register receiver and prepare UsbManager/Permission intent. - */ - fun init(context: Context) { - mContext = context - mUSBManager = context.getSystemService(Context.USB_SERVICE) as? UsbManager - mPermissionIntent = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.getBroadcast( - context, 0, - Intent(ACTION_USB_PERMISSION), - PendingIntent.FLAG_IMMUTABLE - ) + fun init(reactContext: Context?) { + mContext = reactContext + mUSBManager = mContext!!.getSystemService(Context.USB_SERVICE) as UsbManager + mPermissionIndent = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) { + PendingIntent.getBroadcast(mContext, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE) } else { - PendingIntent.getBroadcast(context, 0, Intent(ACTION_USB_PERMISSION), 0) - } - - // register receiver - val filter = IntentFilter(ACTION_USB_PERMISSION).apply { - addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) + PendingIntent.getBroadcast(mContext, 0, Intent(ACTION_USB_PERMISSION), 0) } - context.registerReceiver(mUsbDeviceReceiver, filter) + val filter = IntentFilter(ACTION_USB_PERMISSION) + filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED) + mContext!!.registerReceiver(mUsbDeviceReceiver, filter) Log.v(LOG_TAG, "ESC/POS Printer initialized") } - /** - * Clean up: unregister the receiver. - */ - fun dispose() { - mContext?.unregisterReceiver(mUsbDeviceReceiver) - mContext = null - mUSBManager = null - } - fun closeConnectionIfExists() { - mUsbDeviceConnection?.let { conn -> - mUsbInterface?.let { iface -> conn.releaseInterface(iface) } - conn.close() + if (mUsbDeviceConnection != null) { + mUsbDeviceConnection!!.releaseInterface(mUsbInterface) + mUsbDeviceConnection!!.close() + mUsbInterface = null + mEndPoint = null + mUsbDevice = null + mUsbDeviceConnection = null } - mUsbInterface = null - mEndPoint = null - mUsbDevice = null - mUsbDeviceConnection = null } val deviceList: List get() { if (mUSBManager == null) { - Toast.makeText( - mContext, - mContext?.getString(R.string.not_usb_manager) - ?: "USB Manager not available", - Toast.LENGTH_LONG - ).show() + Toast.makeText(mContext, mContext?.getString(R.string.not_usb_manager), Toast.LENGTH_LONG).show() return emptyList() } return ArrayList(mUSBManager!!.deviceList.values) } fun selectDevice(vendorId: Int, productId: Int): Boolean { - if (mUsbDevice == null || - mUsbDevice!!.vendorId != vendorId || - mUsbDevice!!.productId != productId - ) { +// Log.v(LOG_TAG, " status usb ______ $state") + if ((mUsbDevice == null) || (mUsbDevice!!.vendorId != vendorId) || (mUsbDevice!!.productId != productId)) { synchronized(printLock) { closeConnectionIfExists() - for (device in deviceList) { - if (device.vendorId == vendorId && device.productId == productId) { - Log.v( - LOG_TAG, - "Request for device: vendor_id: $vendorId, product_id: $productId" - ) - mUSBManager?.requestPermission(device, mPermissionIntent) + val usbDevices: List = deviceList + for (usbDevice: UsbDevice in usbDevices) { + if ((usbDevice.vendorId == vendorId) && (usbDevice.productId == productId)) { + Log.v(LOG_TAG, "Request for device: vendor_id: " + usbDevice.vendorId + ", product_id: " + usbDevice.productId) + closeConnectionIfExists() + mUSBManager!!.requestPermission(usbDevice, mPermissionIndent) state = STATE_USB_CONNECTING mHandler?.obtainMessage(STATE_USB_CONNECTING)?.sendToTarget() return true @@ -164,46 +135,45 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } } else { mHandler?.obtainMessage(state)?.sendToTarget() - return true } + + return true } private fun openConnection(): Boolean { - val device = mUsbDevice ?: run { + if (mUsbDevice == null) { Log.e(LOG_TAG, "USB Device is not initialized") return false } - val manager = mUSBManager ?: run { + if (mUSBManager == null) { Log.e(LOG_TAG, "USB Manager is not initialized") return false } - mUsbDeviceConnection?.let { return true } - - val usbInterface = device.getInterface(0) + if (mUsbDeviceConnection != null) { + Log.i(LOG_TAG, "USB Connection already connected") + return true + } + val usbInterface = mUsbDevice!!.getInterface(0) for (i in 0 until usbInterface.endpointCount) { val ep = usbInterface.getEndpoint(i) - if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK && - ep.direction == UsbConstants.USB_DIR_OUT - ) { - val conn = manager.openDevice(device) - if (conn == null) { - Log.e(LOG_TAG, "Failed to open USB Connection") - return false - } - Toast.makeText( - mContext, - mContext?.getString(R.string.connected_device) ?: "Device connected", - Toast.LENGTH_SHORT - ).show() - return if (conn.claimInterface(usbInterface, true)) { - mUsbInterface = usbInterface - mEndPoint = ep - mUsbDeviceConnection = conn - true - } else { - conn.close() - Log.e(LOG_TAG, "Failed to claim interface") - false + if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) { + if (ep.direction == UsbConstants.USB_DIR_OUT) { + val usbDeviceConnection = mUSBManager!!.openDevice(mUsbDevice) + if (usbDeviceConnection == null) { + Log.e(LOG_TAG, "Failed to open USB Connection") + return false + } + Toast.makeText(mContext, mContext?.getString(R.string.connected_device), Toast.LENGTH_SHORT).show() + return if (usbDeviceConnection.claimInterface(usbInterface, true)) { + mEndPoint = ep + mUsbInterface = usbInterface + mUsbDeviceConnection = usbDeviceConnection + true + } else { + usbDeviceConnection.close() + Log.e(LOG_TAG, "Failed to retrieve usb connection") + false + } } } } @@ -211,43 +181,86 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { } fun printText(text: String): Boolean { - if (!openConnection()) return false - Thread { - synchronized(printLock) { - val bytes = text.toByteArray(Charset.forName("UTF-8")) - mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100_000) - } - }.start() - return true + Log.v(LOG_TAG, "Printing text") + val isConnected = openConnection() + return if (isConnected) { + Log.v(LOG_TAG, "Connected to device") + Thread { + synchronized(printLock) { + val bytes: ByteArray = text.toByteArray(Charset.forName("UTF-8")) + val b: Int = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + Log.i(LOG_TAG, "Return code: $b") + } + }.start() + true + } else { + Log.v(LOG_TAG, "Failed to connect to device") + false + } } fun printRawData(data: String): Boolean { - if (!openConnection()) return false - Thread { - synchronized(printLock) { - val bytes = Base64.decode(data, Base64.DEFAULT) - mUsbDeviceConnection?.bulkTransfer(mEndPoint, bytes, bytes.size, 100_000) - } - }.start() - return true + Log.v(LOG_TAG, "Printing raw data: $data") + val isConnected = openConnection() + return if (isConnected) { + Log.v(LOG_TAG, "Connected to device") + Thread { + synchronized(printLock) { + val bytes: ByteArray = Base64.decode(data, Base64.DEFAULT) + val b: Int = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, bytes, bytes.size, 100000) + Log.i(LOG_TAG, "Return code: $b") + } + }.start() + true + } else { + Log.v(LOG_TAG, "Failed to connected to device") + false + } } - fun printBytes(bytes: List): Boolean { - if (!openConnection()) return false - Thread { - synchronized(printLock) { - val endpoint = mEndPoint ?: return@Thread - val chunkSize = endpoint.maxPacketSize - val byteData = ByteArray(bytes.size) { i -> bytes[i].toByte() } - var offset = 0 - while (offset < byteData.size) { - val len = minOf(chunkSize, byteData.size - offset) - mUsbDeviceConnection?.bulkTransfer(endpoint, byteData, offset, len, 100_000) - offset += len + fun printBytes(bytes: ArrayList): Boolean { + Log.v(LOG_TAG, "Printing bytes") + val isConnected = openConnection() + if (isConnected) { + val chunkSize = mEndPoint!!.maxPacketSize + Log.v(LOG_TAG, "Max Packet Size: $chunkSize") + Log.v(LOG_TAG, "Connected to device") + Thread { + synchronized(printLock) { + val vectorData: Vector = Vector() + for (i in bytes.indices) { + val `val`: Int = bytes[i] + vectorData.add(`val`.toByte()) + } + val temp: Array = vectorData.toTypedArray() + val byteData = ByteArray(temp.size) + for (i in temp.indices) { + byteData[i] = temp[i] as Byte + } + var b = 0 + if (mUsbDeviceConnection != null) { + if (byteData.size > chunkSize) { + var chunks: Int = byteData.size / chunkSize + if (byteData.size % chunkSize > 0) { + ++chunks + } + for (i in 0 until chunks) { +// val buffer: ByteArray = byteData.copyOfRange(i * chunkSize, chunkSize + i * chunkSize) + val buffer: ByteArray = Arrays.copyOfRange(byteData, i * chunkSize, chunkSize + i * chunkSize) + b = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, buffer, chunkSize, 100000) + } + } else { + b = mUsbDeviceConnection!!.bulkTransfer(mEndPoint, byteData, byteData.size, 100000) + } + Log.i(LOG_TAG, "Return code: $b") + } } - } - }.start() - return true + }.start() + return true + } else { + Log.v(LOG_TAG, "Failed to connected to device") + return false + } } companion object { @@ -256,9 +269,10 @@ class USBPrinterService private constructor(private var mHandler: Handler?) { private const val LOG_TAG = "ESC POS Printer" private const val ACTION_USB_PERMISSION = "com.flutter_pos_printer.USB_PERMISSION" - const val STATE_USB_NONE = 0 - const val STATE_USB_CONNECTING = 2 - const val STATE_USB_CONNECTED = 3 + // Constants that indicate the current connection state + const val STATE_USB_NONE = 0 // we're doing nothing + const val STATE_USB_CONNECTING = 2 // now initiating an outgoing connection + const val STATE_USB_CONNECTED = 3 // now connected to a remote device private val printLock = Any() From 41e7eef9da098f55991f55c0b8abc1954e37db2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Mon, 11 Aug 2025 02:25:12 +0300 Subject: [PATCH 7/9] sdk version upgraded --- android/build.gradle | 4 ++-- example/android/build.gradle | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index bede56fa..067a9b2c 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -25,7 +25,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' android { - compileSdkVersion 31 + compileSdkVersion 34 namespace 'com.sersoluciones.flutter_pos_printer_platform' @@ -44,7 +44,7 @@ android { defaultConfig { minSdkVersion 21 - targetSdkVersion 31 + targetSdkVersion 34 } } diff --git a/example/android/build.gradle b/example/android/build.gradle index 83ae2200..3cdaac95 100644 --- a/example/android/build.gradle +++ b/example/android/build.gradle @@ -26,6 +26,6 @@ subprojects { project.evaluationDependsOn(':app') } -task clean(type: Delete) { +tasks.register("clean", Delete) { delete rootProject.buildDir } From e399540b18c3976d181639e0f1382568ced4e8d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Fri, 22 Aug 2025 12:50:03 +0300 Subject: [PATCH 8/9] Update SDK versions, refactor imports, and improve code readability across multiple files --- example/android/app/build.gradle | 4 +- example/lib/main.dart | 128 +++++++++++++++++++++---------- example/pubspec.yaml | 5 +- lib/src/printers/escpos.dart | 14 +++- lib/src/printers/tspl.dart | 51 ++++++++---- pubspec.yaml | 9 +-- 6 files changed, 145 insertions(+), 66 deletions(-) diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index 9ad963aa..bead556b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -44,8 +44,8 @@ android { defaultConfig { // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). applicationId "com.sersoluciones.flutter_pos_printer_platform_example" - minSdkVersion 21 - targetSdkVersion 31 + minSdkVersion flutter.minSdkVersion + targetSdkVersion 34 versionCode flutterVersionCode.toInteger() versionName flutterVersionName } diff --git a/example/lib/main.dart b/example/lib/main.dart index 7ff96da8..0fc30af8 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,8 +1,8 @@ import 'dart:async'; import 'dart:developer'; import 'dart:io'; -import 'package:esc_pos_utils/esc_pos_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_esc_pos_utils/flutter_esc_pos_utils.dart'; import 'package:flutter_pos_printer_platform_image_3/flutter_pos_printer_platform_image_3.dart'; void main() { @@ -46,7 +46,8 @@ class _MyAppState extends State { _scan(); // subscription to listen change status of bluetooth connection - _subscriptionBtStatus = PrinterManager.instance.stateBluetooth.listen((status) { + _subscriptionBtStatus = + PrinterManager.instance.stateBluetooth.listen((status) { log(' ----------------- status bt $status ------------------ '); _currentStatus = status; if (status == BTStatus.connected) { @@ -62,11 +63,13 @@ class _MyAppState extends State { if (status == BTStatus.connected && pendingTask != null) { if (Platform.isAndroid) { Future.delayed(const Duration(milliseconds: 1000), () { - PrinterManager.instance.send(type: PrinterType.bluetooth, bytes: pendingTask!); + PrinterManager.instance + .send(type: PrinterType.bluetooth, bytes: pendingTask!); pendingTask = null; }); } else if (Platform.isIOS) { - PrinterManager.instance.send(type: PrinterType.bluetooth, bytes: pendingTask!); + PrinterManager.instance + .send(type: PrinterType.bluetooth, bytes: pendingTask!); pendingTask = null; } } @@ -78,7 +81,8 @@ class _MyAppState extends State { if (Platform.isAndroid) { if (status == USBStatus.connected && pendingTask != null) { Future.delayed(const Duration(milliseconds: 1000), () { - PrinterManager.instance.send(type: PrinterType.usb, bytes: pendingTask!); + PrinterManager.instance + .send(type: PrinterType.usb, bytes: pendingTask!); pendingTask = null; }); } @@ -99,7 +103,9 @@ class _MyAppState extends State { // method to scan devices according PrinterType void _scan() { devices.clear(); - _subscription = printerManager.discovery(type: defaultPrinterType, isBle: _isBle).listen((device) { + _subscription = printerManager + .discovery(type: defaultPrinterType, isBle: _isBle) + .listen((device) { devices.add(BluetoothPrinter( deviceName: device.name, address: device.address, @@ -139,8 +145,11 @@ class _MyAppState extends State { void selectDevice(BluetoothPrinter device) async { if (selectedPrinter != null) { - if ((device.address != selectedPrinter!.address) || (device.typePrinter == PrinterType.usb && selectedPrinter!.vendorId != device.vendorId)) { - await PrinterManager.instance.disconnect(type: selectedPrinter!.typePrinter); + if ((device.address != selectedPrinter!.address) || + (device.typePrinter == PrinterType.usb && + selectedPrinter!.vendorId != device.vendorId)) { + await PrinterManager.instance + .disconnect(type: selectedPrinter!.typePrinter); } } @@ -156,7 +165,8 @@ class _MyAppState extends State { // PaperSize.mm80 or PaperSize.mm58 final generator = Generator(PaperSize.mm80, profile); bytes += generator.setGlobalCodeTable('CP1252'); - bytes += generator.text('Test Print', styles: const PosStyles(align: PosAlign.center)); + bytes += generator.text('Test Print', + styles: const PosStyles(align: PosAlign.center)); bytes += generator.text('Product 1'); bytes += generator.text('Product 2'); @@ -174,7 +184,10 @@ class _MyAppState extends State { bytes += generator.cut(); await printerManager.connect( type: bluetoothPrinter.typePrinter, - model: UsbPrinterInput(name: bluetoothPrinter.deviceName, productId: bluetoothPrinter.productId, vendorId: bluetoothPrinter.vendorId)); + model: UsbPrinterInput( + name: bluetoothPrinter.deviceName, + productId: bluetoothPrinter.productId, + vendorId: bluetoothPrinter.vendorId)); pendingTask = null; break; case PrinterType.bluetooth: @@ -192,11 +205,14 @@ class _MyAppState extends State { case PrinterType.network: bytes += generator.feed(2); bytes += generator.cut(); - await printerManager.connect(type: bluetoothPrinter.typePrinter, model: TcpPrinterInput(ipAddress: bluetoothPrinter.address!)); + await printerManager.connect( + type: bluetoothPrinter.typePrinter, + model: TcpPrinterInput(ipAddress: bluetoothPrinter.address!)); break; default: } - if (bluetoothPrinter.typePrinter == PrinterType.bluetooth && Platform.isAndroid) { + if (bluetoothPrinter.typePrinter == PrinterType.bluetooth && + Platform.isAndroid) { if (_currentStatus == BTStatus.connected) { printerManager.send(type: bluetoothPrinter.typePrinter, bytes: bytes); pendingTask = null; @@ -214,7 +230,10 @@ class _MyAppState extends State { case PrinterType.usb: await printerManager.connect( type: selectedPrinter!.typePrinter, - model: UsbPrinterInput(name: selectedPrinter!.deviceName, productId: selectedPrinter!.productId, vendorId: selectedPrinter!.vendorId)); + model: UsbPrinterInput( + name: selectedPrinter!.deviceName, + productId: selectedPrinter!.productId, + vendorId: selectedPrinter!.vendorId)); _isConnected = true; break; case PrinterType.bluetooth: @@ -227,7 +246,9 @@ class _MyAppState extends State { autoConnect: _reconnect)); break; case PrinterType.network: - await printerManager.connect(type: selectedPrinter!.typePrinter, model: TcpPrinterInput(ipAddress: selectedPrinter!.address!)); + await printerManager.connect( + type: selectedPrinter!.typePrinter, + model: TcpPrinterInput(ipAddress: selectedPrinter!.address!)); _isConnected = true; break; default: @@ -262,7 +283,8 @@ class _MyAppState extends State { : () { _connectDevice(); }, - child: const Text("Connect", textAlign: TextAlign.center), + child: const Text("Connect", + textAlign: TextAlign.center), ), ), const SizedBox(width: 8), @@ -271,19 +293,22 @@ class _MyAppState extends State { onPressed: selectedPrinter == null || !_isConnected ? null : () { - if (selectedPrinter != null) printerManager.disconnect(type: selectedPrinter!.typePrinter); + if (selectedPrinter != null) + printerManager.disconnect( + type: selectedPrinter!.typePrinter); setState(() { _isConnected = false; }); }, - child: const Text("Disconnect", textAlign: TextAlign.center), + child: const Text("Disconnect", + textAlign: TextAlign.center), ), ), ], ), ), DropdownButtonFormField( - value: defaultPrinterType, + initialValue: defaultPrinterType, decoration: const InputDecoration( prefixIcon: Icon( Icons.print, @@ -325,9 +350,11 @@ class _MyAppState extends State { }, ), Visibility( - visible: defaultPrinterType == PrinterType.bluetooth && Platform.isAndroid, + visible: defaultPrinterType == PrinterType.bluetooth && + Platform.isAndroid, child: SwitchListTile.adaptive( - contentPadding: const EdgeInsets.only(bottom: 20.0, left: 20), + contentPadding: + const EdgeInsets.only(bottom: 20.0, left: 20), title: const Text( "This device supports ble (low energy)", textAlign: TextAlign.start, @@ -345,9 +372,11 @@ class _MyAppState extends State { ), ), Visibility( - visible: defaultPrinterType == PrinterType.bluetooth && Platform.isAndroid, + visible: defaultPrinterType == PrinterType.bluetooth && + Platform.isAndroid, child: SwitchListTile.adaptive( - contentPadding: const EdgeInsets.only(bottom: 20.0, left: 20), + contentPadding: + const EdgeInsets.only(bottom: 20.0, left: 20), title: const Text( "reconnect", textAlign: TextAlign.start, @@ -366,44 +395,59 @@ class _MyAppState extends State { .map( (device) => ListTile( title: Text('${device.deviceName}'), - subtitle: Platform.isAndroid && defaultPrinterType == PrinterType.usb + subtitle: Platform.isAndroid && + defaultPrinterType == PrinterType.usb ? null - : Visibility(visible: !Platform.isWindows, child: Text("${device.address}")), + : Visibility( + visible: !Platform.isWindows, + child: Text("${device.address}")), onTap: () { // do something selectDevice(device); }, leading: selectedPrinter != null && - ((device.typePrinter == PrinterType.usb && Platform.isWindows - ? device.deviceName == selectedPrinter!.deviceName - : device.vendorId != null && selectedPrinter!.vendorId == device.vendorId) || - (device.address != null && selectedPrinter!.address == device.address)) + ((device.typePrinter == PrinterType.usb && + Platform.isWindows + ? device.deviceName == + selectedPrinter!.deviceName + : device.vendorId != null && + selectedPrinter!.vendorId == + device.vendorId) || + (device.address != null && + selectedPrinter!.address == + device.address)) ? const Icon( Icons.check, color: Colors.green, ) : null, trailing: OutlinedButton( - onPressed: selectedPrinter == null || device.deviceName != selectedPrinter?.deviceName + onPressed: selectedPrinter == null || + device.deviceName != + selectedPrinter?.deviceName ? null : () async { _printReceiveTest(); }, child: const Padding( - padding: EdgeInsets.symmetric(vertical: 2, horizontal: 20), - child: Text("Print test ticket", textAlign: TextAlign.center), + padding: EdgeInsets.symmetric( + vertical: 2, horizontal: 20), + child: Text("Print test ticket", + textAlign: TextAlign.center), ), ), ), ) .toList()), Visibility( - visible: defaultPrinterType == PrinterType.network && Platform.isWindows, + visible: defaultPrinterType == PrinterType.network && + Platform.isWindows, child: Padding( padding: const EdgeInsets.only(top: 10.0), child: TextFormField( controller: _ipController, - keyboardType: const TextInputType.numberWithOptions(signed: true), + keyboardType: + const TextInputType.numberWithOptions(signed: true), decoration: const InputDecoration( label: Text("Ip Address"), prefixIcon: Icon(Icons.wifi, size: 24), @@ -413,12 +457,14 @@ class _MyAppState extends State { ), ), Visibility( - visible: defaultPrinterType == PrinterType.network && Platform.isWindows, + visible: defaultPrinterType == PrinterType.network && + Platform.isWindows, child: Padding( padding: const EdgeInsets.only(top: 10.0), child: TextFormField( controller: _portController, - keyboardType: const TextInputType.numberWithOptions(signed: true), + keyboardType: + const TextInputType.numberWithOptions(signed: true), decoration: const InputDecoration( label: Text("Port"), prefixIcon: Icon(Icons.numbers_outlined, size: 24), @@ -428,17 +474,21 @@ class _MyAppState extends State { ), ), Visibility( - visible: defaultPrinterType == PrinterType.network && Platform.isWindows, + visible: defaultPrinterType == PrinterType.network && + Platform.isWindows, child: Padding( padding: const EdgeInsets.only(top: 10.0), child: OutlinedButton( onPressed: () async { - if (_ipController.text.isNotEmpty) setIpAddress(_ipController.text); + if (_ipController.text.isNotEmpty) + setIpAddress(_ipController.text); _printReceiveTest(); }, child: const Padding( - padding: EdgeInsets.symmetric(vertical: 4, horizontal: 50), - child: Text("Print test ticket", textAlign: TextAlign.center), + padding: + EdgeInsets.symmetric(vertical: 4, horizontal: 50), + child: Text("Print test ticket", + textAlign: TextAlign.center), ), ), ), diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 79641d30..b707d275 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -30,7 +30,10 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.2 - esc_pos_utils: ^1.1.0 + flutter_esc_pos_utils: + git: + url: https://github.com/E-MRE/flutter_esc_pos_utils + ref: 1.0.0 dev_dependencies: flutter_test: diff --git a/lib/src/printers/escpos.dart b/lib/src/printers/escpos.dart index 6ad845ac..7265309a 100644 --- a/lib/src/printers/escpos.dart +++ b/lib/src/printers/escpos.dart @@ -2,10 +2,12 @@ import 'dart:typed_data'; import 'package:flutter_pos_printer_platform_image_3/printer.dart'; import 'package:flutter_pos_printer_platform_image_3/src/utils.dart'; -import 'package:image_v3/image_v3.dart'; +import 'package:image/image.dart' as img; class EscPosPrinter extends GenericPrinter { - EscPosPrinter(PrinterConnector connector, T model, {this.dpi = 200, required this.width, this.beepCount = 4}) : super(connector, model); + EscPosPrinter(PrinterConnector connector, T model, + {this.dpi = 200, required this.width, this.beepCount = 4}) + : super(connector, model); final int width; final int dpi; @@ -19,9 +21,13 @@ class EscPosPrinter extends GenericPrinter { @override Future image(Uint8List image, {int threshold = 150}) async { - final decodedImage = decodeImage(image)!; + final decodedImage = img.decodeImage(image)!; - final converted = toPixel(ImageData(width: decodedImage.width, height: decodedImage.height), paperWidth: width, dpi: dpi, isTspl: false); + final converted = toPixel( + ImageData(width: decodedImage.width, height: decodedImage.height), + paperWidth: width, + dpi: dpi, + isTspl: false); // final resizedImage = copyResize(decodedImage, width: converted.width, height: converted.height, interpolation: Interpolation.cubic); diff --git a/lib/src/printers/tspl.dart b/lib/src/printers/tspl.dart index 6869fc34..ad214169 100644 --- a/lib/src/printers/tspl.dart +++ b/lib/src/printers/tspl.dart @@ -2,7 +2,7 @@ import 'dart:core'; import 'dart:typed_data'; import 'package:flutter_pos_printer_platform_image_3/printer.dart'; -import 'package:image_v3/image_v3.dart'; +import 'package:image/image.dart' as img; import '../utils.dart'; @@ -70,11 +70,14 @@ class Command { return createLine(SHIFT, [shiftLeft, shiftTop]); } - static String imageString(String x, String y, String widthByte, String heightDot, {String mode = "0"}) { + static String imageString( + String x, String y, String widthByte, String heightDot, + {String mode = "0"}) { return createString(BITMAP, [x, y, widthByte, heightDot, mode, ""]); } - static String reverse(String x, String y, String widthByte, String heightDot) { + static String reverse( + String x, String y, String widthByte, String heightDot) { return createLine(REVERSE, [x, y, widthByte, heightDot]); } @@ -165,14 +168,18 @@ class TsplPrinter extends GenericPrinter { @override Future beep() async { return await sendToConnector(() { - return [Command.clearCache(), Command.beep(), Command.close()].join().codeUnits; + return [Command.clearCache(), Command.beep(), Command.close()] + .join() + .codeUnits; }); } @override Future selfTest() async { return await sendToConnector(() { - return [Command.clearCache(), Command.selfTest(), Command.close()].join().codeUnits; + return [Command.clearCache(), Command.selfTest(), Command.close()] + .join() + .codeUnits; }); } @@ -183,10 +190,13 @@ class TsplPrinter extends GenericPrinter { @override Future image(Uint8List image, {int threshold = 150}) async { - final decodedImage = decodeImage(image)!; + final decodedImage = img.decodeImage(image)!; final rasterizeImage = _toRaster(decodedImage, dpi: int.parse(dpi)); - final converted = toPixel(ImageData(width: decodedImage.width, height: decodedImage.height), - paperWidth: int.parse(_sizeWidth), dpi: int.parse(dpi), isTspl: true); + final converted = toPixel( + ImageData(width: decodedImage.width, height: decodedImage.height), + paperWidth: int.parse(_sizeWidth), + dpi: int.parse(dpi), + isTspl: true); final ms = 1000 + (converted.height * 0.5).toInt(); @@ -195,7 +205,10 @@ class TsplPrinter extends GenericPrinter { List buffer = []; buffer += this._config.codeUnits; buffer += Command.clearCache().codeUnits; - buffer += Command.imageString('0', '0', converted.width.toString(), converted.height.toString(), mode: '0').codeUnits; + buffer += Command.imageString('0', '0', converted.width.toString(), + converted.height.toString(), + mode: '0') + .codeUnits; buffer += rasterizeImage.data; buffer += Command.EOL_HEX; buffer += Command.printIt('1', repeat: '1').codeUnits; @@ -207,22 +220,27 @@ class TsplPrinter extends GenericPrinter { }, delayMs: ms); } - ImageRaster _toRaster(Image imgSrc, {int dpi = 200}) { + ImageRaster _toRaster(img.Image imgSrc, {int dpi = 200}) { // 200 DPI : 1 mm = 8 dots // 300 DPI : 1 mm = 12 dots // width 35mm = 280px // height 25mm = 200px final int multiplier = dpi == 200 ? 8 : 12; - final Image image = copyResize(imgSrc, - width: int.parse(this._sizeWidth) * multiplier, height: int.parse(this._sizeHeight) * multiplier, interpolation: Interpolation.linear); + final img.Image image = img.copyResize(imgSrc, + width: int.parse(this._sizeWidth) * multiplier, + height: int.parse(this._sizeHeight) * multiplier, + interpolation: img.Interpolation.linear); final int widthPx = image.width; final int heightPx = image.height; final int widthBytes = widthPx ~/ 8; // one byte is 8 bits - final List imageBytes = image.getBytes(format: Format.argb); + final List imageBytes = image.getBytes(); List monoPixel = []; for (int i = 0; i < imageBytes.length; i += 4) { - bool shouldBeWhite = imageBytes[i + 3] == 0 || (imageBytes[i] > 100 && imageBytes[i + 1] > 100 && imageBytes[i + 2] > 100); + bool shouldBeWhite = imageBytes[i + 3] == 0 || + (imageBytes[i] > 100 && + imageBytes[i + 1] > 100 && + imageBytes[i + 2] > 100); monoPixel.add(shouldBeWhite ? 1 : 0); } @@ -235,7 +253,10 @@ class TsplPrinter extends GenericPrinter { } } - return new ImageRaster(data: rasterizeImage, width: widthBytes.toString(), height: heightPx.toString()); + return new ImageRaster( + data: rasterizeImage, + width: widthBytes.toString(), + height: heightPx.toString()); } @override diff --git a/pubspec.yaml b/pubspec.yaml index 27a636b2..ff0db533 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_pos_printer_platform_image_3 description: A flutter plugin that prints esc commands to printers in different platforms such as android, ios, windows and different interfaces Bluetooth and BLE, TCP and USB -version: 1.2.4 +version: 1.2.5 homepage: https://github.com/diantahoc/flutter_pos_printer_platform environment: @@ -11,12 +11,11 @@ dependencies: flutter: sdk: flutter - # flutter_star_prnt: ^2.3.6 - enum_to_string: ^2.0.1 - image_v3: ^3.3.0 + enum_to_string: ^2.2.1 + image: ^4.5.4 network_info_plus: ^4.0.2 ping_discover_network_forked: ^0.0.1 - rxdart: ^0.27.7 + rxdart: ^0.28.0 dev_dependencies: flutter_test: From d0e7ed2b59e411809066120e396356a88005342a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emre=20G=C3=BCltekir?= Date: Fri, 15 May 2026 14:54:58 +0300 Subject: [PATCH 9/9] migrate to Kotlin DSL, AGP 8.11.1, Gradle 8.14 --- CHANGELOG.md | 7 ++ android/build.gradle | 53 --------------- android/build.gradle.kts | 65 ++++++++++++++++++ .../gradle/wrapper/gradle-wrapper.properties | 2 +- android/settings.gradle | 1 - android/settings.gradle.kts | 23 +++++++ example/android/app/build.gradle | 68 ------------------- example/android/app/build.gradle.kts | 51 ++++++++++++++ example/android/build.gradle | 31 --------- example/android/build.gradle.kts | 26 +++++++ .../gradle/wrapper/gradle-wrapper.properties | 2 +- example/android/settings.gradle | 11 --- example/android/settings.gradle.kts | 39 +++++++++++ example/pubspec.yaml | 8 +-- pubspec.yaml | 6 +- 15 files changed, 220 insertions(+), 173 deletions(-) delete mode 100644 android/build.gradle create mode 100644 android/build.gradle.kts delete mode 100644 android/settings.gradle create mode 100644 android/settings.gradle.kts delete mode 100644 example/android/app/build.gradle create mode 100644 example/android/app/build.gradle.kts delete mode 100644 example/android/build.gradle create mode 100644 example/android/build.gradle.kts delete mode 100644 example/android/settings.gradle create mode 100644 example/android/settings.gradle.kts diff --git a/CHANGELOG.md b/CHANGELOG.md index c926dcfb..da423a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.2.5 + +* Upgrade Android build system to Gradle 8.14 and AGP 8.11.1 +* Migrate Android build files from Groovy to Kotlin DSL (build.gradle.kts) +* Update Kotlin version to 2.2.20 and target Java 17 +* Migrate example app Android build files to Kotlin DSL with new Flutter Gradle integration + ## 1.2.4 * Relax rxdart version to allow library usage in FlutterFlow app builder diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index 067a9b2c..00000000 --- a/android/build.gradle +++ /dev/null @@ -1,53 +0,0 @@ -group 'com.sersoluciones.flutter_pos_printer_platform' -version '1.0-SNAPSHOT' - -buildscript { - ext.kotlin_version = '1.6.20' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -rootProject.allprojects { - repositories { - google() - mavenCentral() - } -} - -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' - -android { - compileSdkVersion 34 - - namespace 'com.sersoluciones.flutter_pos_printer_platform' - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 34 - } -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 00000000..0cf57dd1 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,65 @@ +group = "com.sersoluciones.flutter_pos_printer_platform" +version = "1.0-SNAPSHOT" + +buildscript { + val kotlinVersion = "2.2.20" + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.11.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } +} + +allprojects { + repositories { + google() + mavenCentral() + } +} + +plugins { + id("com.android.library") + id("kotlin-android") +} + +android { + namespace = "com.sersoluciones.flutter_pos_printer_platform" + + compileSdk = 36 + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + sourceSets { + getByName("main") { + java.srcDirs("src/main/kotlin") + } + getByName("test") { + java.srcDirs("src/test/kotlin") + } + } + + defaultConfig { + minSdk = 24 + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib:2.0.21") +} diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index b1159fc5..c6406bc3 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/android/settings.gradle b/android/settings.gradle deleted file mode 100644 index 057778de..00000000 --- a/android/settings.gradle +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = 'flutter_pos_printer_platform' diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts new file mode 100644 index 00000000..9e78df2d --- /dev/null +++ b/android/settings.gradle.kts @@ -0,0 +1,23 @@ +import org.gradle.api.initialization.resolve.RepositoriesMode + +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } + plugins { + id("com.android.library") version "8.11.1" + id("org.jetbrains.kotlin.android") version "2.2.20" + } +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "flutter_pos_printer_platform" diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index bead556b..00000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion flutter.compileSdkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.sersoluciones.flutter_pos_printer_platform_example" - minSdkVersion flutter.minSdkVersion - targetSdkVersion 34 - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/example/android/app/build.gradle.kts b/example/android/app/build.gradle.kts new file mode 100644 index 00000000..caf6a0db --- /dev/null +++ b/example/android/app/build.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("com.android.application") + id("kotlin-android") + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.sersoluciones.flutter_pos_printer_platform_example" + + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + + sourceSets { + getByName("main") { + java.srcDirs("src/main/kotlin") + } + } + + defaultConfig { + applicationId = "com.sersoluciones.flutter_pos_printer_platform_example" + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutter.versionCode + versionName = flutter.versionName + } + + buildTypes { + release { + signingConfig = signingConfigs.getByName("debug") + } + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index 3cdaac95..00000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.6.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/example/android/build.gradle.kts b/example/android/build.gradle.kts new file mode 100644 index 00000000..7c5636e5 --- /dev/null +++ b/example/android/build.gradle.kts @@ -0,0 +1,26 @@ +buildscript { + val kotlinVersion = "2.2.20" + repositories { + google() + mavenCentral() + } + + dependencies { + classpath("com.android.tools.build:gradle:8.11.1") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion") + } +} + +rootProject.layout.buildDirectory.set(file("../build")) + +subprojects { + layout.buildDirectory.set(rootProject.layout.buildDirectory.dir(project.name)) +} + +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties index cc5527d7..4cc8c0b0 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/example/android/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip diff --git a/example/android/settings.gradle b/example/android/settings.gradle deleted file mode 100644 index 44e62bcf..00000000 --- a/example/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/example/android/settings.gradle.kts b/example/android/settings.gradle.kts new file mode 100644 index 00000000..f59569bf --- /dev/null +++ b/example/android/settings.gradle.kts @@ -0,0 +1,39 @@ +import org.gradle.api.initialization.resolve.RepositoriesMode + +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + checkNotNull(flutterSdkPath) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + settings.extra["flutterSdkPath"] = flutterSdkPath + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.11.1" apply false + id("org.jetbrains.kotlin.android") version "2.2.20" apply false +} + +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositories { + google() + mavenCentral() + maven { url = uri("https://storage.googleapis.com/download.flutter.io") } + } +} + +rootProject.name = "flutter_pos_printer_platform_example" +include(":app") diff --git a/example/pubspec.yaml b/example/pubspec.yaml index b707d275..79228a9f 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -6,7 +6,7 @@ description: Demonstrates how to use the flutter_pos_printer_platform plugin. publish_to: 'none' # Remove this line if you wish to publish to pub.dev environment: - sdk: '>=3.1.0 <4.0.0' + sdk: '>=3.4.1 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -28,12 +28,12 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.2 + cupertino_icons: ^1.0.9 flutter_esc_pos_utils: git: url: https://github.com/E-MRE/flutter_esc_pos_utils - ref: 1.0.0 + ref: 1.0.1 dev_dependencies: flutter_test: @@ -44,7 +44,7 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^1.0.0 + flutter_lints: ^6.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/pubspec.yaml b/pubspec.yaml index ff0db533..3800c6ac 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ version: 1.2.5 homepage: https://github.com/diantahoc/flutter_pos_printer_platform environment: - sdk: '>=3.1.0 <4.0.0' + sdk: '>=3.4.1 <4.0.0' flutter: ">=1.20.0" dependencies: @@ -12,8 +12,8 @@ dependencies: sdk: flutter enum_to_string: ^2.2.1 - image: ^4.5.4 - network_info_plus: ^4.0.2 + image: ^4.8.0 + network_info_plus: ^8.1.0 ping_discover_network_forked: ^0.0.1 rxdart: ^0.28.0