fix & improve importing comic

This commit is contained in:
2024-11-19 18:44:52 +08:00
parent 8402c1c9f3
commit 6aeaeadb10
10 changed files with 557 additions and 450 deletions

View File

@@ -14,6 +14,21 @@ import 'package:venera/utils/file_type.dart';
export 'dart:io';
export 'dart:typed_data';
class IO {
/// A global flag used to indicate whether the app is selecting files.
///
/// Select file and other similar file operations will launch external programs,
/// causing the app to lose focus. AppLifecycleState will be set to paused.
static bool get isSelectingFiles => _isSelectingFiles;
static bool _isSelectingFiles = false;
}
/// A finalizer that can be used to dispose resources related to file operations.
final _finalizer = Finalizer<Function>((e) {
e();
});
class FilePath {
const FilePath._();
@@ -147,24 +162,35 @@ String findValidDirectoryName(String path, String directory) {
}
class DirectoryPicker {
DirectoryPicker() {
_finalizer.attach(this, dispose);
}
String? _directory;
final _methodChannel = const MethodChannel("venera/method_channel");
Future<Directory?> pickDirectory() async {
if (App.isWindows || App.isLinux) {
var d = await file_selector.getDirectoryPath();
_directory = d;
return d == null ? null : Directory(d);
} else if (App.isAndroid) {
var d = await _methodChannel.invokeMethod<String?>("getDirectoryPath");
_directory = d;
return d == null ? null : Directory(d);
} else {
// ios, macos
var d = await _methodChannel.invokeMethod<String?>("getDirectoryPath");
_directory = d;
return d == null ? null : Directory(d);
IO._isSelectingFiles = true;
try {
if (App.isWindows || App.isLinux) {
var d = await file_selector.getDirectoryPath();
_directory = d;
return d == null ? null : Directory(d);
} else if (App.isAndroid) {
var d = await _methodChannel.invokeMethod<String?>("getDirectoryPath");
_directory = d;
return d == null ? null : Directory(d);
} else {
// ios, macos
var d = await _methodChannel.invokeMethod<String?>("getDirectoryPath");
_directory = d;
return d == null ? null : Directory(d);
}
} finally {
Future.delayed(const Duration(milliseconds: 100), () {
IO._isSelectingFiles = false;
});
}
}
@@ -172,11 +198,15 @@ class DirectoryPicker {
if (_directory == null) {
return;
}
if (App.isAndroid && _directory != null) {
return Directory(_directory!).deleteIgnoreError(recursive: true);
if (App.isAndroid &&
_directory != null &&
_directory!.startsWith(App.cachePath)) {
await Directory(_directory!).deleteIgnoreError(recursive: true);
_directory = null;
}
if (App.isIOS || App.isMacOS) {
await _methodChannel.invokeMethod("stopAccessingSecurityScopedResource");
_directory = null;
}
}
}
@@ -186,53 +216,73 @@ class IOSDirectoryPicker {
// 调用 iOS 目录选择方法
static Future<String?> selectDirectory() async {
IO._isSelectingFiles = true;
try {
final String? path = await _channel.invokeMethod('selectDirectory');
return path;
} catch (e) {
// 返回报错信息
return e.toString();
} finally {
Future.delayed(const Duration(milliseconds: 100), () {
IO._isSelectingFiles = false;
});
}
}
}
Future<file_selector.XFile?> selectFile({required List<String> 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: extensions,
);
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: <file_selector.XTypeGroup>[typeGroup],
IO._isSelectingFiles = true;
try {
var extensions = App.isMacOS || App.isIOS ? null : ext;
file_selector.XTypeGroup typeGroup = file_selector.XTypeGroup(
label: 'files',
extensions: extensions,
);
if (file == null) return null;
file_selector.XFile? file;
if (App.isAndroid) {
const selectFileChannel = MethodChannel("venera/select_file");
String mimeType = "*/*";
if(ext.length == 1) {
mimeType = FileType.fromExtension(ext[0]).mime;
if(mimeType == "application/octet-stream") {
mimeType = "*/*";
}
}
var filePath = await selectFileChannel.invokeMethod(
"selectFile",
mimeType,
);
if (filePath == null) return null;
file = _AndroidFileSelectResult(filePath);
} else {
file = await file_selector.openFile(
acceptedTypeGroups: <file_selector.XTypeGroup>[typeGroup],
);
if (file == null) return null;
}
if (!ext.contains(file.path.split(".").last)) {
App.rootContext.showMessage(message: "Invalid file type");
return null;
}
return file;
} finally {
Future.delayed(const Duration(milliseconds: 100), () {
IO._isSelectingFiles = false;
});
}
if (!ext.contains(file.path.split(".").last)) {
App.rootContext.showMessage(message: "Invalid file type");
return null;
}
return file;
}
Future<String?> selectDirectory() async {
var path = await file_selector.getDirectoryPath();
return path;
IO._isSelectingFiles = true;
try {
var path = await file_selector.getDirectoryPath();
return path;
} finally {
Future.delayed(const Duration(milliseconds: 100), () {
IO._isSelectingFiles = false;
});
}
}
// selectDirectoryIOS
@@ -245,25 +295,32 @@ Future<void> saveFile(
if (data == null && file == null) {
throw Exception("data and file cannot be null at the same time");
}
if (data != null) {
var cache = FilePath.join(App.cachePath, filename);
if (File(cache).existsSync()) {
File(cache).deleteSync();
IO._isSelectingFiles = true;
try {
if (data != null) {
var cache = FilePath.join(App.cachePath, filename);
if (File(cache).existsSync()) {
File(cache).deleteSync();
}
await File(cache).writeAsBytes(data);
file = File(cache);
}
await File(cache).writeAsBytes(data);
file = File(cache);
}
if (App.isMobile) {
final params = SaveFileDialogParams(sourceFilePath: file!.path);
await FlutterFileDialog.saveFile(params: params);
} else {
final result = await file_selector.getSaveLocation(
suggestedName: filename,
);
if (result != null) {
var xFile = file_selector.XFile(file!.path);
await xFile.saveTo(result.path);
if (App.isMobile) {
final params = SaveFileDialogParams(sourceFilePath: file!.path);
await FlutterFileDialog.saveFile(params: params);
} else {
final result = await file_selector.getSaveLocation(
suggestedName: filename,
);
if (result != null) {
var xFile = file_selector.XFile(file!.path);
await xFile.saveTo(result.path);
}
}
} finally {
Future.delayed(const Duration(milliseconds: 100), () {
IO._isSelectingFiles = false;
});
}
}
@@ -302,3 +359,16 @@ String bytesToReadableString(int bytes) {
return "${(bytes / 1024 / 1024 / 1024).toStringAsFixed(2)} GB";
}
}
class _AndroidFileSelectResult extends s.XFile {
_AndroidFileSelectResult(super.path) {
_finalizer.attach(this, dispose);
}
void dispose() {
print("dispose $path");
if (path.startsWith(App.cachePath)) {
File(path).deleteIgnoreError();
}
}
}