From cb04e5c483a3bb91cffcb44a2776bd235d9578b2 Mon Sep 17 00:00:00 2001 From: Arash Date: Wed, 5 Nov 2025 11:58:20 +0330 Subject: [PATCH 1/2] fix: fix errors of open, read_binary, write_binary commands for android --- android/.gitignore | 3 +- .../app/tauri/serialplugin/SerialPlugin.kt | 64 +++++++++++++--- .../serialplugin/manager/SerialPortManager.kt | 70 +++++++++--------- .../serialplugin/models/SerialPortConfig.kt | 22 +++--- .../.gradle/8.3/checksums/checksums.lock | Bin 0 -> 17 bytes .../.gradle/8.3/checksums/md5-checksums.bin | Bin 0 -> 29697 bytes .../.gradle/8.3/checksums/sha1-checksums.bin | Bin 0 -> 76109 bytes .../dependencies-accessors.lock | Bin 0 -> 17 bytes .../8.3/dependencies-accessors/gc.properties | 0 .../executionHistory/executionHistory.lock | Bin 0 -> 17 bytes .../.gradle/8.3/fileChanges/last-build.bin | Bin 0 -> 1 bytes .../.gradle/8.3/fileHashes/fileHashes.lock | Bin 0 -> 17 bytes .../src-tauri/.gradle/8.3/gc.properties | 0 .../buildOutputCleanup.lock | Bin 0 -> 17 bytes .../buildOutputCleanup/cache.properties | 2 + .../buildOutputCleanup/outputFiles.bin | Bin 0 -> 18839 bytes .../src-tauri/.gradle/vcs-1/gc.properties | 0 permissions/schemas/schema.json | 4 +- src/mobile_api.rs | 55 ++++++++------ 19 files changed, 142 insertions(+), 78 deletions(-) create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/checksums/checksums.lock create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/checksums/md5-checksums.bin create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/checksums/sha1-checksums.bin create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/dependencies-accessors/dependencies-accessors.lock create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/dependencies-accessors/gc.properties create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/executionHistory/executionHistory.lock create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/fileChanges/last-build.bin create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/fileHashes/fileHashes.lock create mode 100644 examples/serialport-test/src-tauri/.gradle/8.3/gc.properties create mode 100644 examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/buildOutputCleanup.lock create mode 100644 examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/cache.properties create mode 100644 examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/outputFiles.bin create mode 100644 examples/serialport-test/src-tauri/.gradle/vcs-1/gc.properties diff --git a/android/.gitignore b/android/.gitignore index a172d9d..20a1870 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -1,2 +1,3 @@ /build -/.tauri \ No newline at end of file +/.tauri +/.gradle \ No newline at end of file diff --git a/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt b/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt index 5a63110..6c2011c 100644 --- a/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt +++ b/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt @@ -13,7 +13,45 @@ import app.tauri.serialplugin.models.* import android.webkit.WebView import android.util.Log import java.util.concurrent.ConcurrentHashMap +import app.tauri.plugin.JSArray +// --- Reused from previous answer (Converts a Map to a JSObject) --- +fun Map.toJSObject(): JSObject { + val jsObject = JSObject() + for ((key, value) in this) { + val convertedValue: Any? = when (value) { + is Map<*, *> -> @Suppress("UNCHECKED_CAST") (value as Map).toJSObject() + is List<*> -> @Suppress("UNCHECKED_CAST") value.toJSArray() // Call the new list utility + else -> value + } + jsObject.put(key, convertedValue) + } + return jsObject +} + +// --- NEW Utility: Converts a List to a JSArray --- +fun List.toJSArray(): JSArray { + val jsArray = JSArray() + + for (item in this) { + val convertedItem: Any? = when (item) { + is Map<*, *> -> { + // If the item is a Map, convert it to a JSObject + @Suppress("UNCHECKED_CAST") + (item as Map).toJSObject() + } + is List<*> -> { + // If the item is a nested List, convert it to a JSArray (for list of lists) + @Suppress("UNCHECKED_CAST") + item.toJSArray() + } + else -> item // Primitives (String, Int, Boolean, etc.) can be put directly + } + // Add the converted item to the JSArray + jsArray.put(convertedItem) + } + return jsArray +} @InvokeArg class PortConfigArgs { lateinit var path: String @@ -32,6 +70,12 @@ class WriteArgs { lateinit var value: String } +@InvokeArg +class WriteBinaryArgs { + lateinit var path: String + lateinit var value: List +} + @InvokeArg class CloseArgs { lateinit var path: String @@ -51,14 +95,14 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { Log.d("SerialPlugin", "SerialPlugin loaded successfully") } - override fun onDetach() { + override fun onDestroy() { try { Log.d("SerialPlugin", "SerialPlugin detaching, cleaning up resources") serialPortManager.cleanup() } catch (e: Exception) { Log.e("SerialPlugin", "Failed to cleanup: ${e.message}", e) } - super.onDetach() + super.onDestroy() } @Command @@ -68,7 +112,8 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { val ports = serialPortManager.getAvailablePorts() Log.d("SerialPlugin", "Available ports fetched successfully: ${ports.size} ports") val result = JSObject() - result.put("ports", ports) + + result.put("ports", ports.toJSObject()) invoke.resolve(result) } catch (e: Exception) { Log.e("SerialPlugin", "Failed to get available ports: ${e.message}", e) @@ -208,9 +253,10 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { @Command fun writeBinary(invoke: Invoke) { try { - val args = invoke.parseArgs(WriteArgs::class.java) + val args = invoke.parseArgs(WriteBinaryArgs::class.java) Log.d("SerialPlugin", "Writing binary to port: ${args.path}") - val bytesWritten = serialPortManager.writeToPort(args.path, args.value.toByteArray()) + val bytesToSend = args.value.map { it.toByte() }.toByteArray() + val bytesWritten = serialPortManager.writeToPort(args.path, bytesToSend) val result = JSObject() result.put("bytesWritten", bytesWritten) Log.d("SerialPlugin", "Binary write successful: $bytesWritten bytes written") @@ -226,7 +272,7 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { try { val args = invoke.parseArgs(PortConfigArgs::class.java) Log.d("SerialPlugin", "Reading from port: ${args.path}") - val data = serialPortManager.readFromPort(args.path, args.timeout, 1024) + val data = serialPortManager.readFromPort(args.path, args.timeout, args.size) val result = JSObject() result.put("data", String(data)) Log.d("SerialPlugin", "Read successful: ${data.size} bytes read") @@ -242,9 +288,9 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { try { val args = invoke.parseArgs(PortConfigArgs::class.java) Log.d("SerialPlugin", "Reading binary from port: ${args.path}") - val data = serialPortManager.readFullyFromPort(args.path, args.timeout, args.size) + val data = serialPortManager.readFromPort(args.path, args.timeout, args.size) val result = JSObject().apply { - put("data", data.toList()) + put("data", data.toList().map { it.toInt() and 0xFF }.toJSArray()) } Log.d("SerialPlugin", "Binary read successful: ${data.size} bytes read") invoke.resolve(result) @@ -260,7 +306,7 @@ class SerialPlugin(private val activity: Activity) : Plugin(activity) { val args = invoke.parseArgs(CloseArgs::class.java) Log.d("SerialPlugin", "Starting listening on port: ${args.path}") - val listener = { data: ByteArray -> + val listener: (ByteArray) -> Unit = { data: ByteArray -> try { val eventData = JSObject() eventData.put("path", args.path) diff --git a/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt b/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt index 047655e..6ca9299 100644 --- a/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt +++ b/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt @@ -24,9 +24,14 @@ import androidx.annotation.RequiresApi import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit +data class ManagedPort ( + val port: UsbSerialPort, + val config: SerialPortConfig +) + class SerialPortManager(private val context: Context) { private val usbManager: UsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager - private val portMap = mutableMapOf() + private val portMap = mutableMapOf() private val ioManagerMap = mutableMapOf() private val executor = Executors.newCachedThreadPool() private val permissionFutures = mutableMapOf>() @@ -228,8 +233,8 @@ class SerialPortManager(private val context: Context) { FlowControl.HARDWARE -> { Log.d("SerialPortManager", "Enabling hardware flow control") try { - port.setDTR(true) - port.setRTS(true) + port.dtr = true + port.rts = true Log.d("SerialPortManager", "Hardware flow control enabled successfully") } catch (e: UnsupportedOperationException) { Log.w("SerialPortManager", "Hardware flow control not supported by this device") @@ -245,7 +250,7 @@ class SerialPortManager(private val context: Context) { } } - portMap[config.path] = port + portMap[config.path] = ManagedPort(port, config) Log.d("SerialPortManager", "Port opened successfully: ${config.path}") return true @@ -315,14 +320,9 @@ class SerialPortManager(private val context: Context) { Log.d("SerialPortManager", "Writing to port $path: ${data.size} bytes") - val bytesWritten = port.write(data, 1000) // 1 second timeout - - if (bytesWritten > 0) { - Log.d("SerialPortManager", "Write successful: $bytesWritten bytes written") - } else { - Log.w("SerialPortManager", "Write timeout: no bytes written") - } - + port.port.write(data, 1000) // 1 second timeout + val bytesWritten = data.size + return bytesWritten } catch (e: IOException) { Log.e("SerialPortManager", "Write failed: ${e.message}") @@ -337,7 +337,7 @@ class SerialPortManager(private val context: Context) { try { ioManagerMap[path]?.stop() ioManagerMap.remove(path) - portMap[path]?.close() + portMap[path]?.port?.close() portMap.remove(path) Log.d("SerialPortManager", "Port closed: $path") } catch (e: Exception) { @@ -365,7 +365,7 @@ class SerialPortManager(private val context: Context) { fun setPortParameters(path: String, config: SerialPortConfig): Boolean { return try { portMap[path]?.let { port -> - port.setParameters( + port.port.setParameters( config.baudRate, config.dataBits.value, config.stopBits.value, @@ -384,7 +384,7 @@ class SerialPortManager(private val context: Context) { val port = portMap[path] ?: throw IOException("Port not found") val targetSize = size ?: 1024 - val maxPacketSize = port.getReadEndpoint().getMaxPacketSize() + val maxPacketSize = port.port.getReadEndpoint().getMaxPacketSize() val bufferSize = minOf(targetSize, maxPacketSize) val buffer = ByteArray(bufferSize) @@ -392,7 +392,7 @@ class SerialPortManager(private val context: Context) { Log.d("SerialPortManager", "Reading from port $path: bufferSize=$bufferSize, timeout=$adjustedTimeout") - val bytesRead = port.read(buffer, adjustedTimeout) + val bytesRead = port.port.read(buffer, adjustedTimeout) if (bytesRead > 0) { Log.d("SerialPortManager", "Read successful: $bytesRead bytes") @@ -416,7 +416,7 @@ class SerialPortManager(private val context: Context) { val startTime = System.currentTimeMillis() val targetSize = size ?: 1024 - val maxPacketSize = port.getReadEndpoint().getMaxPacketSize() + val maxPacketSize = port.port.getReadEndpoint().getMaxPacketSize() while (buffer.size < targetSize && (System.currentTimeMillis() - startTime) < timeout) { val remainingTime = timeout - (System.currentTimeMillis() - startTime).toInt() @@ -424,7 +424,7 @@ class SerialPortManager(private val context: Context) { val chunkSize = minOf(targetSize - buffer.size, maxPacketSize) val tempBuffer = ByteArray(chunkSize) - val bytesRead = port.read(tempBuffer, remainingTime.coerceAtLeast(200)) + val bytesRead = port.port.read(tempBuffer, remainingTime.coerceAtLeast(200)) if (bytesRead > 0) { buffer.addAll(tempBuffer.copyOf(bytesRead).toList()) @@ -443,7 +443,8 @@ class SerialPortManager(private val context: Context) { fun setBaudRate(path: String, baudRate: Int): Boolean { return try { val port = portMap[path] ?: return false - port.setParameters(baudRate, port.dataBits, port.stopBits, port.parity) + port.config.baudRate = baudRate; + port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set baud rate: ${e.message}", e) @@ -454,7 +455,8 @@ class SerialPortManager(private val context: Context) { fun setDataBits(path: String, dataBits: DataBits): Boolean { return try { val port = portMap[path] ?: return false - port.setParameters(port.baudRate, dataBits.value, port.stopBits, port.parity) + port.config.dataBits = dataBits; + port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set data bits: ${e.message}", e) @@ -466,8 +468,8 @@ class SerialPortManager(private val context: Context) { return try { when (flowControl) { FlowControl.HARDWARE -> { - portMap[path]?.setDTR(true) - portMap[path]?.setRTS(true) + portMap[path]?.port?.dtr = true + portMap[path]?.port?.rts = true } FlowControl.SOFTWARE -> { // Software flow control implementation @@ -484,7 +486,8 @@ class SerialPortManager(private val context: Context) { fun setParity(path: String, parity: Parity): Boolean { return try { val port = portMap[path] ?: return false - port.setParameters(port.baudRate, port.dataBits, port.stopBits, parity.value) + port.config.parity = parity; + port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set parity: ${e.message}", e) @@ -495,7 +498,8 @@ class SerialPortManager(private val context: Context) { fun setStopBits(path: String, stopBits: StopBits): Boolean { return try { val port = portMap[path] ?: return false - port.setParameters(port.baudRate, port.dataBits, stopBits.value, port.parity) + port.config.stopBits = stopBits; + port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set stop bits: ${e.message}", e) @@ -517,7 +521,7 @@ class SerialPortManager(private val context: Context) { fun writeRequestToSend(path: String, level: Boolean): Boolean { return try { - portMap[path]?.setRTS(level) + portMap[path]?.port?.rts = level true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set RTS: ${e.message}", e) @@ -527,7 +531,7 @@ class SerialPortManager(private val context: Context) { fun writeDataTerminalReady(path: String, level: Boolean): Boolean { return try { - portMap[path]?.setDTR(level) + portMap[path]?.port?.dtr = level true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set DTR: ${e.message}", e) @@ -537,7 +541,7 @@ class SerialPortManager(private val context: Context) { fun readClearToSend(path: String): Boolean { return try { - portMap[path]?.getCTS() ?: false + portMap[path]?.port?.cts ?: false } catch (e: Exception) { Log.e("SerialPortManager", "Failed to read CTS: ${e.message}", e) false @@ -546,7 +550,7 @@ class SerialPortManager(private val context: Context) { fun readDataSetReady(path: String): Boolean { return try { - portMap[path]?.getDSR() ?: false + portMap[path]?.port?.dsr ?: false } catch (e: Exception) { Log.e("SerialPortManager", "Failed to read DSR: ${e.message}", e) false @@ -555,7 +559,7 @@ class SerialPortManager(private val context: Context) { fun readRingIndicator(path: String): Boolean { return try { - portMap[path]?.getRI() ?: false + portMap[path]?.port?.ri ?: false } catch (e: Exception) { Log.e("SerialPortManager", "Failed to read RI: ${e.message}", e) false @@ -564,7 +568,7 @@ class SerialPortManager(private val context: Context) { fun readCarrierDetect(path: String): Boolean { return try { - portMap[path]?.getCD() ?: false + portMap[path]?.port?.cd ?: false } catch (e: Exception) { Log.e("SerialPortManager", "Failed to read CD: ${e.message}", e) false @@ -607,7 +611,7 @@ class SerialPortManager(private val context: Context) { fun setBreak(path: String): Boolean { return try { - portMap[path]?.setBreak(true) + portMap[path]?.port?.setBreak(true) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to set break: ${e.message}", e) @@ -617,7 +621,7 @@ class SerialPortManager(private val context: Context) { fun clearBreak(path: String): Boolean { return try { - portMap[path]?.setBreak(false) + portMap[path]?.port?.setBreak(false) true } catch (e: Exception) { Log.e("SerialPortManager", "Failed to clear break: ${e.message}", e) @@ -627,7 +631,7 @@ class SerialPortManager(private val context: Context) { fun startListening(path: String, onDataReceived: (ByteArray) -> Unit) { val port = portMap[path] ?: throw IOException("Port not found") - startIoManager(path, port, onDataReceived) + startIoManager(path, port.port, onDataReceived) } fun stopListening(path: String) { diff --git a/android/src/main/kotlin/app/tauri/serialplugin/models/SerialPortConfig.kt b/android/src/main/kotlin/app/tauri/serialplugin/models/SerialPortConfig.kt index b4bee7e..b1b8927 100644 --- a/android/src/main/kotlin/app/tauri/serialplugin/models/SerialPortConfig.kt +++ b/android/src/main/kotlin/app/tauri/serialplugin/models/SerialPortConfig.kt @@ -10,7 +10,7 @@ enum class DataBits(val value: Int) { companion object { fun fromValue(value: Int): DataBits { - return values().find { it.value == value } ?: EIGHT + return DataBits.entries.find { it.value == value } ?: EIGHT } } } @@ -38,7 +38,7 @@ enum class Parity(val value: Int) { companion object { fun fromValue(value: Int): Parity { - return values().find { it.value == value } ?: NONE + return Parity.entries.find { it.value == value } ?: NONE } } } @@ -49,7 +49,7 @@ enum class StopBits(val value: Int) { companion object { fun fromValue(value: Int): StopBits { - return values().find { it.value == value } ?: ONE + return StopBits.entries.find { it.value == value } ?: ONE } } } @@ -72,11 +72,11 @@ enum class ClearBuffer { } data class SerialPortConfig( - val path: String, - val baudRate: Int = 9600, - val dataBits: DataBits = DataBits.EIGHT, - val flowControl: FlowControl = FlowControl.NONE, - val parity: Parity = Parity.NONE, - val stopBits: StopBits = StopBits.ONE, - val timeout: Int = 1000 -) \ No newline at end of file + var path: String, + var baudRate: Int = 9600, + var dataBits: DataBits = DataBits.EIGHT, + var flowControl: FlowControl = FlowControl.NONE, + var parity: Parity = Parity.NONE, + var stopBits: StopBits = StopBits.ONE, + var timeout: Int = 1000 +) diff --git a/examples/serialport-test/src-tauri/.gradle/8.3/checksums/checksums.lock b/examples/serialport-test/src-tauri/.gradle/8.3/checksums/checksums.lock new file mode 100644 index 0000000000000000000000000000000000000000..b0085acbbb7c881d473d767fa7c477ddc263958c GIT binary patch literal 17 UcmZR!Z~Y@X$J*PB0SuTu0V+HLg#Z8m literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/8.3/checksums/md5-checksums.bin b/examples/serialport-test/src-tauri/.gradle/8.3/checksums/md5-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..5b26e4158198ca38d26f4585784de5ca787ba609 GIT binary patch literal 29697 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 literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/8.3/checksums/sha1-checksums.bin b/examples/serialport-test/src-tauri/.gradle/8.3/checksums/sha1-checksums.bin new file mode 100644 index 0000000000000000000000000000000000000000..8664663faa3002fea5434b0dd03c6c647d48608e GIT binary patch literal 76109 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 literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/8.3/dependencies-accessors/dependencies-accessors.lock b/examples/serialport-test/src-tauri/.gradle/8.3/dependencies-accessors/dependencies-accessors.lock new file mode 100644 index 0000000000000000000000000000000000000000..0a073a531bdf1eccb79ed7e420e18624193ab7f1 GIT binary patch literal 17 TcmZR+^0F}epWr0d7@z3{U_7Kb{2b literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/8.3/gc.properties b/examples/serialport-test/src-tauri/.gradle/8.3/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/buildOutputCleanup.lock new file mode 100644 index 0000000000000000000000000000000000000000..a1d5bd80d527bdfbcfb95adbaa75b64616a80182 GIT binary patch literal 17 UcmZQ>)4KSEeYb=F0|bZx04#R{aR2}S literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/cache.properties b/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/cache.properties new file mode 100644 index 0000000..988c881 --- /dev/null +++ b/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/cache.properties @@ -0,0 +1,2 @@ +#Tue Nov 04 12:15:38 IRST 2025 +gradle.version=8.3 diff --git a/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/outputFiles.bin b/examples/serialport-test/src-tauri/.gradle/buildOutputCleanup/outputFiles.bin new file mode 100644 index 0000000000000000000000000000000000000000..4aeeda6f291fe8a1aaece81eb5c0ce97e55574bc GIT binary patch literal 18839 zcmeI%Pe_w-7{Kx0hS1DENg=Ei)DBTp%FxSj4Ge->ha9OL3W5a%GD)yPP{vLj1_~Vl zArTo1R);a7TZjH!I#gQ@1GS6dz`;5sq?L!TckX!<*=0u=-xuC@Kfdp~=iTpf-6dn3 zEn9M!-J^AP$qE4k5I_I{1Q0*~0R#|0009ILKmY**5I_I{1ZqW~-`dE*@@Aap?WM(` z5o0`!*5b%a`bKi-Nxi>(`$|mzKlt$bqhsDP=PmcZ1MOd;9bIGpGEwR0v}Y!t^~7TF zA(_*weeTq~?847q+ix`{@L2n|W8)`_1K+!4&Qa}!-A%Ch~g|A+YtNdN!< literal 0 HcmV?d00001 diff --git a/examples/serialport-test/src-tauri/.gradle/vcs-1/gc.properties b/examples/serialport-test/src-tauri/.gradle/vcs-1/gc.properties new file mode 100644 index 0000000..e69de29 diff --git a/permissions/schemas/schema.json b/permissions/schemas/schema.json index cd3a456..293a7bc 100644 --- a/permissions/schemas/schema.json +++ b/permissions/schemas/schema.json @@ -775,10 +775,10 @@ "markdownDescription": "Denies the write_rts command without any pre-configured scope." }, { - "description": "# Tauri `serialport` default permissions\n\nThis configuration file defines the default permissions granted\nto the serialport.\n\n### Granted Permissions\n\nThis default permission set enables all read-related commands and\nallows access to the `$APP` folder and sub directories created in it.\nThe location of the `$APP` folder depends on the operating system,\nwhere the application is run.\n\nIn general the `$APP` folder needs to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\n### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n\n#### This default permission set includes:\n\n- `allow-managed-ports`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-cancel-read`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-open`\n- `allow-read`\n- `allow-write`\n- `allow-write-binary`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-bytes-to-read`\n- `allow-bytes-to-write`\n- `allow-cancel-read`\n- `allow-clear-break`\n- `allow-clear-buffer`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-managed-ports`\n- `allow-open`\n- `allow-read`\n- `allow-read-binary`\n- `allow-read-carrier-detect`\n- `allow-read-cd`\n- `allow-read-clear-to-send`\n- `allow-read-cts`\n- `allow-read-data-set-ready`\n- `allow-read-dsr`\n- `allow-read-dtr`\n- `allow-read-ri`\n- `allow-read-ring-indicator`\n- `allow-set-baud-rate`\n- `allow-set-break`\n- `allow-set-data-bits`\n- `allow-set-flow-control`\n- `allow-set-parity`\n- `allow-set-stop-bits`\n- `allow-set-timeout`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-write`\n- `allow-write-binary`\n- `allow-write-data-terminal-ready`\n- `allow-write-dtr`\n- `allow-write-request-to-send`\n- `allow-write-rts`\n- `allow-set-log-level`\n- `allow-get-log-level`", + "description": "# Tauri `serialport` default permissions\r\n\r\nThis configuration file defines the default permissions granted\r\nto the serialport.\r\n\r\n### Granted Permissions\r\n\r\nThis default permission set enables all read-related commands and\r\nallows access to the `$APP` folder and sub directories created in it.\r\nThe location of the `$APP` folder depends on the operating system,\r\nwhere the application is run.\r\n\r\nIn general the `$APP` folder needs to be manually created\r\nby the application at runtime, before accessing files or folders\r\nin it is possible.\r\n\r\n### Denied Permissions\r\n\r\nThis default permission set prevents access to critical components\r\nof the Tauri application by default.\r\nOn Windows the webview data folder access is denied.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-managed-ports`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-cancel-read`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-open`\n- `allow-read`\n- `allow-write`\n- `allow-write-binary`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-bytes-to-read`\n- `allow-bytes-to-write`\n- `allow-cancel-read`\n- `allow-clear-break`\n- `allow-clear-buffer`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-managed-ports`\n- `allow-open`\n- `allow-read`\n- `allow-read-binary`\n- `allow-read-carrier-detect`\n- `allow-read-cd`\n- `allow-read-clear-to-send`\n- `allow-read-cts`\n- `allow-read-data-set-ready`\n- `allow-read-dsr`\n- `allow-read-dtr`\n- `allow-read-ri`\n- `allow-read-ring-indicator`\n- `allow-set-baud-rate`\n- `allow-set-break`\n- `allow-set-data-bits`\n- `allow-set-flow-control`\n- `allow-set-parity`\n- `allow-set-stop-bits`\n- `allow-set-timeout`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-write`\n- `allow-write-binary`\n- `allow-write-data-terminal-ready`\n- `allow-write-dtr`\n- `allow-write-request-to-send`\n- `allow-write-rts`\n- `allow-set-log-level`\n- `allow-get-log-level`", "type": "string", "const": "default", - "markdownDescription": "# Tauri `serialport` default permissions\n\nThis configuration file defines the default permissions granted\nto the serialport.\n\n### Granted Permissions\n\nThis default permission set enables all read-related commands and\nallows access to the `$APP` folder and sub directories created in it.\nThe location of the `$APP` folder depends on the operating system,\nwhere the application is run.\n\nIn general the `$APP` folder needs to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\n### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n\n#### This default permission set includes:\n\n- `allow-managed-ports`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-cancel-read`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-open`\n- `allow-read`\n- `allow-write`\n- `allow-write-binary`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-bytes-to-read`\n- `allow-bytes-to-write`\n- `allow-cancel-read`\n- `allow-clear-break`\n- `allow-clear-buffer`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-managed-ports`\n- `allow-open`\n- `allow-read`\n- `allow-read-binary`\n- `allow-read-carrier-detect`\n- `allow-read-cd`\n- `allow-read-clear-to-send`\n- `allow-read-cts`\n- `allow-read-data-set-ready`\n- `allow-read-dsr`\n- `allow-read-dtr`\n- `allow-read-ri`\n- `allow-read-ring-indicator`\n- `allow-set-baud-rate`\n- `allow-set-break`\n- `allow-set-data-bits`\n- `allow-set-flow-control`\n- `allow-set-parity`\n- `allow-set-stop-bits`\n- `allow-set-timeout`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-write`\n- `allow-write-binary`\n- `allow-write-data-terminal-ready`\n- `allow-write-dtr`\n- `allow-write-request-to-send`\n- `allow-write-rts`\n- `allow-set-log-level`\n- `allow-get-log-level`" + "markdownDescription": "# Tauri `serialport` default permissions\r\n\r\nThis configuration file defines the default permissions granted\r\nto the serialport.\r\n\r\n### Granted Permissions\r\n\r\nThis default permission set enables all read-related commands and\r\nallows access to the `$APP` folder and sub directories created in it.\r\nThe location of the `$APP` folder depends on the operating system,\r\nwhere the application is run.\r\n\r\nIn general the `$APP` folder needs to be manually created\r\nby the application at runtime, before accessing files or folders\r\nin it is possible.\r\n\r\n### Denied Permissions\r\n\r\nThis default permission set prevents access to critical components\r\nof the Tauri application by default.\r\nOn Windows the webview data folder access is denied.\r\n\r\n\n#### This default permission set includes:\n\n- `allow-managed-ports`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-cancel-read`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-open`\n- `allow-read`\n- `allow-write`\n- `allow-write-binary`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-available-ports`\n- `allow-available-ports-direct`\n- `allow-bytes-to-read`\n- `allow-bytes-to-write`\n- `allow-cancel-read`\n- `allow-clear-break`\n- `allow-clear-buffer`\n- `allow-close`\n- `allow-close-all`\n- `allow-force-close`\n- `allow-managed-ports`\n- `allow-open`\n- `allow-read`\n- `allow-read-binary`\n- `allow-read-carrier-detect`\n- `allow-read-cd`\n- `allow-read-clear-to-send`\n- `allow-read-cts`\n- `allow-read-data-set-ready`\n- `allow-read-dsr`\n- `allow-read-dtr`\n- `allow-read-ri`\n- `allow-read-ring-indicator`\n- `allow-set-baud-rate`\n- `allow-set-break`\n- `allow-set-data-bits`\n- `allow-set-flow-control`\n- `allow-set-parity`\n- `allow-set-stop-bits`\n- `allow-set-timeout`\n- `allow-start-listening`\n- `allow-stop-listening`\n- `allow-write`\n- `allow-write-binary`\n- `allow-write-data-terminal-ready`\n- `allow-write-dtr`\n- `allow-write-request-to-send`\n- `allow-write-rts`\n- `allow-set-log-level`\n- `allow-get-log-level`" } ] } diff --git a/src/mobile_api.rs b/src/mobile_api.rs index 335054a..b7cba9d 100644 --- a/src/mobile_api.rs +++ b/src/mobile_api.rs @@ -33,6 +33,24 @@ struct AvailablePortsResponse { ports: HashMap, } +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct WriteResponse { + bytes_written: usize, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ReadBinaryResponse { + data: Vec, +} + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +struct ReadResponse { + data: String, +} + impl SerialPort { /// Lists all available serial ports pub fn available_ports(&self) -> Result>, Error> { @@ -166,8 +184,8 @@ impl SerialPort { "value": data, }); - match self.0.run_mobile_plugin("write", params) { - Ok(Value::Number(n)) => Ok(n.as_u64().unwrap_or(0) as usize), + match self.0.run_mobile_plugin::("write", params) { + Ok(WriteResponse { bytes_written }) => Ok(bytes_written), Ok(_) => Err(Error::new("Invalid response format")), Err(e) => Err(Error::new(format!("Plugin error: {}", e))), } @@ -180,8 +198,11 @@ impl SerialPort { "value": data, }); - match self.0.run_mobile_plugin("writeBinary", params) { - Ok(Value::Number(n)) => Ok(n.as_u64().unwrap_or(0) as usize), + match self + .0 + .run_mobile_plugin::("writeBinary", params) + { + Ok(WriteResponse { bytes_written }) => Ok(bytes_written), Ok(_) => Err(Error::new("Invalid response format")), Err(e) => Err(Error::new(format!("Plugin error: {}", e))), } @@ -201,7 +222,7 @@ impl SerialPort { }); match self.0.run_mobile_plugin("read", params) { - Ok(Value::String(data)) => Ok(data), + Ok(ReadResponse { data }) => Ok(data), Ok(_) => Err(Error::new("Invalid response format")), Err(e) => Err(Error::new(format!("Plugin error: {}", e))), } @@ -215,23 +236,13 @@ impl SerialPort { size: Option, ) -> Result, Error> { let params = serde_json::json!({ - "path": path, - "timeout": timeout.unwrap_or(1000), - "size": size.unwrap_or(1024), - }); - - match self.0.run_mobile_plugin("read_binary", params) { - Ok(Value::Array(bytes)) => { - let mut result = Vec::with_capacity(bytes.len()); - for byte in bytes { - if let Some(n) = byte.as_u64() { - result.push(n as u8); - } else { - return Err(Error::new("Invalid byte format")); - } - } - Ok(result) - } + "path": path, + "timeout": timeout.unwrap_or(1000), + "size": size.unwrap_or(1024), + }); + + match self.0.run_mobile_plugin("readBinary", params) { + Ok(ReadBinaryResponse { data }) => Ok(data), Ok(_) => Err(Error::new("Invalid response format")), Err(e) => Err(Error::new(format!("Plugin error: {}", e))), } From 7ce3811516ef2312913ca2a9e81b2d4114174ace Mon Sep 17 00:00:00 2001 From: Arash Date: Wed, 5 Nov 2025 12:07:33 +0330 Subject: [PATCH 2/2] fix: fix warnings --- .../app/tauri/serialplugin/SerialPlugin.kt | 3 +- .../serialplugin/manager/SerialPortManager.kt | 48 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt b/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt index 6c2011c..ff1a37c 100644 --- a/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt +++ b/android/src/main/kotlin/app/tauri/serialplugin/SerialPlugin.kt @@ -1,7 +1,8 @@ +@file:Suppress("unused") + package app.tauri.serialplugin import android.app.Activity -import android.content.Context import app.tauri.annotation.Command import app.tauri.annotation.InvokeArg import app.tauri.annotation.TauriPlugin diff --git a/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt b/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt index 6ca9299..31ea9a6 100644 --- a/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt +++ b/android/src/main/kotlin/app/tauri/serialplugin/manager/SerialPortManager.kt @@ -12,15 +12,11 @@ import com.hoho.android.usbserial.driver.UsbSerialPort import com.hoho.android.usbserial.driver.UsbSerialProber import com.hoho.android.usbserial.util.SerialInputOutputManager import com.hoho.android.usbserial.driver.ProbeTable -import com.hoho.android.usbserial.driver.FtdiSerialDriver -import com.hoho.android.usbserial.driver.Cp21xxSerialDriver -import com.hoho.android.usbserial.driver.Ch34xSerialDriver -import com.hoho.android.usbserial.driver.ProlificSerialDriver import app.tauri.serialplugin.models.* import java.util.concurrent.Executors import java.io.IOException import android.util.Log -import androidx.annotation.RequiresApi +import androidx.core.content.ContextCompat import java.util.concurrent.CompletableFuture import java.util.concurrent.TimeUnit @@ -80,14 +76,19 @@ class SerialPortManager(private val context: Context) { if (Build.VERSION.SDK_INT >= 33) { context.registerReceiver(usbReceiver, filter, Context.RECEIVER_EXPORTED) } else { - context.registerReceiver(usbReceiver, filter) + ContextCompat.registerReceiver( + context, + usbReceiver, + filter, + ContextCompat.RECEIVER_NOT_EXPORTED + ) } } fun unregisterReceiver() { try { context.unregisterReceiver(usbReceiver) - } catch (e: IllegalArgumentException) { + } catch (_: IllegalArgumentException) { Log.w("SerialPortManager", "Receiver not registered") } } @@ -180,12 +181,9 @@ class SerialPortManager(private val context: Context) { val permissionFuture = CompletableFuture() permissionFutures[device.deviceName] = permissionFuture - val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + val flags = PendingIntent.FLAG_IMMUTABLE - } else { - 0 - } - + val permissionIntent = PendingIntent.getBroadcast( context, 0, @@ -220,7 +218,7 @@ class SerialPortManager(private val context: Context) { config.parity.value ) Log.d("SerialPortManager", "Port parameters set successfully") - } catch (e: UnsupportedOperationException) { + } catch (_: UnsupportedOperationException) { Log.w("SerialPortManager", "setParameters not supported for this device, using default settings") // Some devices don't support parameter changes, continue with defaults } catch (e: Exception) { @@ -236,7 +234,7 @@ class SerialPortManager(private val context: Context) { port.dtr = true port.rts = true Log.d("SerialPortManager", "Hardware flow control enabled successfully") - } catch (e: UnsupportedOperationException) { + } catch (_: UnsupportedOperationException) { Log.w("SerialPortManager", "Hardware flow control not supported by this device") } catch (e: Exception) { Log.w("SerialPortManager", "Failed to enable hardware flow control: ${e.message}") @@ -299,15 +297,19 @@ class SerialPortManager(private val context: Context) { ioManagerMap[path] = ioManager try { - executor.submit(Runnable { + executor.submit { try { ioManager.start() Log.d("SerialPortManager", "IO Manager started successfully for $path") } catch (e: Exception) { - Log.e("SerialPortManager", "Failed to start IO Manager for $path: ${e.message}", e) + Log.e( + "SerialPortManager", + "Failed to start IO Manager for $path: ${e.message}", + e + ) closePort(path) } - }) + } } catch (e: Exception) { Log.e("SerialPortManager", "Failed to submit IO Manager task for $path: ${e.message}", e) closePort(path) @@ -384,7 +386,7 @@ class SerialPortManager(private val context: Context) { val port = portMap[path] ?: throw IOException("Port not found") val targetSize = size ?: 1024 - val maxPacketSize = port.port.getReadEndpoint().getMaxPacketSize() + val maxPacketSize = port.port.readEndpoint.maxPacketSize val bufferSize = minOf(targetSize, maxPacketSize) val buffer = ByteArray(bufferSize) @@ -416,7 +418,7 @@ class SerialPortManager(private val context: Context) { val startTime = System.currentTimeMillis() val targetSize = size ?: 1024 - val maxPacketSize = port.port.getReadEndpoint().getMaxPacketSize() + val maxPacketSize = port.port.readEndpoint.maxPacketSize while (buffer.size < targetSize && (System.currentTimeMillis() - startTime) < timeout) { val remainingTime = timeout - (System.currentTimeMillis() - startTime).toInt() @@ -443,7 +445,7 @@ class SerialPortManager(private val context: Context) { fun setBaudRate(path: String, baudRate: Int): Boolean { return try { val port = portMap[path] ?: return false - port.config.baudRate = baudRate; + port.config.baudRate = baudRate port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { @@ -455,7 +457,7 @@ class SerialPortManager(private val context: Context) { fun setDataBits(path: String, dataBits: DataBits): Boolean { return try { val port = portMap[path] ?: return false - port.config.dataBits = dataBits; + port.config.dataBits = dataBits port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { @@ -486,7 +488,7 @@ class SerialPortManager(private val context: Context) { fun setParity(path: String, parity: Parity): Boolean { return try { val port = portMap[path] ?: return false - port.config.parity = parity; + port.config.parity = parity port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) { @@ -498,7 +500,7 @@ class SerialPortManager(private val context: Context) { fun setStopBits(path: String, stopBits: StopBits): Boolean { return try { val port = portMap[path] ?: return false - port.config.stopBits = stopBits; + port.config.stopBits = stopBits port.port.setParameters(port.config.baudRate, port.config.dataBits.value, port.config.stopBits.value, port.config.parity.value) true } catch (e: Exception) {