From 3f16594c48571355779edd86b791ad18edeaeaf4 Mon Sep 17 00:00:00 2001 From: Aswanidev Date: Sat, 8 Aug 2026 12:00:45 +0530 Subject: [PATCH] fix: harden Android completion and responsive UI --- .../main/java/com/wails/app/MainActivity.java | 27 +++- frontend/src/components/common/Toast.vue | 4 +- frontend/src/components/layout/AppLayout.vue | 51 ++++--- frontend/src/components/pair/PairModal.vue | 4 +- .../components/receive/IncomingRequest.vue | 4 +- frontend/src/components/transfer/FileRow.vue | 8 +- .../src/components/transfer/TransferArea.vue | 10 +- frontend/src/styles/index.css | 39 ++++- frontend/src/views/ReceiveView.vue | 12 +- frontend/src/views/SendView.vue | 8 +- frontend/src/views/SettingsView.vue | 12 +- .../light/filetransfer_integration_test.go | 141 ++++++++++++++++++ 12 files changed, 261 insertions(+), 59 deletions(-) create mode 100644 internal/light/filetransfer_integration_test.go diff --git a/build/android/app/src/main/java/com/wails/app/MainActivity.java b/build/android/app/src/main/java/com/wails/app/MainActivity.java index b23ef93..e3f0e09 100644 --- a/build/android/app/src/main/java/com/wails/app/MainActivity.java +++ b/build/android/app/src/main/java/com/wails/app/MainActivity.java @@ -436,23 +436,37 @@ public void copyToFolder(final String json) { java.io.File source = new java.io.File(o.getString("sourcePath")); if (!source.exists()) { bridge.emitEvent("android:copyDone", - "{\"ok\":false,\"error\":\"staging file missing\"}"); + "{\"ok\":false,\"error\":" + JSONObject.quote("staging file missing") + "}"); return; } + if (!DocumentsContract.isTreeUri(treeUri)) { + throw new IllegalArgumentException("Invalid folder URI"); + } + + // createDocument expects a document URI. The folder picker + // returns a tree URI, so first resolve that tree's root + // document URI before asking the provider to create a file. + String treeDocumentId = DocumentsContract.getTreeDocumentId(treeUri); + Uri parentDocumentUri = DocumentsContract.buildDocumentUriUsingTree( + treeUri, treeDocumentId); + // Create the destination document in the chosen folder. String mime = android.webkit.MimeTypeMap.getSingleton() .getMimeTypeFromExtension(fileName.contains(".") ? fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase() : ""); Uri docUri = DocumentsContract.createDocument(getContentResolver(), - treeUri, mime != null ? mime : "application/octet-stream", fileName); + parentDocumentUri, mime != null ? mime : "application/octet-stream", fileName); + if (docUri == null) { + throw new IllegalStateException("could not create destination file"); + } try (InputStream in = new FileInputStream(source); OutputStream out = getContentResolver().openOutputStream(docUri)) { if (out == null) { bridge.emitEvent("android:copyDone", - "{\"ok\":false,\"error\":\"cannot open destination\"}"); + "{\"ok\":false,\"error\":" + JSONObject.quote("cannot open destination") + "}"); return; } byte[] buf = new byte[64 * 1024]; @@ -464,12 +478,13 @@ public void copyToFolder(final String json) { // Staging copy is no longer needed. boolean deleted = source.delete(); bridge.emitEvent("android:copyDone", - "{\"ok\":true,\"fileName\":\"" + fileName.replace("\"", "\\\"") - + "\",\"deleted\":" + deleted + "}"); + "{\"ok\":true,\"fileName\":" + JSONObject.quote(fileName) + + ",\"deleted\":" + deleted + "}"); } catch (Exception e) { Log.e(TAG, "copyToFolder failed", e); bridge.emitEvent("android:copyDone", - "{\"ok\":false,\"error\":\"" + e.getMessage() + "\"}"); + "{\"ok\":false,\"error\":" + + JSONObject.quote(e.getMessage() != null ? e.getMessage() : "copy failed") + "}"); } }).start(); } diff --git a/frontend/src/components/common/Toast.vue b/frontend/src/components/common/Toast.vue index 692767d..36f3afc 100644 --- a/frontend/src/components/common/Toast.vue +++ b/frontend/src/components/common/Toast.vue @@ -6,12 +6,12 @@ const { toasts } = useUI()