fix
This commit is contained in:
@@ -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.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
|
<application
|
||||||
android:label="Cloud Archiver"
|
android:label="Cloud Archiver"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|||||||
+31
-3
@@ -5,6 +5,7 @@ import 'package:cloud_archiver/utils.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:webdav_client/webdav_client.dart' as dav;
|
import 'package:webdav_client/webdav_client.dart' as dav;
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
@@ -46,6 +47,33 @@ class MyHomePage extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var width = MediaQuery.sizeOf(context).width;
|
var width = MediaQuery.sizeOf(context).width;
|
||||||
@@ -171,7 +199,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
if (syncItemConfig != null) {
|
if (syncItemConfig != null) {
|
||||||
upload(syncItemConfig, cloudModifiedTimeTs == null);
|
upload(context, syncItemConfig, cloudModifiedTimeTs == null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Icon(Icons.upload_rounded, size: 22),
|
child: Icon(Icons.upload_rounded, size: 22),
|
||||||
@@ -184,7 +212,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
? null
|
? null
|
||||||
: () {
|
: () {
|
||||||
if (syncItemConfig != null) {
|
if (syncItemConfig != null) {
|
||||||
download(syncItemConfig, cloudArchives.sorted.first);
|
download(context, syncItemConfig, cloudArchives.sorted.first);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLongPress: cloudModifiedTimeTs == null
|
onLongPress: cloudModifiedTimeTs == null
|
||||||
@@ -204,7 +232,7 @@ class _MyHomePageState extends State<MyHomePage> {
|
|||||||
value: file,
|
value: file,
|
||||||
onTap: () {
|
onTap: () {
|
||||||
if (syncItemConfig != null) {
|
if (syncItemConfig != null) {
|
||||||
download(syncItemConfig, file);
|
download(context, syncItemConfig, file);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
|
|||||||
+42
-7
@@ -4,6 +4,7 @@ import 'package:archive/archive_io.dart';
|
|||||||
import 'package:cloud_archiver/config.dart';
|
import 'package:cloud_archiver/config.dart';
|
||||||
import 'package:cloud_archiver/state.dart';
|
import 'package:cloud_archiver/state.dart';
|
||||||
import 'package:filesize/filesize.dart';
|
import 'package:filesize/filesize.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:webdav_client/webdav_client.dart' as dav;
|
import 'package:webdav_client/webdav_client.dart' as dav;
|
||||||
@@ -109,7 +110,7 @@ void loadCloudArchives() async {
|
|||||||
davStatus.value = DavStatus.connected;
|
davStatus.value = DavStatus.connected;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> upload(SyncItemConfig item, bool needCreateDir) async {
|
Future<void> upload(BuildContext context, SyncItemConfig item, bool needCreateDir) async {
|
||||||
if (needCreateDir) {
|
if (needCreateDir) {
|
||||||
await _client.mkdir(item.name);
|
await _client.mkdir(item.name);
|
||||||
}
|
}
|
||||||
@@ -122,26 +123,52 @@ Future<void> upload(SyncItemConfig item, bool needCreateDir) async {
|
|||||||
var zipTempFilePath = join(cacheDir.path, zipFileName);
|
var zipTempFilePath = join(cacheDir.path, zipFileName);
|
||||||
zipFileEncoder.create(zipTempFilePath);
|
zipFileEncoder.create(zipTempFilePath);
|
||||||
if (file.statSync().type == FileSystemEntityType.directory) {
|
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 {
|
} else {
|
||||||
await zipFileEncoder.addFile(file);
|
await zipFileEncoder.addFile(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
await zipFileEncoder.close();
|
await zipFileEncoder.close();
|
||||||
|
|
||||||
await _client.writeFromFile(zipTempFilePath, join(item.name, zipFileName));
|
await _client.writeFromFile(zipTempFilePath, join(item.name, zipFileName));
|
||||||
|
|
||||||
File(zipTempFilePath).delete();
|
File(zipTempFilePath).delete();
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('上传完成'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
loadCloudArchives();
|
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}');
|
print('Start download: ${cloudArchive.path}');
|
||||||
var cacheDir = await getApplicationCacheDirectory();
|
var cacheDir = await getApplicationCacheDirectory();
|
||||||
var zipPath = join(cacheDir.path, '${item.name}_${cloudArchive.name}');
|
var zipPath = join(cacheDir.path, '${item.name}_${cloudArchive.name}');
|
||||||
await _client.read2File(cloudArchive.path!, zipPath);
|
await _client.read2File(
|
||||||
await extractFileToDisk(zipPath, cacheDir.path);
|
cloudArchive.path!,
|
||||||
|
zipPath,
|
||||||
|
onProgress: (count, total) {
|
||||||
|
print('Downloaded: $count/$total');
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await extractFileToDisk(
|
||||||
|
zipPath,
|
||||||
|
cacheDir.path,
|
||||||
|
callback: (entry) {
|
||||||
|
print('Unzip: ${entry.name}');
|
||||||
|
},
|
||||||
|
);
|
||||||
File(zipPath).delete();
|
File(zipPath).delete();
|
||||||
|
|
||||||
var temp = File(join(cacheDir.path, basename(item.path)));
|
var temp = File(join(cacheDir.path, basename(item.path)));
|
||||||
@@ -165,6 +192,14 @@ Future<void> download(SyncItemConfig item, dav.File cloudArchive) async {
|
|||||||
default:
|
default:
|
||||||
print('Failed: $fileSystemEntityType');
|
print('Failed: $fileSystemEntityType');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text('下载完成'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension DateTimeExt on DateTime {
|
extension DateTimeExt on DateTime {
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ static void my_application_activate(GApplication* application) {
|
|||||||
if (use_header_bar) {
|
if (use_header_bar) {
|
||||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
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_header_bar_set_show_close_button(header_bar, TRUE);
|
||||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||||
} else {
|
} else {
|
||||||
gtk_window_set_title(window, "cloud_archiver");
|
gtk_window_set_title(window, "Cloud Archiver");
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_default_size(window, 1280, 720);
|
gtk_window_set_default_size(window, 1280, 720);
|
||||||
|
|||||||
+1
-2
@@ -33,8 +33,6 @@ dependencies:
|
|||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
cupertino_icons: ^1.0.8
|
|
||||||
crypto: ^3.0.7
|
|
||||||
path_provider: ^2.1.6
|
path_provider: ^2.1.6
|
||||||
path: ^1.9.1
|
path: ^1.9.1
|
||||||
webdav_client: ^1.2.2
|
webdav_client: ^1.2.2
|
||||||
@@ -43,6 +41,7 @@ dependencies:
|
|||||||
uuid: ^4.5.3
|
uuid: ^4.5.3
|
||||||
collection: ^1.19.1
|
collection: ^1.19.1
|
||||||
archive: ^4.0.9
|
archive: ^4.0.9
|
||||||
|
permission_handler: ^12.0.3
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
Reference in New Issue
Block a user