This commit is contained in:
2026-07-14 01:36:11 +08:00
parent fd3a204c81
commit c9b282d985
5 changed files with 84 additions and 15 deletions
@@ -1,6 +1,13 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- 针对 Android 11 及以上 -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" />
<application
android:label="Cloud Archiver"
android:name="${applicationName}"
+31 -3
View File
@@ -5,6 +5,7 @@ import 'package:cloud_archiver/utils.dart';
import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:path/path.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:webdav_client/webdav_client.dart' as dav;
Future<void> main() async {
@@ -46,6 +47,33 @@ class MyHomePage extends StatefulWidget {
}
class _MyHomePageState extends State<MyHomePage> {
@override
void initState() {
checkAndRequestStoragePermission();
super.initState();
}
Future<void> checkAndRequestStoragePermission() async {
// 1. 检查是否已经拥有“所有文件访问权限”
var status = await Permission.manageExternalStorage.status;
if (!status.isGranted) {
print("未获得管理外部存储权限,尝试申请...");
// 2. 首次申请,或者已经被拒绝过
// 在 Android 11+ 上,这行代码会自动尝试跳转到系统的“所有文件访问权限”设置列表
var requestStatus = await Permission.manageExternalStorage.request();
// 3. 如果系统未能自动跳转(某些国产ROM限制),则强制通过插件打开系统设置
if (!requestStatus.isGranted) {
print("自动跳转失败,提示用户手动去设置开启");
await openAppSettings();
}
} else {
print("已获得所有文件访问权限,可以放心执行 listSync()");
}
}
@override
Widget build(BuildContext context) {
var width = MediaQuery.sizeOf(context).width;
@@ -171,7 +199,7 @@ class _MyHomePageState extends State<MyHomePage> {
? null
: () {
if (syncItemConfig != null) {
upload(syncItemConfig, cloudModifiedTimeTs == null);
upload(context, syncItemConfig, cloudModifiedTimeTs == null);
}
},
child: Icon(Icons.upload_rounded, size: 22),
@@ -184,7 +212,7 @@ class _MyHomePageState extends State<MyHomePage> {
? null
: () {
if (syncItemConfig != null) {
download(syncItemConfig, cloudArchives.sorted.first);
download(context, syncItemConfig, cloudArchives.sorted.first);
}
},
onLongPress: cloudModifiedTimeTs == null
@@ -204,7 +232,7 @@ class _MyHomePageState extends State<MyHomePage> {
value: file,
onTap: () {
if (syncItemConfig != null) {
download(syncItemConfig, file);
download(context, syncItemConfig, file);
}
},
child: SizedBox(
+42 -7
View File
@@ -4,6 +4,7 @@ import 'package:archive/archive_io.dart';
import 'package:cloud_archiver/config.dart';
import 'package:cloud_archiver/state.dart';
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:webdav_client/webdav_client.dart' as dav;
@@ -109,7 +110,7 @@ void loadCloudArchives() async {
davStatus.value = DavStatus.connected;
}
Future<void> upload(SyncItemConfig item, bool needCreateDir) async {
Future<void> upload(BuildContext context, SyncItemConfig item, bool needCreateDir) async {
if (needCreateDir) {
await _client.mkdir(item.name);
}
@@ -122,26 +123,52 @@ Future<void> upload(SyncItemConfig item, bool needCreateDir) async {
var zipTempFilePath = join(cacheDir.path, zipFileName);
zipFileEncoder.create(zipTempFilePath);
if (file.statSync().type == FileSystemEntityType.directory) {
await zipFileEncoder.addDirectory(Directory(item.path));
var directory = Directory(item.path);
for (var file in directory.listSync()) {
print('Add file: ${file.path}');
}
await zipFileEncoder.addDirectory(
directory,
onProgress: (progress) {
print('Zip progress: $progress');
},
);
} else {
await zipFileEncoder.addFile(file);
}
await zipFileEncoder.close();
await _client.writeFromFile(zipTempFilePath, join(item.name, zipFileName));
File(zipTempFilePath).delete();
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('上传完成'),
),
);
}
loadCloudArchives();
}
Future<void> download(SyncItemConfig item, dav.File cloudArchive) async {
Future<void> download(BuildContext context, SyncItemConfig item, dav.File cloudArchive) async {
print('Start download: ${cloudArchive.path}');
var cacheDir = await getApplicationCacheDirectory();
var zipPath = join(cacheDir.path, '${item.name}_${cloudArchive.name}');
await _client.read2File(cloudArchive.path!, zipPath);
await extractFileToDisk(zipPath, cacheDir.path);
await _client.read2File(
cloudArchive.path!,
zipPath,
onProgress: (count, total) {
print('Downloaded: $count/$total');
},
);
await extractFileToDisk(
zipPath,
cacheDir.path,
callback: (entry) {
print('Unzip: ${entry.name}');
},
);
File(zipPath).delete();
var temp = File(join(cacheDir.path, basename(item.path)));
@@ -165,6 +192,14 @@ Future<void> download(SyncItemConfig item, dav.File cloudArchive) async {
default:
print('Failed: $fileSystemEntityType');
}
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('下载完成'),
),
);
}
}
extension DateTimeExt on DateTime {
+2 -2
View File
@@ -45,11 +45,11 @@ static void my_application_activate(GApplication* application) {
if (use_header_bar) {
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
gtk_widget_show(GTK_WIDGET(header_bar));
gtk_header_bar_set_title(header_bar, "cloud_archiver");
gtk_header_bar_set_title(header_bar, "Cloud Archiver");
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
} else {
gtk_window_set_title(window, "cloud_archiver");
gtk_window_set_title(window, "Cloud Archiver");
}
gtk_window_set_default_size(window, 1280, 720);
+1 -2
View File
@@ -33,8 +33,6 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
crypto: ^3.0.7
path_provider: ^2.1.6
path: ^1.9.1
webdav_client: ^1.2.2
@@ -43,6 +41,7 @@ dependencies:
uuid: ^4.5.3
collection: ^1.19.1
archive: ^4.0.9
permission_handler: ^12.0.3
dev_dependencies:
flutter_test: