更改安卓端的文件访问方式,优化导入逻辑 (#64)

* Refactor import function & Allow import local comics without copying them to local path.

* android: use file_picker instead, support directory access for android 10

* Improve import logic

* Fix sql query.

* Add ability to remove invalid favorite items.

* Perform sort before choosing cover

* Revert changes of "use file_picker instead".

* Try catch on "check update"

* Added module 'flutter_saf'

* gitignore

* remove unsupported arch in build.gradle

* Use flutter_saf to handle android's directory and files, improve import logic.

* revert changes of 'requestLegacyExternalStorage'

* fix cbz import

* openDirectoryPlatform

* Remove double check on source folder

* use openFilePlatform

* remove unused import

* improve local comic's path handling

* bump version

* fix pubspec format

* return null when comic folder is empty
This commit is contained in:
pkuislm
2024-11-23 11:05:00 +08:00
committed by GitHub
parent c3474b1dff
commit a1474ca9c3
16 changed files with 369 additions and 253 deletions

View File

@@ -1,4 +1,3 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';
import 'package:venera/components/components.dart';
@@ -497,6 +496,10 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
String? selectedFolder;
bool copyToLocalFolder = true;
bool cancelled = false;
@override
void dispose() {
loading = false;
@@ -530,22 +533,23 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
),
)
: Column(
key: key,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 600),
...List.generate(importMethods.length, (index) {
return RadioListTile(
title: Text(importMethods[index]),
value: index,
groupValue: type,
onChanged: (value) {
setState(() {
type = value as int;
});
},
);
}),
key: key,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const SizedBox(width: 600),
...List.generate(importMethods.length, (index) {
return RadioListTile(
title: Text(importMethods[index]),
value: index,
groupValue: type,
onChanged: (value) {
setState(() {
type = value as int;
});
},
);
}),
if(type != 3)
ListTile(
title: Text("Add to favorites".tl),
trailing: Select(
@@ -559,10 +563,19 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
},
),
).paddingHorizontal(8),
const SizedBox(height: 8),
Text(info).paddingHorizontal(24),
],
),
CheckboxListTile(
enabled: true,
title: Text("Copy to app local path".tl),
value: copyToLocalFolder,
onChanged:(v) {
setState(() {
copyToLocalFolder = !copyToLocalFolder;
});
}).paddingHorizontal(8),
const SizedBox(height: 8),
Text(info).paddingHorizontal(24),
],
),
actions: [
Button.text(
child: Row(
@@ -620,18 +633,20 @@ class _ImportComicsWidgetState extends State<_ImportComicsWidget> {
void selectAndImport() async {
height = key.currentContext!.size!.height;
setState(() {
loading = true;
});
var importer = ImportComic(selectedFolder: selectedFolder);
var result = false;
if (type == 2) {
result = await importer.cbz();
} else if (type == 3) {
result = await importer.ehViewer();
} else {
result = await importer.directory(type == 0);
}
var importer = ImportComic(
selectedFolder: selectedFolder,
copyToLocal: copyToLocalFolder);
var result = switch(type) {
0 => await importer.directory(true),
1 => await importer.directory(false),
2 => await importer.cbz(),
3 => await importer.ehViewer(),
int() => true,
};
if(result) {
context.pop();
} else {

View File

@@ -604,7 +604,7 @@ ImageProvider _createImageProvider(int page, BuildContext context) {
var reader = context.reader;
var imageKey = reader.images![page - 1];
if (imageKey.startsWith('file://')) {
return FileImage(File(imageKey.replaceFirst("file://", '')));
return FileImage(openFilePlatform(imageKey.replaceFirst("file://", '')));
} else {
return ReaderImageProvider(
imageKey,

View File

@@ -469,7 +469,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
ImageProvider image;
var imageKey = images[index];
if (imageKey.startsWith('file://')) {
image = FileImage(File(imageKey.replaceFirst("file://", '')));
image = FileImage(openFilePlatform(imageKey.replaceFirst("file://", '')));
} else {
image = ReaderImageProvider(
imageKey,
@@ -515,7 +515,7 @@ class _ReaderScaffoldState extends State<_ReaderScaffold> {
}
}
if (imageKey.startsWith("file://")) {
return await File(imageKey.substring(7)).readAsBytes();
return await openFilePlatform(imageKey.substring(7)).readAsBytes();
} else {
return (await CacheManager().findCache(
"$imageKey@${context.reader.type.sourceKey}@${context.reader.cid}@${context.reader.eid}"))!

View File

@@ -86,29 +86,33 @@ Future<bool> checkUpdate() async {
}
Future<void> checkUpdateUi([bool showMessageIfNoUpdate = true]) async {
var value = await checkUpdate();
if (value) {
showDialog(
context: App.rootContext,
builder: (context) {
return ContentDialog(
title: "New version available".tl,
content: Text(
"A new version is available. Do you want to update now?".tl),
actions: [
Button.text(
onPressed: () {
Navigator.pop(context);
launchUrlString(
"https://github.com/venera-app/venera/releases");
},
child: Text("Update".tl),
),
],
);
});
} else if (showMessageIfNoUpdate) {
App.rootContext.showMessage(message: "No new version available".tl);
try {
var value = await checkUpdate();
if (value) {
showDialog(
context: App.rootContext,
builder: (context) {
return ContentDialog(
title: "New version available".tl,
content: Text(
"A new version is available. Do you want to update now?".tl),
actions: [
Button.text(
onPressed: () {
Navigator.pop(context);
launchUrlString(
"https://github.com/venera-app/venera/releases");
},
child: Text("Update".tl),
),
],
);
});
} else if (showMessageIfNoUpdate) {
App.rootContext.showMessage(message: "No new version available".tl);
}
} catch (e, s) {
Log.error("Check Update", e.toString(), s);
}
}

View File

@@ -38,6 +38,16 @@ class _LocalFavoritesSettingsState extends State<LocalFavoritesSettings> {
for (var e in LocalFavoritesManager().folderNames) e: e
},
).toSliver(),
_CallbackSetting(
title: "Delete all unavailable local favorite items".tl,
callback: () async {
var controller = showLoadingDialog(context);
var count = await LocalFavoritesManager().removeInvalid();
controller.close();
context.showMessage(message: "Deleted @a favorite items".tlParams({'a': count}));
},
actionTitle: 'Delete'.tl,
).toSliver(),
],
);
}