From d34c7c38065e0fa1ab7f0459e240520f06cc11bd Mon Sep 17 00:00:00 2001 From: nyne Date: Wed, 13 Nov 2024 18:55:25 +0800 Subject: [PATCH 1/2] fix importing data on Android --- .../com/github/wgh136/venera/MainActivity.kt | 51 ++++++++++++++++++- lib/utils/file_type.dart | 10 +++- lib/utils/io.dart | 34 ++++++++++--- 3 files changed, 85 insertions(+), 10 deletions(-) diff --git a/android/app/src/main/kotlin/com/github/wgh136/venera/MainActivity.kt b/android/app/src/main/kotlin/com/github/wgh136/venera/MainActivity.kt index ee6c6ac..b72653a 100644 --- a/android/app/src/main/kotlin/com/github/wgh136/venera/MainActivity.kt +++ b/android/app/src/main/kotlin/com/github/wgh136/venera/MainActivity.kt @@ -10,7 +10,6 @@ import android.view.KeyEvent import android.Manifest import android.os.Environment import android.provider.Settings -import androidx.annotation.RequiresApi import androidx.core.app.ActivityCompat import androidx.core.content.ContextCompat import androidx.documentfile.provider.DocumentFile @@ -34,6 +33,8 @@ class MainActivity : FlutterActivity() { private val storageRequestCode = 0x10 private var storagePermissionRequest: ((Boolean) -> Unit)? = null + private val selectFileCode = 0x11 + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == pickDirectoryCode) { @@ -59,6 +60,41 @@ class MainActivity : FlutterActivity() { storagePermissionRequest?.invoke(Environment.isExternalStorageManager()) } storagePermissionRequest = null + } else if (requestCode == selectFileCode) { + if (resultCode != Activity.RESULT_OK) { + result.success(null) + return + } + val uri = data?.data + if (uri == null) { + result.success(null) + return + } + val contentResolver = context.contentResolver + val file = DocumentFile.fromSingleUri(context, uri) + if (file == null) { + result.success(null) + return + } + val fileName = file.name + if (fileName == null) { + result.success(null) + return + } + // copy file to cache directory + val cacheDir = context.cacheDir + val newFile = File(cacheDir, fileName) + val inputStream = contentResolver.openInputStream(uri) + if (inputStream == null) { + result.success(null) + return + } + val outputStream = FileOutputStream(newFile) + inputStream.copyTo(outputStream) + inputStream.close() + outputStream.close() + // send file path to flutter + result.success(newFile.absolutePath) } } @@ -112,6 +148,12 @@ class MainActivity : FlutterActivity() { res.success(result) } } + + val selectFileChannel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "venera/select_file") + selectFileChannel.setMethodCallHandler { _, res -> + openFile() + result = res + } } private fun getProxy(): String { @@ -223,6 +265,13 @@ class MainActivity : FlutterActivity() { storagePermissionRequest = null } } + + fun openFile() { + val intent = Intent(Intent.ACTION_OPEN_DOCUMENT) + intent.addCategory(Intent.CATEGORY_OPENABLE) + intent.type = "*/*" + startActivityForResult(intent, selectFileCode) + } } class VolumeListen{ diff --git a/lib/utils/file_type.dart b/lib/utils/file_type.dart index 124c9be..72cdba4 100644 --- a/lib/utils/file_type.dart +++ b/lib/utils/file_type.dart @@ -1,5 +1,3 @@ -import 'dart:typed_data'; - import 'package:mime/mime.dart'; class FileType { @@ -7,6 +5,14 @@ class FileType { final String mime; const FileType(this.ext, this.mime); + + static FileType fromExtension(String ext) { + if(ext.startsWith('.')) { + ext = ext.substring(1); + } + var mime = lookupMimeType('no-file.$ext'); + return FileType(".$ext", mime ?? 'application/octet-stream'); + } } FileType detectFileType(List data) { diff --git a/lib/utils/io.dart b/lib/utils/io.dart index 34377c5..8ddf9c6 100644 --- a/lib/utils/io.dart +++ b/lib/utils/io.dart @@ -9,6 +9,7 @@ import 'package:venera/utils/ext.dart'; import 'package:path/path.dart' as p; import 'package:share_plus/share_plus.dart' as s; import 'package:file_selector/file_selector.dart' as file_selector; +import 'package:venera/utils/file_type.dart'; export 'dart:io'; export 'dart:typed_data'; @@ -86,7 +87,7 @@ extension DirectoryExtension on Directory { } String sanitizeFileName(String fileName) { - if(fileName.endsWith('.')) { + if (fileName.endsWith('.')) { fileName = fileName.substring(0, fileName.length - 1); } const maxLength = 255; @@ -126,7 +127,8 @@ Future copyDirectory(Directory source, Directory destination) async { } } -Future copyDirectoryIsolate(Directory source, Directory destination) async { +Future copyDirectoryIsolate( + Directory source, Directory destination) async { await Isolate.run(() { copyDirectory(source, destination); }); @@ -196,14 +198,32 @@ class IOSDirectoryPicker { } Future selectFile({required List ext}) async { + var extensions = App.isMacOS || App.isIOS ? null : ext; + if (App.isAndroid) { + for (var e in ext) { + var fileType = FileType.fromExtension(e); + if (fileType.mime == "application/octet-stream") { + extensions = null; + break; + } + } + } file_selector.XTypeGroup typeGroup = file_selector.XTypeGroup( label: 'files', - extensions: App.isMacOS || App.isIOS ? null : ext, + extensions: extensions, ); - final file_selector.XFile? file = await file_selector.openFile( - acceptedTypeGroups: [typeGroup], - ); - if (file == null) return null; + file_selector.XFile? file; + if (extensions == null && App.isAndroid) { + const selectFileChannel = MethodChannel("venera/select_file"); + var filePath = await selectFileChannel.invokeMethod("selectFile"); + if (filePath == null) return null; + file = file_selector.XFile(filePath); + } else { + file = await file_selector.openFile( + acceptedTypeGroups: [typeGroup], + ); + if (file == null) return null; + } if (!ext.contains(file.path.split(".").last)) { App.rootContext.showMessage(message: "Invalid file type"); return null; From a8bc09754189e8a7edd11eb877e8f969a1e5598d Mon Sep 17 00:00:00 2001 From: nyne Date: Wed, 13 Nov 2024 18:56:22 +0800 Subject: [PATCH 2/2] Update windows build script --- windows/build.iss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/build.iss b/windows/build.iss index 8061f10..01d6c2b 100644 --- a/windows/build.iss +++ b/windows/build.iss @@ -51,7 +51,7 @@ Source: "{#RootPath}\build\windows\x64\runner\Release\desktop_webview_window_plu Source: "{#RootPath}\build\windows\x64\runner\Release\WebView2Loader.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\share_plus_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\url_launcher_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion -Source: "{#RootPath}\build\windows\x64\runner\Release\screen_retriever_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion +Source: "{#RootPath}\build\windows\x64\runner\Release\battery_plus_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\screen_retriever_windows_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\window_manager_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion Source: "{#RootPath}\build\windows\x64\runner\Release\zip_flutter.dll"; DestDir: "{app}"; Flags: ignoreversion